Struct

Struct 是一个创建并返回结构对象的内置函数。
该对象可用于使用对象语法访问定义的结构。
SetCapacity 方法可用于为结构体和指针分配内存。

Methods:

Struct 需要一个定义并可选地接受结构内存和初始化对象的地址。

OutputVar := Struct(Definition ,StructMemory, InitObject)
函数示例: pt := Struct("int x;y",,{x:10,y:20})

参数

OutputVar(输出变量)

用于存储 Structure 对象的变量的名称。

Definition(定义)

该参数必须是包含结构定义的字符串或变量。
定义与C类似,因此大多数结构可以直接使用或很容易转换。
Following 默认数据类型 可以在Structures中使用,其他类型必须在脚本中定义。
可以使用分号(;)或逗号(,)来分隔字段,甚至可以混合使用。
如果没有给定第一个字段的类型,将使用 UInt,否则将使用先前的类型。
例如在 “a,Int x,y,字符 c,d”,a 将为 UInt,x 和 y 为 Int,c 和 d 为 Char。
如果只给出一种类型,例如 "UInt""POINT",假设它是一个由 1 组成的数组,与 "UInt[1]""POINT[1]"

注意! 如果定义包含逗号或分号,例如 "len;""MyVar," 它被解释为 "UInt length""UInt MyVar",
去哪里 Struct("MyVar"), MyVar 必须定义一个结构,例如 MyVar := "Int x, Int y",如果没有 MyVar 变量或者它为空 Struct() 会抛出错误。

评论和一些例子:

从包含结构定义的字符串创建结构。

pt:=Struct("Int x,Int y")

定义可以保存在变量中。 Struct 如有必要,还将把给定的字符串解析为变量,但是该结构将被创建为 1 的数组,因此 Struct("POINT") 等于 Struct("POINT[1]").
这将改变如何访问字段的语法,例如 pt:=Struct(点)pt:=Struct("UInt x,y") 您可以直接访问这些值,例如pt.x 在哪里 pt:=Struct("点")pt:=Struct("POINT[1]") 您将需要使用 pt.1.x 或 pt[1].x 或 pt["1"].x。
求值顺序始终为 Structure -> 数组 -> 指针 -> 等 -> 字段。
所以在下面的结构中 s:=Struct("*UInt") 这与以下相同 s:=Struct("*UInt[1]"),
第一个数组被访问 s.1,然后是指针 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")  ; 与 pt:=Struct("POINT[1]") 相同

如果没有注释,新行也可以省略。这样定义就可以写得更加紧凑。

POINT:="Int x,y"

UInt 是默认类型,因此如果负值不相关(对于负值必须使用 Int),也可以省略它。

POINT:="x,y"

UNION AND STRUCT

Struct 支持结构中的联合和结构。请注意,子结构不能有名称,因此主结构和子结构中的字段不能使用相同的名称。
您可以简单地在结构名称前面加上前缀,例如虚拟_a、虚拟_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.d
Global / Static / Local variables

我们可以从函数外部甚至另一个函数中的静态函数变量创建结构。因此,请包含函数名称并将变量括在括号中。如果您从静态变量创建静态结构(如此处的 MyFunc 中所示),这也是必要的。

MyFunc() ; 使用这种方法我们可以创建静态结构
AnotherFunc() ; 该方法可以在任何地方使用来访问变量
pt:=Struct("MyFunc(POINT)",,{x:100,y:200}) ; 即使在函数之外我们也可以访问定义变量
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)
}
StructMemory(结构内存,可选)

表示结构的内存地址。该变量用于访问内存中现有的结构。例如这里我们将使用变量的内存 pointMem 对于结构。

VarSetCapacity(pointMem,8)
pt:=Struct("x,y",&pointMem)
pt.x:=10
MsgBox %  pt[] " = " (&pointMem) "`n" pt.x " = " NumGet(pointMem,"UInt")
InitObject(初始化对象,可选)

使用对象、映射、数组或其他结构立即初始化您的结构。
键和值的顺序不相关。

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

方法

Clone

返回相同类型的新结构对象。

OutputVar := Struct.Clone()
OutputVar 用于存储新结构对象的变量的名称。
pt:=Struct("x,y")
pt1:=pt.Clone()
pt1[]:={x:10,y:20}
MsgBox %  pt1.x "-" pt1.y

CountOf

