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.
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 BottomStructCreate("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?Bottom
To change the values in the RECT structure, use the Struct@ function. For example, to change the Left value to 100Struct@("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: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: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:; 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.ahk
; ********************************* ; 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.ahk
; ********************************* ; 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 } ; *********************************