ahkstructlib2
ahkstructlib2 is an Include file that I've been working on to hopefully simplify working with structures in AutoHotkey. If anyone has the time to try it out and provide a bit of feedback I'd appreciate it. I'd be interested in hearing any comments, suggestions, bug reports, questions on usage, etc...
** Updated to version 2.02 ** - June 1, 2008
-
Version 2.02 is now available with a few changes added since the beta release.
- Version 1 can be downloaded
here in case anyone still needs the first version that was posted.
- The current version (2.02) can be downloaded
here or the function(s) can be copied and pasted from the code posted below.
Help (Version 2.01) wrote:
Usage:To create a structure, first use the StructCreate function. This will create the structure itself and create AutoHotkey variables that can be used to send/retrieve values from the structure. The variables are named by using the name of the structure, then a
? character, then the name of the variable.
For example, if StructCreate was used to create a
RECT structure with the variables:
Left,
Top,
Right and
BottomCode:
StructCreate("RECT", "Int", "Left", "Int", "Top", "Int", "Right", "Int", "Bottom")
;
; or using the alternate syntax (either is valid with version 2.0)
;
StructCreate("RECT", "Left As Int", "Top As Int", "Right As Int", "Bottom As Int")
The resulting variables would then be:
RECT - The RECT structure created
RECT?Left
RECT?Top
RECT?Right
RECT?BottomTo change the values in the RECT structure, use the Struct@ function. For example, to change the Left value to 100
Code:
Struct@("RECT?Left", "100")
To save making multiple calls if many changes need to be made, change the values of the variables first then use the Struct@ function but specify the name of the structure instead of the variable to change:
Code:
RECT?Left = 100
RECT?Top = 50
RECT?Right = 200
RECT?Bottom = 250
; This will change all values in the structure to the values currently assigned to the variables
Struct@("RECT")
If a structure has been used in DllCall and the contents of the structure has been changed, use the Struct? function to retrieve the new values. Similar to using the Struct@ function, if you specify the name of the structure only then the variables are updated with the new values from the structure or you can specify a specific variable to only overwrite the value of that variable with the value from the structure. For example:
Code:
StructCreate("POINT1", "x as long", "y as long")
DllCall("GetCursorPos", "Str", POINT1)
; get the new values from the structure
Struct?("POINT1")
MsgBox, Mouse X position: %POINT1?x%
MsgBox, Mouse Y position: %POINT1?y%
That's about it