返回数组定义的大小,如果结构或字段不是数组,则返回 0。
注意! 可以使用 SetCapacity 增加数组大小,但 CountOf 将始终返回定义的值。

OutputVar := Struct.CountOf([field])
OutputVar 用于存储数组长度的变量的名称。
field 结构内现有字段的名称..
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() ; 仍会返回 2

Encoding

返回字段的编码。

OutputVar := Struct.Encoding([field])
OutputVar 用于存储编码的变量的名称。
field 结构内现有字段的名称。
如果字段或结构的类型不是字符串类型(TCHAR、CHAR、UCHAR、LPTSTR...)之一,则返回 -1。否则,对于 CP0,它返回 0;对于 UTF-16,它返回 1200 ...
其他编码类型必须使用 StrGet 和 StrSet 来检索正确的文本。
str1:=Struct("LPSTR name")
str2:=Struct("LPTSTR name")
MsgBox %  str1.Encoding("name") " != " str2.Encoding("name")

GetAddress

返回字段或结构的地址。

OutputVar := Struct.GetAddress([field])
OutputVar 用于存储地址的变量的名称。
field 结构内现有字段的名称。省略时,返回结构本身的地址。要获取结构的地址,您还可以使用 [] 或 [""] 作为结构对象,使用 [""] 作为其字段。 注意 字段不能使用 []。
pt:=Struct("x,y")
MsgBox %  pt.GetAddress() " = " pt[]
MsgBox %  pt.GetAddress("y") " = " pt.y[""]

GetCapacity

返回先前使用 .SetCapacity() 或通过分配字符串分配的容量。

OutputVar := Struct.GetCapacity([field])
OutputVar 用于存储容量(以字节为单位)的变量的名称。
field 我们结构中现有字段的名称。
str:=Struct("LPTSTR name")
str.SetCapacity("name",2000)
MsgBox %  str.GetCapacity("name")

GetPointer

返回保存在结构或字段中的已分配内存的指针。

OutputVar := Struct.GetPointer([field])
OutputVar 用于存储地址的变量的名称。
field 结构内现有字段的名称。省略时,返回结构中第一项的指针。
str:=Struct("LPTSTR name",,{name:"AutoHotkey"})
MsgBox %  str.GetPointer("name") "`n" StrGet(str.GetPointer("name"))

也可以使用“”来读取指针。因此,[""] 返回地址,["",""] 返回指针,["","",""] 指向指针的指针,依此类推。

str:=Struct("LPTSTR name",,{name:"AutoHotkey"})
MsgBox %  str.name["",""] "`n" StrGet(str.name["",""])

IsPointer

如果字段或结构是指针,则返回 true。

OutputVar := Struct.IsPointer([field])
OutputVar 如果字段或结构是指针,则在其中存储 true 的变量名称,否则存储 0 / false。
field 结构内现有字段的名称。省略时,如果结构本身是指针,则返回 true。
s:=Struct("UInt *a,UInt b")
MsgBox %  s.IsPointer("a") " != " s.IsPointer("b")
s:=Struct("UInt*")
MsgBox %  s.IsPointer()

Offset

返回字段的偏移量。

OutputVar := Struct.Offset(field)
OutputVar 用于存储偏移量的变量的名称。
field 结构内现有字段的名称。
pt:=Struct("x,y")
MsgBox %  pt.Offset("y") ; returns 4
; 注意!因为结构动态解析指针和数组,所以它们的偏移量将相对于父项
MyStruct:="Int a,b"
pt:=Struct("MyStruct a[2]")
MsgBox %  pt.a.Offset(2)  ; returns 8
       . " / " pt.a.2.Offset("b") ; returns 4

SetCapacity

为字段分配内存,如果分配了新内存,则返回分配的大小。

OutputVar := Struct.SetCapacity([field,] newsize)
OutputVar 用于存储新大小的变量的名称。
field 应分配内存的字段名称。
新尺寸 必须是数字或包含数字的变量,表示要分配的新内存大小。
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 ; 正如你所看到的,指针没有改变,因为内存只是减少了。
MsgBox %  str.name.GetCapacity() ; 但是,分配的大小发生了变化。

当内存被重新分配时,内容将被复制到新内存。将字符串分配给字段时,将重新分配内存,除非大小恰好是字符串+终止符所需的大​​小。

