Code: Select all
#Requires AutoHotkey v2-b+
; Simple example usage using MyStruct
v := MyStruct()
v.someInt := 0xcafe
v.someStr := StrPtr("Hello, world!")
MsgBoxF "Size of MyStruct: {:u} bytes`nType of object: {}`nSome integer: 0x{:08X}`nSome string: {}",
MyStruct.Size,
Type(v),
v.someInt,
StrGet(v.someStr)
; Structs are fully functional native Buffer objects, and as a result
; they are eligible for optimizations in AutoHotkey's built-in library.
MsgBoxF "MyStruct vtable: 0x{:p}`nBuffer vtable: 0x{:p}",
NumGet(ObjPtr(v), "Ptr"),
NumGet(ObjPtr(Buffer()), "Ptr")
; Real world example: Using a Win32 struct
DllCall("GetSystemTime", "Ptr", now := SYSTEMTIME())
MsgBoxF "Today is {:04d}/{:02d}/{:02d}`nCurrent time: {:02d}:{:02d}:{:02d}",
now.wYear, now.wMonth, now.wDay,
now.wHour, now.wMinute, now.wSecond
; Helper function for MsgBox + Format (printf-esque)
MsgBoxF(Fmt, Args*) => MsgBox(Format(Fmt, Args*))
; Simple example struct definition
class MyStruct extends EasyStruct {
static Template := "
(
UInt someInt
Ptr someStr
)"
}
; https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime
class SYSTEMTIME extends EasyStruct {
static Template := "
(
UShort wYear
UShort wMonth
UShort wDayOfWeek
UShort wDay
UShort wHour
UShort wMinute
UShort wSecond
UShort wMilliseconds
)"
}
class EasyStruct extends Buffer {
; Map of known types along with their sizes for use with NumGet/Put.
static Types := Map(
"UChar", 1, "Char", 1,
"UShort", 2, "Short", 2,
"UInt", 4, "Int", 4, "Float", 4,
"Int64", 8, "Double", 8,
"Ptr", A_PtrSize, "UPtr", A_PtrSize
)
; Disallow creating EasyStruct instances directly.
; (In the future it could be enhanced to allow creating ad-hoc structs)
static Call(*) {
throw Error("Attempted to instantiate an abstract class")
}
; This initializer method is called for every descendant of EasyStruct.
; Build the class prototype dynamically according to its template.
static __New() {
if this == EasyStruct
return
proto := this.Prototype,
className := proto.__Class,
; Remove EasyStruct from the class inheritance
this.base := EasyStruct.base,
proto.base := EasyStruct.Prototype.base,
; Not needed: below will error out if the property does not exist.
;if not ObjHasOwnProp(this, "Template")
; throw Error("Struct " className " has no template definition")
template := this.Template,
this.DeleteProp("Template"),
; Define own-properties for every field in the struct.
size := 0,
align := 0
Loop Parse template, "`n", "`r" {
if not t := Trim(A_LoopField)
continue
if not RegExMatch(t, "^([a-zA-Z_][0-9a-zA-Z_]*)\s+([a-zA-Z_][0-9a-zA-Z_]*)$", &o)
throw Error("Invalid struct field",, t)
if not tsize := EasyStruct.Types.Get(tname := o[1], 0)
throw Error("Invalid field type",, t)
size := (size + tsize - 1) &~ (tsize - 1), ; Field alignment.
align := Max(align, size),
proto.DefineProp(o[2], {
get: NumGet.Bind(, size, tname),
set: EasyStruct.NumPutWrap.Bind(, size, tname)
}),
size += tsize
}
; Finalize the struct class and build a constructor for it.
size := (size + align - 1) &~ (align - 1), ; Struct alignment.
this.Size := size,
this.DefineProp("Call", {
call: Buffer.Call.Bind(, size, 0)
})
}
; Setter-compatible wrapper for NumPut.
static NumPutWrap(Offset, Type_, Value) => (NumPut(Type_, Value, this, Offset), Value)
}