Struct()

Auto Hotkey

Struct

Struct() is a build-in function that creates and returns a special structure object.
This special object can be used to access the stucture its fields using object syntax.


OutputVar := Struct(Definition ,AddressToStructure, InitObject)
Function Example: pt := Struct("int x;y",{x:10,y:20})

Parameters

OutputVar

The name of the variable in which to store the Structure Object.

Definition

This parameter must be a string containing the structure definition.
Definition is similar to C so most structures can be transformed very easily, also default data types can be used.
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, y Int and d Char.
If only one type/key is given, e.g. "UInt", it is assumed to be a structure definition that resolves to default type or a variable that describes the structure.
Note! something like "len;" or "MyVar," are interpreted as "UInt length" and "UInt MyVar" unless len and MyVar are a variable defining the structure.
So 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("MyVar") will be same as Struct("UInt MyVar").

_POINT:="(
Int x; // also comments in C style are supported
       // empty lines will be simply ignored.
Int y; // last ; is optional and can be omitted.
)"
New lines can be omitted too. This way the definition can be written much more compact.
_POINT:="Int x,Int 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"
UNION AND STRUCT
Struct supports unions and structures in structures, note such structures do not have a name so you can't use same name for a field in main structure and sub-structure.
_MyUnion:="
(
union {
  UInt int;
  struct {
    UShort x;
    UShort y;
  };
  struct {
    Byte a;
    Byte b;
    Byte c;
    Byte d;
  };
};
)"
mys:=Struct(_MyUnion)
mys.int:=0xFFFFFFFF
MsgBox % mys.int "`n" mys.x " " mys.y "`n" mys.a " " mys.b " " mys.c " " mys.d
Create structure from a string containing the structure definition.
pt:=Struct("UInt x,UInt y")
The definition can be saved in a variable. Struct will also resolve the given string to the variable if necessary.
POINT:="UInt x,UInt y"
pt:=Struct("POINT")
An existing structure object can be used too.
POINT:=Struct("x,y")
pt:= Struct(POINT)
Global / Static / Local variables

Inside a function you can also use local and static variables for structure definition.

Even when this objects are returned the static variable refference in definition can be resolved dynamically.

It is also possible to create a structure from a static variable even if you are not inside the 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)
}
AddressToStructure

Address of memory representing the structure. This variable is used to access an existing structure in memory.
When no AddressToStructure is given but InitObject is used, it must be passed in this parameter, see InitObject example. For example here we would use the memory of variable pointMem for the structure.

VarSetCapacity(pointMem,8)
pt:=Struct("x,y",&pointMem)
pt.x:=10
MsgBox % pt[] " = " (&pointMem) "`n" pt.x " = " NumGet(pointMem,"UInt")
InitObject

Initialize your structure right away. To initialize the structure you will need to pass an object with keys and values.
The order of keys and values is not relevant because it will be enumerated in alphabetical order anyway.
When no AddressToStructure is given, InitObject must be used in AddressToStructure parameter.

pt:=Struct("x,y",{x:100,y:200})
MsgBox % pt.x "`n" pt.y

Methods

Size

Returns the size in bytes of a structure or its 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 8
If 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

Offset

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

CountOf

Returns size of array or 0 if structure or field is not an array.

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

Fill

Fills the structure with given value.

Struct.Fill([value])
value A digit value or character that will fill the structure. Mainly used to empty/clear a structure.
pt:=Struct("x,y",{x:10,y:20})
MsgBox % pt.x
pt.Fill() ; same as pt.Fill(0)
MsgBox % pt.x
s:=Struct("Byte[10]")
s.Fill("A")
MsgBox % StrGet(s[],10,"CP0")

GetAddress

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("x") " = " pt.x[""]

Encoding

Returns encoding for field or structure.