. To help in troubleshooting potential problems when working with structures I also put together an optional function called struct_enum. This function retrieves the current names and values of each part of the structure. For example, to view the values from the example above, you could use:
Code:
; Display values in POINT1 structure
MsgBox, % struct_enum("POINT1", "`=")
; Display values in variables for POINT1 structure
MsgBox, % struct_enum("POINT1", "`=", "V")
Changes/additions since version 1:- Fixed a bug with the "As" syntax that caused a struct to be created with an incorrect size in some cases (fixed in version 2.02)
- An optional, alternate syntax is now supported in the StructCreate function ("StructName", "VarName As Type", "VarName As Type", etc...).
- Added the ability to use Tabs and spaces to align code for readability with the new, optional syntax for StructCreate
- Only 1 variable is now used as a reference Index to cut down on the number of global variables used
- Added the ability to use different variable types
- Added support for a few other names for common variable types: long, dword, word, hwnd, byte
- Changed function names (hopefully easier to remember)
- The Struct? function will return a value when a variable name is specified, so that the function can be used in an expression
- Simplified usage - in general
- ahkstructlib2 now uses the original ExtractInteger and InsertInteger functions from the AHK help file (renamed in case modified versions of these functions are in use)
- Removed the struct_enum function from the ahkstructlib2 library and added an updated version in a separate Debug plugin library (of course you can still combine the two into one file if you'd like - copy, paste)
- many other changes...
Requirements: - AutoHotkey Version 1.0.44.06 or the latest version of AutoHotkey that is available for download
Suggested filename: ahkstructlib2.ahkCode:
; *********************************
; AHK Function Library - Structures
; Version 2.02
; - by Corrupt
; - Updated June 1, 2008
; *********************************
;
; *********************************
; Comments, suggestions, etc... welcome :)
; *********************************
;
; *********************************
; Create a new structure
; *********************************
; Params: "Structure Name", "VarType", "VarName", "Type", "Name", etc...
; - Currently a maximum of 32 variables in a structure is supported
; (to add support for more, add to the list below)
;
; Optional alternate syntax allowed: "Structure Name", "VarName As Type", "VarName As Type", ...
; - mixing of the 2 different syntax styles in the same call is not currently supported
; *********************************
StructCreate(struct_name ,s_type1, s_var1 ,s_type2="", s_var2="" ,s_type3="", s_var3=""
,s_type4="" , s_var4="" ,s_type5="" , s_var5="" ,s_type6="" , s_var6="" ,s_type7="" , s_var7=""
,s_type8="" , s_var8="" ,s_type9="" , s_var9="" ,s_type10="", s_var10="" ,s_type11="", s_var11=""
,s_type12="", s_var12="" ,s_type13="", s_var13="" ,s_type14="", s_var14="" ,s_type15="", s_var15=""
,s_type16="", s_var16="" ,s_type17="", s_var17="" ,s_type18="", s_var18="" ,s_type19="", s_var19=""
,s_type20="", s_var20="" ,s_type21="", s_var21="" ,s_type22="", s_var22="" ,s_type23="", s_var23=""
,s_type24="", s_var24="" ,s_type25="", s_var25="" ,s_type26="", s_var26="" ,s_type27="", s_var27=""
,s_type28="", s_var28="" ,s_type29="", s_var29="" ,s_type30="", s_var30="" ,s_type31="", s_var31=""
,s_type32="", s_var32=""){
Global
Local struct_sizeA, struct_sizeB, struct_temp1, struct_temp2, struct_temp3, struct_temp4
, struct_temp40, struct_temp41, struct_temp42, struct_temp5, struct_temp6, struct_templast
struct_sizeA = 0
If (!A_AhkScriptProcessID) {
Process, Exist
A_AhkScriptProcessID = %ErrorLevel%i
}
struct_temp3 = %struct_name%_%A_AhkScriptProcessID%
%struct_name%= init
%struct_name%=
; Process elements
Loop, 32 {
struct_temp42 := s_type%A_Index%
struct_temp41 := s_var%A_Index%
struct_temp5 = 1
; check for As syntax
StringReplace, struct_temp42, struct_temp42, %A_Tab%, %A_Space%, All
If (InStr(struct_temp42, " as ")) {
StringReplace, struct_temp42, struct_temp42, %A_Space%As%A_Space%, `,
struct_temp4 = %struct_temp42%
StringSplit, struct_temp4, struct_temp4, `,, %A_Space%
struct_temp4 := s_var%A_Index%
struct_temp5 = 2
}
Loop, %struct_temp5%
{
If (A_Index = "2") {
struct_temp41 = %struct_temp4%
StringReplace, struct_temp41, struct_temp41, %A_Tab%, %A_Space%, All
StringReplace, struct_temp41, struct_temp41, %A_Space%As%A_Space%, `,
struct_temp4 = %struct_temp41%
StringSplit, struct_temp4, struct_temp4, `,, %A_Space%
}
; Check Type (u - unsigned, p - pointer)
If (struct_temp42="Int" OR struct_temp42="long")
struct_temp2 = 4
Else If (struct_temp42="UInt" OR struct_temp42="dword" OR struct_temp42="hwnd")
struct_temp2 = 4u
Else If (struct_temp42="Str")
struct_temp2 = 4up
Else If (struct_temp42="Short")
struct_temp2 = 2
Else If (struct_temp42="UShort" OR struct_temp42="word")
struct_temp2 = 2u
Else If (struct_temp42="UChar" OR struct_temp42="byte")
struct_temp2 = 1u
Else If (struct_temp42="Char")
struct_temp2 = 1
Else If (struct_temp42="Int64")
struct_temp2 = 8
Else If (struct_temp42="UInt64")
struct_temp2 = 8u
Else
struct_temp2 = 4p
If struct_temp41=
{
struct_temp6 = 1
break
}
; ** Create variables **
struct_temp1 := "?" . struct_temp41
If (!InStr(struct_temp2, "p"))
%struct_name%%struct_temp1% = 0
Else {
%struct_name%%struct_temp1%=init
%struct_name%%struct_temp1%=
}
; ** Create reference **
%struct_temp3% := %struct_temp3% . struct_temp2 . ":" . struct_temp41 "|"
struct_sizeA += struct_temp2
}
If (struct_temp6)
break
}
VarSetCapacity(%struct_name%, struct_sizeA, 0)
Return, True
}
; *********************************
; Retrieve a value from the structure
; *********************************
; Params: struct_name or struct?varname
; - specifying struct_name will retrieve/update values for all variables in the structure
; - specifying struct?varname will retrieve/update only the variable specified
; *********************************
struct?(s_query)
{
Global
Local struct_sizeA, struct_sizeB, struct_temp1, struct_temp2, struct_temp3, struct_temp4, struct_temp5,
struct_temp6
, struct_temp0, struct_temp00, struct_temp01, struct_temp02
StringSplit, struct_temp, s_query, ?
struct_temp3 = %struct_temp1%_%A_AhkScriptProcessID%
If (!%struct_temp3%) {
VarSetCapacity(%struct_temp3%, 0)
ErrorLevel = Invalid_Struct
Return
}
struct_sizeA = 0
Loop, Parse, %struct_temp3%, |
{
StringSplit, struct_temp0, A_LoopField, :
If struct_temp00 = 0
break
struct_sizeA += struct_temp01
struct_temp4 := !InStr(struct_temp01, "u")
struct_sizeB = %struct_temp01%
EnvAdd, struct_temp01, 0
If (struct_temp02 = struct_temp2 OR struct_temp2 = "") {
struct_temp5 := ExtractIntegerSL(%struct_temp1%, (struct_sizeA - struct_temp01), !InStr(struct_temp01, "u"),
struct_temp01)
If (InStr(struct_sizeB, "p"))
DllCall("lstrcpyA", "Str", %struct_temp1%?%struct_temp02%, "UInt", struct_temp5)
Else
%struct_temp1%?%struct_temp02% = %struct_temp5%
If (struct_temp02 = struct_temp2)
Return, %struct_temp1%?%struct_temp02%
}
}
Return
}
; *********************************
; Send a value to the structure
; *********************************
; Params: struct_name or struct?varname
; - specifying struct_name will send/update values from all variables in the structure
; - specifying struct?varname will send/update only the variable specified
; *********************************
struct@(s_modify, s_value="")
{
Global
Local struct_sizeA, struct_sizeB, struct_temp1, struct_temp2, struct_temp3, struct_temp4, struct_temp0
StringSplit, struct_temp, s_modify, ?
struct_temp3 = %struct_temp1%_%A_AhkScriptProcessID%
If (!%struct_temp3%) {
VarSetCapacity(%struct_temp3%, 0)
ErrorLevel = Invalid_Struct
Return
}
struct_sizeA = 0
Loop, Parse, %struct_temp3%, |
{
Loop, Parse, A_LoopField, :
{
If (A_Index = "1") {
struct_sizeA += A_LoopField
struct_sizeB = %A_LoopField%
struct_temp4 = %A_LoopField%
EnvAdd, struct_sizeB, 0
}
Else {
If (A_LoopField = struct_temp2 OR struct_temp2="") {
If struct_temp2<>
%struct_temp1%?%A_LoopField% = %s_value%
If (InStr(struct_temp4, "p"))
InsertIntegerSL(&%struct_temp1%?%A_LoopField%, %struct_temp1%, (struct_sizeA - struct_sizeB),
struct_sizeB)
Else
InsertIntegerSL(%struct_temp1%?%A_LoopField%, %struct_temp1%, (struct_sizeA - struct_sizeB),
struct_sizeB)
}
}
}
}
Return
}
; *********************************
; Required functions - ExtractInteger, InsertInteger
; - original versions from Version 1.0.44.06 of the AutoHotkey help file
; by Chris Mallett
; // Renamed in case someone is using a modified version of these functions
; // somewhere else in their code
; *********************************
ExtractIntegerSL(ByRef pSource, pOffset = 0, pIsSigned = false, pSize = 4)
; pSource is a string (buffer) whose memory area contains a raw/binary integer at pOffset.
; The caller should pass true for pSigned to interpret the result as signed vs. unsigned.
; pSize is the size of PSource's integer in bytes (e.g. 4 bytes for a DWORD or Int).
; pSource must be ByRef to avoid corruption during the formal-to-actual copying process
; (since pSource might contain valid data beyond its first binary zero).
{
Loop %pSize% ; Build the integer by adding up its bytes.
result += *(&pSource + pOffset + A_Index-1) << 8*(A_Index-1)
if (!pIsSigned OR pSize > 4 OR result < 0x80000000)
return result ; Signed vs. unsigned doesn't matter in these cases.
; Otherwise, convert the value (now known to be 32-bit) to its signed counterpart:
return -(0xFFFFFFFF - result + 1)
}
; *********************************
InsertIntegerSL(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; The caller must ensure that pDest has sufficient capacity. To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
Loop %pSize% ; Copy each byte in the integer into the structure as raw binary data.
DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1)
& 0xFF)
}
; *********************************
Suggested filename: ahkstructlib2_debug.ahkCode:
; *********************************
; Debug plugin
; *********************************
; AHK Function Library - Structures
; Version 2.01
; - by Corrupt
; - July 1, 2006
; *********************************
; *********************************
; Retrieve a list of all variable names and values in a structure
; *********************************
; "StructName", "DelimChar", "V"
; - DelimChar may be character(s) to use to separate variable and value
; - The info for each variable is separated by a `n character
; - This function retrieves the values stored in the structure by default.
; "V" can be specified as the 3rd param to retrieve the values from the
; associated variables instead.
; *********************************
struct_enum(s_query, struct_delim2="", struct_local="")
{
Global
Local struct_sizeA, struct_sizeB, struct_temp1, struct_temp2, struct_temp3, struct_temp4, struct_temp5, struct_temp6
, struct_temp0, struct_temp00, struct_temp01, struct_temp02, struct_temp7, struct_temp8
StringSplit, struct_temp, s_query, ?
struct_temp3 = %struct_temp1%_%A_AhkScriptProcessID%
If (!%struct_temp3%) {
VarSetCapacity(%struct_temp3%, 0)
ErrorLevel = Invalid_Struct
Return
}
If struct_delim2=
struct_delim2 = `=
struct_sizeA = 0
Loop, Parse, %struct_temp3%, |
{
StringSplit, struct_temp0, A_LoopField, :
If struct_temp00 = 0
break
struct_sizeA += struct_temp01
struct_temp4 := !InStr(struct_temp01, "u")
struct_sizeB = %struct_temp01%
EnvAdd, struct_temp01, 0
If struct_local = V
struct_temp5 := %struct_temp1%?%struct_temp02%
Else {
struct_temp5 := ExtractIntegerSL(%struct_temp1%, (struct_sizeA - struct_temp01), struct_temp4, struct_temp01)
If (InStr(struct_sizeB, "p"))
{
struct_temp7 = %struct_temp5%
struct_temp8 := DllCall("lstrlen", "UInt", struct_temp7)
VarSetCapacity(struct_temp5, struct_temp8, 0)
DllCall("RtlMoveMemory", "Str", struct_temp5, "UInt", struct_temp7, "Int", struct_temp8)
}
}
struct_temp6 = %struct_temp6%`n%struct_temp02%%struct_delim2%%struct_temp5%
}
Return, struct_temp6
}
; *********************************