Struct is a build-in function that creates and returns a structure object.
This object can be used to access the defined structure using object syntax.
SetCapacity Method can be used to allocate memory to structure and pointers.
Methods:
Struct requires a definition and accepts optionally address of structure memory and initialization object.
OutputVar := Struct(Definition ,StructMemory, InitObject)Function Example: pt := Struct("int x;y",,{x:10,y:20})
The name of the variable in which to store the Structure Object.
This parameter must be a string or variable containing the structure definition.
Definition is similar to C so most structures can be used directly or transformed very
easily.
Following default data types can be
used in Structures, other types mus be defined in script.
Either semicolon (;) or comma (,) can be used to separate fields, even
mixed.
If no type for first field is given UInt will be used, otherwise
previous type will be used.
E.g. in "a,Int x,y,Char c,d", a will
be UInt, x and y Int, c and d Char.
If only one type is given, e.g. "UInt" or "POINT", it is assumed to be an array of one, same as "UInt[1]" or "POINT[1]"
Note! if definition contains a comma or semicolon, e.g. "len;" or "MyVar," it is interpreted as "UInt
length" and "UInt MyVar",
where for Struct("MyVar"), MyVar must define a structure, e.g.
MyVar := "Int x, Int y", if there is no MyVar variable or it is empty Struct() will throw an error.
Comments and few examples:
Creating a structure from a string containing the structure definition.
pt:=Struct("Int x,Int y")
The definition can be saved in a variable. Struct will also resolve the
given string to the variable if necessary, however the structure will
be created as array of 1 so Struct("POINT") is equal to Struct("POINT[1]").
This will change the syntax how to access the fields,
for pt:=Struct(POINT) or pt:=Struct("UInt x,y") you can access the values directly, e.g. pt.x
where for pt:=Struct("POINT") and pt:=Struct("POINT[1]") you will need to use pt.1.x or pt[1].x or pt["1"].x.
The evaluation order is always Structure -> Array -> Pointer -> etc. -> field.
So in following structure s:=Struct("*UInt") which is the same as s:=Struct("*UInt[1]"),
first array is accessed s.1, then pointer s.1.1.
POINT:=" ( Int x; // also comments in C syntax are supported // empty lines will be simply ignored. Int y; // last ; is optional and can be omitted. )"
POINT:="Int x,y" pt:=Struct("POINT") ; same as pt:=Struct("POINT[1]")
Without comments, new lines can be omitted too. This way the definition can be written much more compact.
POINT:="Int x,y"
UInt is default type so it can be omitted too if negative values are not relevant (for negative values Int must be used).
POINT:="x,y"
Struct supports unions and structures in structures. Note, sub-structures
must not have a name so you can't use same name for a field in main
structure and sub-structures.
You can simply prefix those with the structure name, e.g. dummy_a, dummy_b....
UnionStruct:=" ( union { UInt int; struct { UShort x; UShort y; }; struct { Byte a; Byte b; Byte c; Byte d; }; }; )" mys:=Struct(UnionStruct) mys.int:=0xFFFFFFFF MsgBox mys.int "`n" mys.x " " mys.y "`n" mys.a " " mys.b " " mys.c " " mys.dGlobal / Static / Local variables
We can create a structure from a static function variables outside the function or even in another function. Therefore include the name of the function and enclose variable in brackets. This is also necessary if you create a static structure from a static variable like in MyFunc here.
MyFunc() ; using this method we can create static structures AnotherFunc() ; this method can be used anywhere to adccess the variable pt:=Struct("MyFunc(POINT)",,{x:100,y:200}) ; even outside the function we can access the definition variable MsgBox pt.x "-" pt.y MyFunc(){ static POINT:="UInt x,UInt y" , pt:=Struct("MyFunc(POINT)",,{x:10,y:20}) MsgBox(pt.x "-" pt.y) } AnotherFunc(){ static pt:=Struct("MyFunc(POINT)",,{x:10,y:20}) MsgBox(pt.x "-" pt.y) }
Buffer object or Address/pointer to memory representing the structure. This variable is used
to access an existing structure in memory.
For example here we would use the memory of variable pointMem for the structure.
pointMem:=BufferAlloc(8) pt:=Struct("x,y",pointMem) pt.x:=10 MsgBox pt[] " = " pointMem.Ptr "`n" pt.x " = " NumGet(pointMem,"UInt")
Initialize your structure right away using an object, map, array or another structure.
The order of keys and values is not relevant.
pt:=Struct("x,y",,{x:100,y:200}) MsgBox pt.x "`n" pt.y
Returns new structure object of same type.
OutputVar := Struct.Clone()
OutputVar | The name of the Variable in which to store the new structure object. |
pt:=Struct("x,y") pt1:=pt.Clone() pt1[]:={x:10,y:20} MsgBox pt1.x "-" pt1.y
Returns size of array definition or 0 if structure or field is not an array.
Note! Array size can be increased using SetCapacity, however CountOf will always return defined value.
OutputVar := Struct.CountOf([field])
OutputVar | The name of the Variable in which to store the length of array. |
field | Name of existing field within the structure.. |
uint:=Struct("UInt[10]") MsgBox uint.CountOf() ; returns 10 pt:=Struct("UInt x[2],UInt y[2]") MsgBox pt.CountOf("x") ; returns 2 pt:=Struct("Uint a[2]") MsgBox pt.a.CountOf() ; returns 2 pt.SetCapacity(16) MsgBox pt.a.CountOf() ; will still return 2
Returns encoding for a field.
OutputVar := Struct.Encoding([field])
OutputVar | The name of the Variable in which to store the encoding. |
field | Name of existing field within the structure. |
str1:=Struct("LPSTR name") str2:=Struct("LPTSTR name") MsgBox str1.Encoding("name") " != " str2.Encoding("name")
Returns address of field or structure.
OutputVar := Struct.GetAddress([field])
OutputVar | The name of the Variable in which to store the address. |
field | Name of existing field within the structure. When omitted, returns address of structure itself. To get the address for structure you can also use[] or [""] for structure objects and [""] for its fields. Note you cannot use [] for fields. |
pt:=Struct("x,y") MsgBox pt.GetAddress() " = " pt[] MsgBox pt.GetAddress("y") " = " pt.y[""]
Returns Capacity previously allocated using .SetCapacity() or by assigning a string.
OutputVar := Struct.GetCapacity([field])
OutputVar | The name of the Variable in which to store the capacity in bytes. |
field | Name of existing field in our structure. |
str:=Struct("LPTSTR name") str.SetCapacity("name",2000) MsgBox str.GetCapacity("name")
Returns the pointer for allocated memory saved in structure or field.
OutputVar := Struct.GetPointer([field])
OutputVar | The name of the Variable in which to store the address. |
field | Name of existing field within the structure. When omitted, returns pointer of first item in the structure. |
str:=Struct("LPTSTR name",,{name:"AutoHotkey"}) MsgBox str.GetPointer("name") "`n" StrGet(str.GetPointer("name"))
You can also use "" to read the pointer. So [""] returns address and ["",""] returns the pointer, ["","",""] pointer to pointer and so on.
str:=Struct("LPTSTR name",,{name:"AutoHotkey"}) MsgBox str.name["",""] "`n" StrGet(str.name["",""])
Returns true if the field or structure is a pointer.
OutputVar := Struct.IsPointer([field])
OutputVar | The name of the Variable in which to store true if field or structure is a pointer or 0 / false otherwise. |
field | Name of existing field within the structure. When omitted, returns true if structure itself is a pointer. |
s:=Struct("UInt *a,UInt b") MsgBox s.IsPointer("a") " != " s.IsPointer("b") s:=Struct("UInt*") MsgBox s.IsPointer()
Returns offset for a field.
OutputVar := Struct.Offset(field)
OutputVar | The name of the Variable in which to store the offset. |
field | Name of existing field within the structure. |
pt:=Struct("x,y") MsgBox pt.Offset("y") ; returns 4 ; Note! because structure resolves pointers and arrays dynamically, offset for those will be relative to parent item MyStruct:="Int a,b" pt:=Struct("MyStruct a[2]") MsgBox pt.a.Offset(2) ; returns 8 . " / " pt.a.2.Offset("b") ; returns 4
Allocate memory for a field, returns allocated size if new memory was allocated.
OutputVar := Struct.SetCapacity([field,] newsize)
OutputVar | The name of the Variable in which to store the new size. |
field | Name of the field where memory should be allocated. |
new size | Must be a digit or a variable containing a digit that represents new size of memory to be allocated. |
str:=Struct("LPTSTR name") str.SetCapacity("name",2000) previous_pointer := str.GetPointer("name") str.name:="AutoHotkey" MsgBox previous_pointer " = " str.GetPointer("name") "`n" str.name ; as you can see pointer did not change since memory was only reduced. MsgBox str.name.GetCapacity() ; however, allocated size changed.
When memory is reallocated the content will be copied to new memory. When a string is assigned to a field the memory is reallocated unless the size is exactly the needed size for the string + terminator.
str:=Struct("LPTSTR name") str.SetCapacity("name",100) MsgBox str.GetCapacity("name") str.name:="AutoHotkey" MsgBox str.GetCapacity("name")
Returns the size in bytes of a structure or field.
OutputVar := Struct.Size([field])
OutputVar | The name of the Variable in which to store the size of field or structure. |
field | Name of existing field within the structure, if omitted the size of structure is returned. |
pt:=Struct("x,y") MsgBox pt.Size() ; returns 8
struct:=Struct("Int64 x,y") MsgBox struct.Size("y") ; returns 8If structure is an array you will needle to pass a digit to retrieve the size of a field.
struct:=Struct("Int64[2]") MsgBox struct.Size(1) ; returns 8
s:=Struct("LPTSTR str") s.str:="Hello World!" MsgBox StrGet(s.str["",""])
Struct supports type only strcuctures for all default types like
Int,Byte,Char... .
Those will be created as array, so "char" is equivalent to "char[1]".
To access the field of such structures you will always need to use struct.1 or struct[1] or struct["1"].<>
u:=Struct("UInt") ; equivalent to UInt[1] u.1:=10 MsgBox u.1
Same way arrays are supported.
u:=Struct("UInt[10]") u.10:=100 MsgBox u.10
Struct also supports pointers.
mystruct:=Struct("*int") ; same as "*int[1]" mystruct.SetCapacity(1,8) mystruct.1.1:=100 MsgBox mystruct.1.1
Struct supports custom structures and fields.
POINT:="Int x, Int y" pt:=Struct("POINT p",,{ p: { x:10, y:20 } } ) MsgBox pt.p.x " , " pt.p.yAlso pointers are supported, however before the field can be accessed we have to allocate memory to it.
POINT:="Int x, Int y" pt:=Struct("POINT *p") pt.SetCapacity("p",sizeof(pt.p)) pt.p.x:=100, pt.p.y:=200 MsgBox pt.p.x " , " pt.p.y
The memory will be managed
internally and freed whenever the object is deleted.
For strings memory will be initialized automatically before.
s:=Struct("LPTSTR string") s.string:="Hello World!" MsgBox s.string
The size of allocated memory can be retrieved with .GetCapacity() method
MsgBox s.GetCapacity("string")
Whenever a new string is assigned, memory will be reallocated if the new string is not same lengths.
To free the memory manually we can use .SetCapacity().
s.SetCapacity("string",0) MsgBox s.GetCapacity("string")
You can manually allocate memory to fields using .SetCapacity()
method.
However, to keep allocated memory you have to use StrPut("new string",s.string[""]) to write the string,
otherwise the memory will be reallocated if new string is different length.
s:=Struct("LPTSTR string") s.SetCapacity("string",260)
Also bit fields are supported, see Bit Fields for more information.
Bits:=Struct(" ( { Byte int; struct { Byte a:1,b:1,c:1,d:1,e:1,f:1,g:1,h:1; } } )") Loop 0xFF { bit:=(Bits.int:=A_Index) "`t" For k, v in Bits If A_Index>1 bit.= v " " ToolTip "int bits: 1 2 3 4 5 6 7 8`n" bit Sleep 200 }
Using the for loop we can enumerate the structure to retrieve field names
and their values.
Enumeration will be executed in same order as the structure was defined.
s:=Struct("Byte x,Int u,LPTSTR str") s.x:=10 s.u:=1000 s.str:="AutoHotkey" for k,v in s MsgBox k ": " v
Also Arrays can be enumerated same way.
x:=Struct("UInt[10]",[9,8,7,6,5,4,3,2,1,0]) for k, v In x MsgBox k ": " v
Note! Fields of unknown size like "Uint *a" can be only enumerated to first value and not deeper!
To enumerate the structure completeley it needs to be defined completely!
MyStruct:="UInt[3]" pt:=Struct("MyStruct *a") pt.a.SetCapacity(1,100) pt.a.1:=[100,200,300] for k,v in pt.a.1 MsgBox k "=" v
; Create array from existing memory _POINT:="x,y" pt:=Struct("*_POINT") ; similar to "*_POINT[1] but we have to allocate memory" pt.SetCapacity(1,A_PtrSize),pt.SetCapacity(2,A_PtrSize) pt.1.SetCapacity(1,8),pt.2.SetCapacity(1,8) ; allocate memory pt.1.1.x:=100, pt.2.1.x:=200 MsgBox pt.1.1.x " / " pt.2.1.x
; More examples pt:=Struct("x,y") ;POINT structure pt.x:=100 MsgBox pt.x rc:=Struct("left,top,right,bottom") ; RECT structure Gui:=GuiCreate() Gui.Show("w640 h480") DllCall("GetWindowRect","PTR",gui.hwnd,"PTR",rc[]) MsgBox "left: " rc.left "`ntop: " rc.top "`nright: " rc.right "`nbottom: " rc.bottom Gui.Destroy
; Array Examples ; Simple array structures. ; Array is always accessed using integer arr:=Struct("Uint[10]") arr.5:=10 MsgBox arr.5 MyArray:="a,b" arr:=Struct("MyArray[10]") arr.1.a:=1 arr.2.b:=2 MsgBox arr.1.a " / " arr.2.b
; Pointer Examples ; SIMPLE POINTER* int:=Struct("*UInt") int.SetCapacity(1,4) int.1.1:=100 MsgBox int.1.1
; Pointer to array of pointers s:=Struct("**UInt") s.SetCapacity(1,8) ; array of 2 to pointer to uint s.1.SetCapacity(1,8),s.1.SetCapacity(2,8) s.1.1.SetCapacity(1,8),s.1.2.SetCapacity(1,8) s.1.1.1:=10 s.1.1.2:=20 s.1.2.1:=30 s.1.2.2:=40 MsgBox s.1.1.1 "`n" s.1.1.2 "`n" s.1.2.1 "`n" s.1.2.2 s[]:=[[[50,60],[70,80]]] MsgBox s.1.1.1 "`n" s.1.1.2 "`n" s.1.2.1 "`n" s.1.2.2
; String Examples ; Simple user defined structure user:="UInt Id, LPTSTR Name" users := Struct("user[2]") ; array of structs users.1.Id := 1 ,users.2.Id := 2 users.1.Name := "Admin" ,users.2.Name := "User" MsgBox users.1.Id "`t" users.1.Name "`n" users.2.Id "`t" users.2.Name ; we can use an object to assign values too users[]:=[{id:2,name:"Struct"},{id:2,name:"Object"}] MsgBox users.1.Id "`t" users.1.Name "`n" users.2.Id "`t" users.2.Name
; Char array String:=Struct("TCHAR char[26]") Loop 26 string["char"][A_Index]:=Chr(A_Index+64) Loop 3 MsgBox String["char"][A_Index*2] ;show some characters MsgBox StrGet(string[],26) ;get complete string
; rect example Gui:=GuiCreate() _RECT:="left,top,right,bottom" RC:=Struct(_RECT) ;create structure Gui.Add("Text",,"Press Escape to continue") Gui.Show("w200 h100") ;show window DllCall("GetWindowRect","PTR",Gui.hwnd,"PTR",rc[]) ;get window position rc.right := rc.right - rc.left ;Set rc.right to be the width rc.bottom := rc.bottom - rc.top ;Set rc.bottom to be the height While DllCall("GetCursorPos","PTR",rc[]) { DllCall("MoveWindow","PTR",gui.hwnd,"int",rc.left,"int",rc.top,"int",rc.right,"int",rc.bottom,"Int",1) If GetKeyState("Escape","P") break } Gui.Destroy
; Findfirstfile example _FILETIME := "dwLowDateTime,dwHighDateTime" _SYSTEMTIME := "WORD wYear,WORD wMonth,WORD wDayOfWeek,WORD wDay,WORD wHour,WORD wMinute,WORD wSecond,WORD Milliseconds" _WIN32_FIND_DATA := "dwFileAttributes,_FILETIME ftCreationTime,_FILETIME ftLastAccessTime,_FILETIME ftLastWriteTime,UInt nFileSizeHigh,nFileSizeLow,dwReserved0,dwReserved1,TCHAR cFileName[260],TCHAR cAlternateFileName[14]" file:=Struct("_WIN32_FIND_DATA[2]") time:=Struct(_SYSTEMTIME) DllCall("FindFirstFile","Str",A_ScriptFullPath,"Uint",file.1[""]) DllCall("FindFirstFile","Str",A_AhkPath,"UInt",file.2[""]) MsgBox StrGet(file.1.cFileName[""]) MsgBox "A_ScriptFullPath:`t" StrGet(file.1.cFileName[""]) "`t" StrGet(file.1.cAlternateFileName[""]) "`nA_AhkPath:`t" StrGet(file.2.cFileName[""]) "`t" StrGet(file.2.cAlternateFileName[""]) handle:=DllCall("FindFirstFile","Str","C:\*","Uint",file.2[""]) Loop { If !DllCall("FindNextFile","Uint",handle,"Uint",file.2[""]) break DllCall("FileTimeToSystemTime","Uint",file.2.ftLastWriteTime[""],"Uint",time[""]) ToolTip StrGet(file.2.cFileName[""]) "`n" StrGet(file.2.cAlternateFileName[""]) "`n" file.2.nFileSizeHigh " - " file.2.nFileSizeLow . "`n" time.wYear . "-" time.wMonth . "-" time.wDay . "`n" time.wDayOfWeek . "`n" time.wHour . ":" time.wMinute . ":" time.wSecond . ":" time.Milliseconds Sleep 200 } ToolTip DllCall("FindClose","Uint",handle)
; Process32first example MAX_PATH:=260 _PROCESSENTRY32:= ( "DWORD dwSize; DWORD cntUsage; DWORD th32ProcessID; ULONG_PTR th32DefaultHeapID; DWORD th32ModuleID; DWORD cntThreads; DWORD th32ParentProcessID; LONG pcPriClassBase; DWORD dwFlags; TCHAR szExeFile[" MAX_PATH "];" ) pEntry:= Struct(_PROCESSENTRY32) pEntry.dwSize := sizeof(_PROCESSENTRY32) hSnapshot:=DllCall("CreateToolhelp32Snapshot","UInt",TH32CS_SNAPALL:=0x0000001F,"PTR",0) DllCall("Process32First" (A_IsUnicode?"W":""),"PTR",hSnapshot,"PTR",pEntry[""]) While (A_Index=1 || DllCall("Process32Next" (A_IsUnicode?"W":""),"PTR",hSnapshot,"PTR",pEntry[""])) { ToolTip pEntry.cntUsage "`n" pEntry.th32ProcessID . "`n" pEntry.th32DefaultHeapID "`n" pEntry.th32ModuleID . "`n" pEntry.cntThreads "`n" pEntry.th32ParentProcessID . "`n" pEntry.pcPriClassBase "`n" pEntry.dwFlags "`n" StrGet(pEntry.szExeFile[""]) Sleep 150 } ToolTip
; Listprocessmodules example MAX_PATH:=260 MAX_MODULE_NAME32:=255 _MODULEENTRY32:= ( "DWORD dwSize; DWORD th32ModuleID; DWORD th32ProcessID; DWORD GlblcntUsage; DWORD ProccntUsage; BYTE *modBaseAddr; DWORD modBaseSize; HMODULE hModule; TCHAR szModule[" MAX_MODULE_NAME32 + 1 "]; TCHAR szExePath[" MAX_PATH "];" ) ListProcessModules(DllCall("GetCurrentProcessId")) Return ListProcessModules(dwPID) { global _MODULEENTRY32 static TH32CS_SNAPMODULE:=0x00000008,INVALID_HANDLE_VALUE:=-1 me32 := Struct(_MODULEENTRY32) ; Take a snapshot of all modules in the specified process. hModuleSnap := DllCall("CreateToolhelp32Snapshot","UInt", TH32CS_SNAPMODULE,"PTR", dwPID ) if( hModuleSnap = INVALID_HANDLE_VALUE ) { MsgBox "CreateToolhelp32Snapshot (of modules)" return FALSE } ; Set the size of the structure before using it. me32.dwSize := sizeof("_MODULEENTRY32") ; Retrieve information about the first module, ; and exit if unsuccessful if( !DllCall("Module32First" (A_IsUnicode?"W":""),"PTR", hModuleSnap,"PTR", me32[] ) ) { MsgBox "Error Module32First`n" ErrorMessage() ; // Show cause of failure DllCall("CloseHandle","PTR", hModuleSnap ) ; // Must clean up the snapshot object! return FALSE } ;// Now walk the module list of the process, ;// and display information about each module while(A_Index=1 || DllCall("Module32Next" (A_IsUnicode?"W":""),"PTR",hModuleSnap,"PTR", me32[""] ) ) { ToolTip "`tMODULE NAME`t=`t" StrGet(me32.szModule[""]) . "`n`texecutable`t=`t" StrGet(me32.szExePath[""]) . "`n`tprocess ID`t=`t" me32.th32ProcessID . "`n`tref count (g)`t=`t" me32.GlblcntUsage . "`n`tref count (p)`t=`t" me32.ProccntUsage . "`n`tbase address`t=`t" me32.modBaseAddr[""] . "`n`tbase size`t=`t" me32.modBaseSize Sleep 200 } ;// Do not forget to clean up the snapshot object. DllCall("CloseHandle","PTR",hModuleSnap) return TRUE }
; Enumerate a structure. ; enumerate simple structure MyStruct:="a,b,c" s:=Struct(MyStruct,,{a:1,b:2,c:3}) for k, v in s MsgBox k ": " v ; ENUMERATE ARRAY OF STRUCTURES MyStruct:="a,b,c" s:=Struct("MyStruct[3]",,[{a:1,b:2,c:3},{a:4,b:5,c:6},{a:7,b:8,c:9}]) for k, v in s for key,value in v MsgBox key ": " value ; ENUMERATE DYNAMIC STRUCTURE MyStruct:="a,b,c" s:=Struct("Short size,LPTSTR name,MyStruct ms",,{size:sizeof(MyStruct),name:"MyStruct",ms:{a:1,b:2,c:3}}) for k, v in s if !IsObject(v) MsgBox k ": " v else for key,value in v MsgBox key ": " value