str:=Struct("LPTSTR name")
str.SetCapacity("name",100)
MsgBox %  str.GetCapacity("name")
str.name:="AutoHotkey"
MsgBox %  str.GetCapacity("name")

Size

返回结构或字段的大小(以字节为单位)。

OutputVar := Struct.Size([field])
OutputVar 用于存储字段或结构大小的变量的名称。
field 结构中现有字段的名称,如果省略,则返回结构的大小。
pt:=Struct("x,y")
MsgBox %  pt.Size() ; returns 8
struct:=Struct("Int64 x,y")
MsgBox %  struct.Size("y") ; returns 8
如果结构是一个数组,您将需要传递一个数字来检索字段的大小。
struct:=Struct("Int64[2]")
MsgBox %  struct.Size(1) ; returns 8
Features and Remarks(功能和备注)
关于结构对象和更多功能的一些评论。
默认情况下,结构与最大的后续项对齐,但是可以通过在结构开头指定后跟 : 的对齐方式来更改这一点。
例如 sizeof("Int a,Int64 b") == 16,其中 sizeof("4:Int a, Int64 b") == 12。
Base__Class 是属性,无法使用点 (.) 语法访问,请使用 MyStruct["base"] 代替!
结构对象可以调整大小,因此您甚至可以通过增加分配的内存来创建结构数组。
您可以使用空密钥接收结构或密钥的地址(例如 MyStruct.item[""])。同样也可用于结构对象(例如 MyStruct[] 或 MyStruct[""])。

当密钥没有指定类型时,例如 “LPTSTR 键1,键2”,使用以前的类型。如果第一个键缺少类型,则使用 Uint,因此 “键1,键2” 相当于 “UInt key1,key2”“UInt key1,UInt key2”.

注意:需要为每个元素指定指针,所以 “UInt *key1,key2” 相当于 “UInt *key1,UInt key2”。如果两个元素都是指针 “UInt *key1,*key2” 必须使用。

要访问指针中的指针,您可以多次指定空键,例如MyStruct["",""] 将获取 MyStruct 地址处的指针。
对于密钥同样有效,例如MyStruct.key["",""]。
s:=Struct("LPTSTR str")
s.str:="Hello World!"
MsgBox %  StrGet(s.str["",""])
Type only structures

Struct 支持所有默认类型(如 Int、Byte、Char...)的仅类型结构。
这些将被创建为数组,因此“char”相当于“char[1]”。要访问此类结构的字段,您始终需要使用 struct.1 或 struct[1] 或 struct["1"]。

u:=Struct("UInt") ; 相当于 UInt[1]
u.1:=10
MsgBox %  u.1
Arrays

支持数组的方式相同。

u:=Struct("UInt[10]")
u.10:=100
MsgBox %  u.10
Pointers

Struct也支持指针。

mystruct:=Struct("*int") ; 与“*int[1]”相同
mystruct.SetCapacity(1,8)
mystruct.1.1:=100
MsgBox %  mystruct.1.1
自定义和默认字段和结构

Struct 支持自定义结构和字段。

POINT:="Int x, Int y"
pt:=Struct("POINT p",,{ p: { x:10, y:20 } } )
MsgBox %  pt.p.x " , " pt.p.y
还支持指针,但是在访问该字段之前,我们必须为其分配内存。
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

内存将在内部管理并在对象被删除时释放。
对于字符串,内存会在之前自动初始化。

s:=Struct("LPTSTR string")
s.string:="Hello World!"
MsgBox %  s.string

可以使用 .GetCapacity() 方法检索分配的内存大小

MsgBox %  s.GetCapacity("string")

每当分配新字符串时,如果新字符串长度不同,则将重新分配内存。
要手动释放内存,我们可以使用.SetCapacity()。

s.SetCapacity("string",0)
MsgBox %  s.GetCapacity("string")

您可以使用 .SetCapacity() 方法手动为字段分配内存。
但是,要保留分配的内存,您必须使用 StrPut("new string",s.string[""]) 写入字符串,否则如果新字符串长度不同,内存将被重新分配。

s:=Struct("LPTSTR string")
s.SetCapacity("string",260)
BIT FIELDS

还支持位字段,请参阅 Bit Fields 了解更多信息。

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
}
枚举结构

使用 for 循环,我们可以枚举结构来检索字段名称及其值。
枚举将以与定义结构相同的顺序执行。

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

数组也可以用同样的方式枚举。