OutputVar := Struct.Encoding([field])
OutputVar The name of the Variable in which to store the encoding.
field Name of existing field within the structure. When omitted, Encoding of structure itself will be returned.
If type of field or structure is not one of String types (TCHAR, CHAR, UCHAR, LPTSTR...) -1 is returned. Otherwise it returns 0 for CP0 and 1200 for UTF-16 ...
Other encoding types have to use StrGet and StrSet to retrieve correct text.
str1:=Struct("LPTSTR name")
str2:=Struct("LPTSTR")
MsgBox % str1.Encoding("name") " = " str2.Encoding()

IsPointer

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") "`n" s.IsPointer("b")
s:=Struct("UInt*")
MsgBox % s.IsPointer()

GetPointer

Returns pointer 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, reads and returns pointer of first array item in our 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["",""]))

SetCapacity

Set new size for our structure or pointer, returns true 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 capacity should be set, if omitted structure capacity will be set. This is mainly used for arrays to shrink or increase the size. When setting capacity for main structure, its old memory will be freed if necessary and new memory will be zero-filled.
new size Must be a digit or a variable containing a digit that represents new size of our field or structure.
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.

You can allocate and use memory as you like, for example here we will store a String and the pointer in same memory block. Of course when you allocate new memory here you will loose contents.

str:=Struct("LPTSTR")
str.SetCapacity(2000)
str.1[""]:=str[]+A_PtrSize
str.1:="AutoHotkey"
MsgBox % str.1

GetCapacity

Returns Capacity previously set using .SetCapacity() or via 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, if omitted returns structure capacity.
str:=Struct("LPTSTR name")
str.SetCapacity("name",2000)
MsgBox % str.GetCapacity("name")

Clone

Returns new structure object of same type.

OutputVar := Struct.Clone([AddressOfStructure,InitObject])
OutputVar The name of the Variable in which to store the new structure object.
AddressOfStructure Address to memory for this structure. When omitted, memory will be reserved and set internally.
InitObject An object used to initialize the structure.
pt:=Struct("x,y")
pt1:=pt.Clone({x:10,y:20})
MsgBox % pt1.x "-" pt1.y
You can do the same using the new operator
pt:=Struct("x,y")
pt1:= new pt({x:10,y:20})
MsgBox pt1.x "-" pt1.y


Features and Remarks
Some remarks about structure objects and more features.

A structure object cannot be altered, so you cannot add more fields to it. The only exeption are arrays, here items are resolved dynamically.
You can receive the address of structure or key using empty key (e.g. struct.item[""]). For structure objects also [] can be used (e.g. struct[] or struct[""]).

When a key is not given a type, e.g. "LPTSTR key1,key2", previous type is used. If the first key lacks a type, Uint is used, so "key1,key2" is equivalent to "UInt key1,key2" or "UInt key1,UInt key2".

Note: pointer needs to be specified for each element, so "*UInt key1,key2" is equivalent to "UInt *key1,UInt key2". If both elements are pointers "UInt *key1,*key2" must be used.

To access a pointer in pointer you can specify empty key several times, e.g. object["",""] would get the pointer at address of object[].
Same is valid for keys, so here you'll receive the pointer.
s:=Struct("LPTSTR str")
s.str:="Hello World!"
MsgBox % StrGet(s.str["",""])
TYPE ONLY DEFINITION Struct supports type only definition for all default types like Int,Byte,Char... .
To access fields of such structures you will allways need to use digits like in arrays.
u:=Struct("UInt") ; this is similar to UInt[1]
u.1:=10
MsgBox % u.1
ARRAYS Same way arrays are supported.
u:=Struct("UInt[10]")
u.10:=100
MsgBox % u.10
ARRAYS OF UNKNOWN SIZE Any structure can be used as array.
However be careful, accessing memory that does not belong to the structure may crash your application.
pt:=Struct("x,y")
pt.SetCapacity(16) ; increase capacity to hold 2 structures
pt.x := 1 ; we can access the first field right away, similar to pt.1.x := 1
pt.2.x := 2 ; here we access the second structure
MsgBox % pt.2.x " != " pt.x
ENUMERATING A STRUCTURE Using a 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, not alphabetically like for simple objects.
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 dedicated Arrays can be enumerated same way.
x:=Struct("UInt[10]",[0,9,8,7,6,5,4,3,2,1])
for k, v In x
  MsgBox % k ": " v
DYNAMIC AND STATIC FIELDS AND STRUCTURES Struct supports calling dynamic structures and fields, such that are defined but do not exist as an object/field.
These objects/fields are created dynamically whenever needed and invoked/called automatically internally.
A static field on the other hand is defined directly in structure object.
For Example, here x and y are static fields.
myStruct:=Struct("Int x, Int y")
Dynamic fields and structures are little different to static, they use refference to the definition to create the structure internally.
So here x and y fields will be created dynamically only when necessary.
POINT:="Int x, Int y"
pt:=Struct("POINT p", { p: { x:10, y:20 } } ) ; same structure as above but POINT structure is resoved dynamically.
MsgBox % pt.p.x " , " pt.p.y
Furthermore, you can allocate memory for static fields using .SetCapacity() method, see below. This memory will be managed internally and freed whenever the object is deleted (last
reference is released).
This feature allows assigning a string without initializing memory manually before.
s:=Struct("LPTSTR string")
s.string:="Hello World!"
MsgBox % s.string
The size of allocated memory is backed up internally and can be retrieved with .GetCapacity() method
MsgBox % s.GetCapacity("string")
Whenever a new string is assigned, memory will be only reallocated if a larger memory is needed.
To free the memory manually use .SetCapacity() method.
s.SetCapacity("string",0)
MsgBox % s.GetCapacity("string")
You can manually allocate memory to fields using .SetCapacity() method.
s:=Struct("LPTSTR string")
s.SetCapacity("string",260)

NOTE You cannot allocate memory for dynamic fields.
Calling .GetCapacity() method on dynamic field will return -1.
Also calling .SetCapacity() method will simply fail.
BIT FIELDS
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 100
}

Related

sizeof, StructTypes , DllCall, NumGet, NumPut

Examples

 ; SIMPLE STRUCTURE
pt:=Struct("x,y")
; SAME STRUCTURE CREATED FROM VARIABLE
_POINT:="x,y"
pt:=Struct(_POINT)
; ARRAY OF POINTS
pt:=Struct("_POINT[10]")
; ARRAY OF POINTS OF UNKNOWN SIZE
pt:=Struct("*_POINT")

; TO CREATE ARRAY FROM POINTER
_POINT:="x,y", VarSetCapacity(pt1,8)
pt:=Struct("*_POINT") ; similar to "*_POINT[1]"
pt["",""]:=&;pt1 ; assign pointer to fist item
pt.x:=1 ; same as pt.1.x
MsgBox % pt.x


; Create a structure from structure
pt1:= new pt
pt1:= pt.Clone()


; 				More examples
pt:=Struct("x,y") ;POINT structure
pt.x:=100
MsgBox % pt.x
rc:=Struct("left,top,right,bottom") ; RECT structure
Gui,Show,w100 h100,Test
Gui,+LastFound
DllCall("GetWindowRect","PTR",WinExist(),"PTR",rc[])
MsgBox % "left: " rc.left "`ntop: " rc.top "`nright: " rc.right "`nbottom: " rc.bottom



