use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct ObjectTypeFlags(pub u32);
impl ObjectTypeFlags {
pub const HAS_OPTIONAL_INFO: u32 = 0x01;
pub const HAS_COM_INTERFACE: u32 = 0x02;
pub const IS_VISUAL: u32 = 0x80;
pub const ACTIVEX: u32 = 0x800;
#[inline]
pub fn has(self, flag: u32) -> bool {
self.0 & flag != 0
}
#[inline]
pub fn has_optional_info(self) -> bool {
self.has(Self::HAS_OPTIONAL_INFO)
}
#[inline]
pub fn is_class(self) -> bool {
self.0 & 0x82 == 0x02
}
#[inline]
pub fn is_form(self) -> bool {
self.0 & 0x82 == 0x82
}
#[inline]
pub fn is_module(self) -> bool {
self.0 & 0x82 == 0x00
}
pub fn kind_name(self) -> &'static str {
if self.is_form() {
"Form"
} else if self.is_class() {
"Class"
} else {
"Module"
}
}
}
impl fmt::Debug for ObjectTypeFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ObjectTypeFlags(0x{:08X} {})", self.0, self.kind_name())
}
}
impl fmt::Display for ObjectTypeFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct ThreadFlags(pub u32);
impl ThreadFlags {
pub const APARTMENT_MODEL: u32 = 0x01;
pub const REQUIRE_LICENSE: u32 = 0x02;
pub const UNATTENDED: u32 = 0x04;
pub const SINGLE_THREADED: u32 = 0x08;
pub const RETAINED: u32 = 0x10;
#[inline]
pub fn has(self, flag: u32) -> bool {
self.0 & flag != 0
}
#[inline]
pub fn is_apartment_model(self) -> bool {
self.has(Self::APARTMENT_MODEL)
}
#[inline]
pub fn is_unattended(self) -> bool {
self.has(Self::UNATTENDED)
}
#[inline]
pub fn is_single_threaded(self) -> bool {
self.has(Self::SINGLE_THREADED)
}
}
impl fmt::Debug for ThreadFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ThreadFlags(0x{:02X}", self.0)?;
let flags: &[(&str, u32)] = &[
("APARTMENT_MODEL", Self::APARTMENT_MODEL),
("REQUIRE_LICENSE", Self::REQUIRE_LICENSE),
("UNATTENDED", Self::UNATTENDED),
("SINGLE_THREADED", Self::SINGLE_THREADED),
("RETAINED", Self::RETAINED),
];
let mut first = true;
for &(name, val) in flags {
if self.has(val) {
if first {
write!(f, " ")?;
first = false;
} else {
write!(f, " | ")?;
}
write!(f, "{name}")?;
}
}
write!(f, ")")
}
}
impl fmt::Display for ThreadFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct IntrinsicControlFlags(pub u32);
impl IntrinsicControlFlags {
pub const PICTURE_BOX: u32 = 1 << 0;
pub const LABEL: u32 = 1 << 1;
pub const TEXT_BOX: u32 = 1 << 2;
pub const FRAME: u32 = 1 << 3;
pub const COMMAND_BUTTON: u32 = 1 << 4;
pub const CHECK_BOX: u32 = 1 << 5;
pub const OPTION_BUTTON: u32 = 1 << 6;
pub const COMBO_BOX: u32 = 1 << 7;
pub const LIST_BOX: u32 = 1 << 8;
pub const HSCROLL_BAR: u32 = 1 << 9;
pub const VSCROLL_BAR: u32 = 1 << 10;
pub const TIMER: u32 = 1 << 11;
const CONTROL_NAMES: [&str; 12] = [
"PictureBox",
"Label",
"TextBox",
"Frame",
"CommandButton",
"CheckBox",
"OptionButton",
"ComboBox",
"ListBox",
"HScrollBar",
"VScrollBar",
"Timer",
];
#[inline]
pub fn has(self, flag: u32) -> bool {
self.0 & flag != 0
}
pub fn used_controls(self) -> Vec<&'static str> {
Self::CONTROL_NAMES
.iter()
.enumerate()
.filter(|(i, _)| self.0 & (1 << i) != 0)
.map(|(_, name)| *name)
.collect()
}
}
impl fmt::Debug for IntrinsicControlFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "IntrinsicControlFlags(0x{:08X}", self.0)?;
let controls = self.used_controls();
if !controls.is_empty() {
write!(f, " {}", controls.join(", "))?;
}
write!(f, ")")
}
}
impl fmt::Display for IntrinsicControlFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_object_type_real_module() {
let flags = ObjectTypeFlags(0x00018001);
assert!(flags.has_optional_info());
assert!(flags.is_module());
assert!(!flags.is_class());
assert!(!flags.is_form());
assert_eq!(flags.kind_name(), "Module");
}
#[test]
fn test_object_type_real_class() {
let flags = ObjectTypeFlags(0x00118003);
assert!(flags.has_optional_info());
assert!(flags.is_class());
assert!(!flags.is_module());
assert!(!flags.is_form());
assert_eq!(flags.kind_name(), "Class");
}
#[test]
fn test_object_type_real_form() {
let flags = ObjectTypeFlags(0x00018083);
assert!(flags.has_optional_info());
assert!(flags.is_form());
assert!(!flags.is_module());
assert!(!flags.is_class());
assert_eq!(flags.kind_name(), "Form");
}
#[test]
fn test_object_type_low_byte_patterns() {
assert!(ObjectTypeFlags(0x01).is_module());
assert!(ObjectTypeFlags(0x03).is_class());
assert!(ObjectTypeFlags(0x83).is_form());
}
#[test]
fn test_object_type_ocx_class() {
let flags = ObjectTypeFlags(0x00118803);
assert!(flags.is_class());
assert!(flags.has(ObjectTypeFlags::ACTIVEX));
}
#[test]
fn test_object_type_debug_format() {
let flags = ObjectTypeFlags(0x00018083);
let s = format!("{flags:?}");
assert!(s.contains("Form"));
assert!(s.contains("00018083"));
}
#[test]
fn test_thread_flags() {
let flags = ThreadFlags(0x05); assert!(flags.is_apartment_model());
assert!(flags.is_unattended());
assert!(!flags.is_single_threaded());
}
#[test]
fn test_thread_flags_debug() {
let flags = ThreadFlags(0x05);
let s = format!("{flags:?}");
assert!(s.contains("APARTMENT_MODEL"));
assert!(s.contains("UNATTENDED"));
}
#[test]
fn test_thread_flags_display() {
let flags = ThreadFlags(0x05);
let s = format!("{flags}");
assert!(s.contains("APARTMENT_MODEL"));
assert!(s.contains("UNATTENDED"));
}
#[test]
fn test_flags_copy_eq() {
let f1 = ObjectTypeFlags(0x00018001);
let f2 = f1;
assert_eq!(f1, f2);
}
#[test]
fn test_intrinsic_control_flags() {
let flags = IntrinsicControlFlags(0x0030F80F);
assert!(flags.has(IntrinsicControlFlags::PICTURE_BOX));
assert!(flags.has(IntrinsicControlFlags::LABEL));
assert!(flags.has(IntrinsicControlFlags::TEXT_BOX));
assert!(flags.has(IntrinsicControlFlags::FRAME));
assert!(flags.has(IntrinsicControlFlags::TIMER));
assert!(!flags.has(IntrinsicControlFlags::CHECK_BOX));
let controls = flags.used_controls();
assert!(controls.contains(&"PictureBox"));
assert!(controls.contains(&"Timer"));
assert!(!controls.contains(&"CheckBox"));
}
#[test]
fn test_intrinsic_control_flags_none() {
let flags = IntrinsicControlFlags(0x0030F000);
assert!(flags.used_controls().is_empty());
}
#[test]
fn test_intrinsic_control_flags_debug() {
let flags = IntrinsicControlFlags(0x0030F80F);
let s = format!("{flags:?}");
assert!(s.contains("PictureBox"));
assert!(s.contains("Timer"));
}
}