x:=Struct("UInt[10]",[9,8,7,6,5,4,3,2,1,0])
for k, v In x
  MsgBox %  k ": " v

注意! 像“Uint *a”这样未知大小的字段只能枚举到第一个值,而不能枚举到更深的值!
要完整地枚举结构,需要完整地定义它!

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

相关

sizeof, StructTypes

示例

; 创建指针数组
_POINT:="x,y"
pt:=Struct("*_POINT") ; 类似于“*_POINT[1],但我们必须分配内存”
pt.SetCapacity(1,A_PtrSize),pt.SetCapacity(2,A_PtrSize)
pt.1.SetCapacity(1,8),pt.2.SetCapacity(1,8) ; 分配内存
pt.1.1.x:=100, pt.2.1.x:=200
MsgBox %  pt.1.1.x " / " pt.2.1.x
; 更多示例
pt:=Struct("x,y") ; 点结构
pt.x:=100
MsgBox %  pt.x
rc:=Struct("left,top,right,bottom") ; 矩形结构
Gui,New,HWNDhwnd
Gui,Show, w640 h480
DllCall("GetWindowRect","PTR",hwnd,"PTR",rc[])
MsgBox %  "left: " rc.left "`ntop: " rc.top "`nright: " rc.right "`nbottom: " rc.bottom
Gui, Destroy
ExitApp
; 数组示例
;简单的数组结构。
;数组始终使用整数访问
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
; 指针示例
; 简单的指针*
int:=Struct("*UInt")
int.SetCapacity(1,4)
int.1.SetCapacity(1,4)
int.1.1:=100
MsgBox %  int.1.1
; 指向指针数组的指针
s:=Struct("**UInt")
s.SetCapacity(1,8) ; 指向 uint 的指针的 2 数组
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
; 字符串示例
;简单的用户定义结构
user:="UInt Id, LPTSTR Name"
users := Struct("user[2]") ; 结构体数组
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
; 我们也可以使用对象来赋值
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
; 字符数组
String:=Struct("TCHAR char[26]")
Loop 26
 string["char"][A_Index]:=Chr(A_Index+64)
Loop 3
 MsgBox %  String["char"][A_Index*2] ; 显示一些字符
MsgBox %  StrGet(string[],26) ; 获取完整字符串
; 正确的例子
Gui,New,HWNDhwnd
_RECT:="left,top,right,bottom"
RC:=Struct(_RECT) ; 创建结构
Gui,Add,Text,,Press Escape to continue
Gui,Show,w200 h100 ; 显示窗口
DllCall("GetWindowRect","PTR",hwnd,"PTR",rc[]) ; 获取窗口位置
rc.right := rc.right - rc.left ; 将 rc.right 设置为宽度
rc.bottom := rc.bottom - rc.top ; 将 rc.bottom 设置为高度
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
}
ExitApp
; 查找第一个文件示例
_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)

; Process32第一个示例
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
; 列出进程模块示例
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)

  ;  对指定进程中的所有模块创建快照。
  hModuleSnap := DllCall("CreateToolhelp32Snapshot","UInt", TH32CS_SNAPMODULE,"PTR", dwPID )
  if( hModuleSnap = INVALID_HANDLE_VALUE )
  {
    MsgBox %  "CreateToolhelp32Snapshot (of modules)"
    return FALSE
  }

  ; 使用结构之前先设置结构大小。
  me32.dwSize := sizeof("_MODULEENTRY32")

  ;  检索第一个模块的信息,
  ;  如果失败则退出

  if( !DllCall("Module32First" (A_IsUnicode?"W":""),"PTR", hModuleSnap,"PTR", me32[] ) )
  {
    MsgBox %  "Error Module32First`n" ErrorMessage() ;  // 显示失败原因
    DllCall("CloseHandle","PTR", hModuleSnap ) ;     // 必须清理 snapshot 对象!
    return  FALSE
  }

  ;//  现在遍历该进程的模块列表,
  ;//  并显示每个模块的信息
  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
  }

  ;//  不要忘记清理 snapshot 对象。
  DllCall("CloseHandle","PTR",hModuleSnap)
  return TRUE
}
; 枚举一个结构。
; 枚举简单结构
MyStruct:="a,b,c"
s:=Struct(MyStruct,,{a:1,b:2,c:3})
for k, v in s
  MsgBox %  k ": " v
; 枚举结构数组
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
; 枚举动态结构
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