; 				Array Examples
; Simple array structures.
; Array is always accessed using integer
array:=Struct("Uint[10]")
array.5:=10
MsgBox % array.5
MyArray:="a,b"
array:=Struct("MyArray[10]")
array.1.a:=1
array.2.b:=2
MsgBox % array.1.a "`n" array.2.b



;				Pointer Examples
; SIMPLE POINTER*
int:=Struct("*UInt"), VarSetCapacity(mem,100)
int["",""]:=&mem
int.1:=100 ; automatically resolves to pointer.
MsgBox % int.1 ; again pointer is resolved automatically
; POINTER TO ARRAY OF POINTERS
VarSetCapacity(Arr,10*A_PtrSize,0)
Loop 10 ; create 10 arrays to hold 10 integers
 VarSetCapacity(Arr%A_Index%,10*sizeof("UInt"),0)
 ,NumPut(&Arr%A_Index%,&arr,(A_Index-1)*A_PtrSize,"PTR")
s:=Struct("**UInt")
s["",""]:=&Arr
s.1.1:=10
s.2.10:=20
MsgBox % s.1.1 "-" s.2.10
; ARRAY OF POINTERS
VarSetCapacity(Arr,10*A_PtrSize,0)
Loop 10 ; create 10 arrays to hold 10 integers
  VarSetCapacity(Arr%A_Index%,10*sizeof("UInt"),0)
	,NumPut(&Arr%A_Index%,&arr,(A_Index-1)*A_PtrSize,"PTR")
s:=Struct("*UInt[10]",&Arr)
s.1.1:=30
s.2.10:=40
MsgBox % s.1.1 "-" s.2.10
; ARRAY OF POINTERS TO POINTERS
VarSetCapacity(Arr,10*A_PtrSize,0)
Loop 10 ; create 10 arrays to hold 10 arrays of 10 integers
{
 i:=A_Index
 VarSetCapacity(Arr%i%,10*A_PtrSize,0)
 ,NumPut(&Arr%i%,&arr,(i-1)*4,"PTR")
 Loop 10
  VarSetCapacity(Arr%i%_%A_Index%,10*sizeof("UInt"),0)
	,NumPut(&Arr%i%_%A_Index%,&arr%i%,(A_Index-1)*4,"PTR")
}
s:=Struct("**UInt[10]",&Arr)
s.1.1.1:=50, s.2.3.10:=60
MsgBox % s.1.1.1 "-" s.2.3.10



; 				String Examples
; SIMPLE USER DEFINED STRUCTURE
user:="UInt Id, LPTSTR Name"
users := Struct("user[2]") ; array of structs
; here no automatic String allocation can be done because users is evaluated dynamically so we use own memory
Loop 2
 VarSetCapacity(Str%A_Index%,256)
users.1.name[""]:=&Str1 ,users.2.name[""]:=&Str2
; above assignment can be assigned via object too
; users:=[{name:{"":&Str1}},{name:{"":&Str2}}]
users.1.Id := 1 ,users.2.Id := 2
users.1.Name := "Admin" ,users.2.Name := "User"
; same here, we could use an object to assign values
; users:=[{id:1,name:"Admin"},{id:2,name:"User"}]
MsgBox % users.1.Id "`t" users.1.Name "`n" users.2.Id "`t" users.2.Name
; Now to do the same with automatic String memory we would need to use non dynamic structure
; Therefore we define the structure completely and we can initialize it directly
users := Struct("UInt id1, LPTSTR Name1,UInt id2,LPTSTR Name2",{id1:1,name1:"Admin",id2:2,name2:"User"})
MsgBox % users.id1 "`t" users.name1 "`n" users.id2 "`t" users.name2

; 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,+LastFound
hwnd:=WinExist() ;get window handle
_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",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",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,"
. "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("CloseHandle","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 "];
 )"
VarSetCapacity(string,260)
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, 200
}


; 				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"))
ListProcessModules(dwPID){
 global _Struct
 static TH32CS_SNAPMODULE:=0x00000008,INVALID_HANDLE_VALUE:=-1
 hModuleSnap := Struct("HANDLE")
 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 % "Module32First" ; 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 % "MODULE NAME`t=`t" StrGet(me32.szModule[""])
	 . "`nexecutable`t=`t" StrGet(me32.szExePath[""])
	 . "`nprocess ID`t=`t" me32.th32ProcessID
	 . "`nref count (g)`t=`t" me32.GlblcntUsage
	 . "`nref count (p)`t=`t" me32.ProccntUsage
	 . "`nbase address`t=`t" me32.modBaseAddr[""]
	 . "`nbase 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