Jump to content


Photo

[CLASS] DllStruct (AHK 1.1)


  • Please log in to reply
31 replies to this topic

#1 just me

just me
  • Members
  • 1175 posts

Posted 16 July 2011 - 09:49 AM

I know about HotKeyIt's Struct() script, but remaining with AHK Basic I could not use it, and (to tell the truth) I did (and do) not understand entirely, why it is working, so I never tried.

Things changed with AHK 1.1. Curious I started to play around with the new class syntax and after some times of struggle I got an idea of the new possibilities for scripting with AHK. So this is my attempt to design a class for structures. It does not have any advantages over HotKeyIt's function other than "better" readability. It supports "plain" structures only, it is less complete (but it's easy to comlete the WinTypes object), it may be less comfortable and may have less performance, but someone may find it usefull anyway or may take it as an example of the class syntax:

Download Class_DllStruct.ahk

Sample script:
; ======================================================================================================================
; AHK_L 1.1 +
; ======================================================================================================================
#NoEnv
#Include Class_DllStruct.ahk
#Include APIStructures.ahk
Title := "Structures"
Gui, +LastFound +OwnDialogs
Gui, Margin, 20, 20
Gui, Font, s10
Gui, Add, Text, xm+10, ListView properties:
Gui, Font, s10, Courier New
Gui, Add, ListView, xm y+5 w400 r20 vLV hwndHLV, Structure|Field|Value
Gui, Show, , %Title%

; ======================================================================================================================
; Create structure TIMEZONEINFORMATION for use in proximate DllCall
TZI := New DllStruct(struct_TIMEZONEINFORMATION)
DllCall("Kernel32\GetTimeZoneInformation", "Ptr", TZI.GetPtr())
; Create structure SYSTEMTIME pointing to the field "DaylightDate" of TZI (as one possibility)
SYSTIME := New DllStruct(struct_SYSTEMTIME, TZI.GetPtr("DaylightDate"))
; Get and display the field values using "GetData" method
MsgBox, 0, %Title%, % "DaylightName:`n"
        . TZI.GetData("DaylightName")
        . "`n`nDaylightDate:`n"
        . "Year: " . SYSTIME.GetData("wYear") . "`n"
        . "Month: " . SYSTIME.GetData("wMonth") . "`n"
        . "DoW: " . SYSTIME.GetData("wDayOfWeek") . "`n"
        . "Day: " . SYSTIME.GetData("wDay") . "`n"
        . "Hour: " . SYSTIME.GetData("wHour")
; Free structures
SYSTIME := ""
TZI := ""
; ======================================================================================================================

; ======================================================================================================================
; Create structure RECT with one multiple field and set/get the field values using arrays
RECT := New DllStruct("LONG rcWindow[4 : Left, Top, Right, Bottom]")
RECT.rcWindow := [20, 20, 400, 600]
MsgBox, 0, %Title%, % "RECT.rcwindow[""Left""] is " . RECT.rcwindow["Left"]
Arr_Pos := RECT.rcWindow
MsgBox, 0, %Title%, % "Arr_Pos:`n"
        . "1 (Left) = " . Arr_Pos[1] . "`n"
        . "2 (Top) = " . Arr_Pos[2] . "`n"
        . "3 (Right) = " . Arr_Pos[3] . "`n"
        . "4 (Bottom) = " . Arr_Pos[4]
; Display RECT properties (keys)
Msg := "RECT Properties:`n`n"
For Key In RECT
   Msg .= A_Index . ": Key = " . Key . "`n"
MsgBox, 0, %Title%, %Msg%
; Free structure
RECT := ""
; ======================================================================================================================

; ======================================================================================================================
; Show some of the ListView properties
; Get the font
SendMessage, WM_GETFONT := 0x31, 0, 0, , ahk_id %HLV%
HFONT := ErrorLevel
; Create structure LOGFONT
LOGFONT := New DllStruct(struct_LOGFONT)
; Assign HFONT properties into LOGFONT
DllCall("Gdi32\GetObject", "Ptr", HFONT, "INT", LOGFONT.GetSize(), "Ptr", LOGFONT.GetPtr())
; Get and output field "lfFaceName"
LV_Add("", "LOGFONT", "lfFaceName", LOGFONT.GetData("lfFaceName"))
; Get and output field 1 ("lfHeight")
LV_Add("", "LOGFONT", "lfHeight", LOGFONT.GetData(1))
; Create structure RECT
RECT := New DllStruct(struct_RECT)
; Get the rectangle of the ListView's window into RECT
DllCall("User32\GetWindowRect", "Ptr", HLV, "Ptr", RECT.GetPtr())
; Get and output field "left"
LV_Add("", "RECT", "left", RECT.left)
; Get and output field "top"
LV_Add("", "RECT", "top", RECT.top)
; Get and output field 3 ("right")
LV_Add("", "RECT", "3", RECT.3)
; Get and output field 4 ("bottom")
LV_Add("", "RECT", "4", RECT.4)
Loop, 3
   LV_ModifyCol(A_Index, "AutoHdr")
Return
; Free structures
LOGFONT := ""
RECT := ""
; ======================================================================================================================

GuiClose:
GuiEscape:
Gui, Destroy
ExitApp
APIStructures.ahk (example of some predefined structures):
; ======================================================================================================================
; AHK_L 1.1 +
; ======================================================================================================================
; GDI Bitmap Structures ================================================================================================
; ======================================================================================================================
; BITMAP ---------------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/dd183371%28VS.85%29.aspx
struct_BITMAP := "
(
{;
   LONG   bmType;
   LONG   bmWidth;
   LONG   bmHeight;
   LONG   bmWidthBytes;
   WORD   bmPlanes;
   WORD   bmBitsPixel;
   LPVOID bmBits;
};
)"

; BITMAPFILEHEADER -----------------------------------------------------------------------------------------------------
struct_BITMAPFILEHEADER := "
(
{;
   WORD  bfType;
   DWORD bfSize;
   WORD  bfReserved1;
   WORD  bfReserved2;
   DWORD bfOffBits;
};
)"

; BITMAPINFOHEADER -----------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/dd183376%28VS.85%29.aspx
struct_BITMAPINFOHEADER := "
(
{;
   DWORD biSize;
   LONG  biWidth;
   LONG  biHeight;
   WORD  biPlanes;
   WORD  biBitCount;
   DWORD biCompression;
   DWORD biSizeImage;
   LONG  biXPelsPerMeter;
   LONG  biYPelsPerMeter;
   DWORD biClrUsed;
   DWORD biClrImportant;
};
)"

; BITMAPINFO -----------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/dd183375%28VS.85%29.aspx
; RGBQUAD bmiColors[1] -> DWORD bmiColors[1]
struct_BITMAPINFO := struct_BITMAPINFOHEADER . "DWORD bmiColors[1];"

; BLENDFUNCTION --------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/dd183393%28VS.85%29.aspx
struct_BLENDFUNCTION := "
(
{;
   BYTE BlendOp;
   BYTE BlendFlags;
   BYTE SourceConstantAlpha;
   BYTE AlphaFormat;
};
)"

; RGBQUAD --------------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/dd162938%28VS.85%29.aspx
struct_RGBQUAD := "
(
{;
   BYTE rgbBlue;
   BYTE rgbGreen;
   BYTE rgbRed;
   BYTE rgbReserved;
};
)"

; SIZE -----------------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/dd145106%28VS.85%29.aspx
struct_SIZE := "
(
{;
   LONG cx;
   LONG cy;
};
)"

; ======================================================================================================================
; GDI Font and Text Structures =========================================================================================
; ======================================================================================================================
; LOGFONT --------------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/dd145037%28VS.85%29.aspx
struct_LOGFONT := "
(
{;
   LONG  lfHeight;
   LONG  lfWidth;
   LONG  lfEscapement;
   LONG  lfOrientation;
   LONG  lfWeight;
   BYTE  lfItalic;
   BYTE  lfUnderline;
   BYTE  lfStrikeOut;
   BYTE  lfCharSet;
   BYTE  lfOutPrecision;
   BYTE  lfClipPrecision;
   BYTE  lfQuality;
   BYTE  lfPitchAndFamily;
   TCHAR lfFaceName[32];
};
)"

; ======================================================================================================================
; GDI Rectangle Structures =============================================================================================
; ======================================================================================================================
; RECT -----------------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/dd162897%28VS.85%29.aspx
struct_RECT := "
(
{;
   LONG left;
   LONG top;
   LONG right;
   LONG bottom;
};
)"

; POINT ----------------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/dd162805%28VS.85%29.aspx
struct_POINT := "
(
{;
   LONG x; 
   LONG y;
};
)"

; POINTS ---------------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/dd162808%28VS.85%29.aspx
struct_POINTS := "
(
{;
   SHORT x; 
   SHORT y;
};
)"

; ======================================================================================================================
; System Information Structures ========================================================================================
; ======================================================================================================================
; OSVERSION ------------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/ms724834%28VS.85%29.aspx
struct_OSVERSIONINFO := "
(
{;
   DWORD dwOSVersionInfoSize;
   DWORD dwMajorVersion;
   DWORD dwMinorVersion;
   DWORD dwBuildNumber;
   DWORD dwPlatformId;
   TCHAR szCSDVersion[128];
};
)"

; OSVERSIONEX ----------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/ms724833%28VS.85%29.aspx
struct_OSVERSIONINFOEX := "
(
{;
   DWORD dwOSVersionInfoSize;
   DWORD dwMajorVersion;
   DWORD dwMinorVersion;
   DWORD dwBuildNumber;
   DWORD dwPlatformId;
   TCHAR szCSDVersion[128];
   WORD  wServicePackMajor;
   WORD  wServicePackMinor;
   WORD  wSuiteMask;
   BYTE  wProductType;
   BYTE  wReserved;
};
)"

; ======================================================================================================================
; Time Structures ======================================================================================================
; ======================================================================================================================
; FILETIME -------------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/ms724284%28VS.85%29.aspx
struct_FILETIME := "
(
{;
   DWORD dwLowDateTime;
   DWORD dwHighDateTime;
};
)"

; SYSTEMTIME -----------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/ms724950%28VS.85%29.aspx
struct_SYSTEMTIME := "
(
{;
   WORD wYear;
   WORD wMonth;
   WORD wDayOfWeek;
   WORD wDay;
   WORD wHour;
   WORD wMinute;
   WORD wSecond;
   WORD wMilliseconds;
};
)"

; TIME_ZONE_INFORMATION ------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/ms725481%28VS.85%29.aspx
; StandardDate and DaylightDate are of type SYSTEMTIME -> WORD[8]
struct_TIMEZONEINFORMATION := "
(
{;
   LONG  Bias;
   WCHAR StandardName[32];
   WORD  StandardDate[8];
   LONG  StandardBias;
   WCHAR DaylightName[32];
   WORD  DaylightDate[8];
   LONG  DaylightBias;
};
)"

; ======================================================================================================================
; Window Structures ====================================================================================================
; ======================================================================================================================
; WINDOWINFO -----------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/ms632610%28VS.85%29.aspx
; RECT rcWindow, rcClient -> LONG[4], ATOM atomWindowType -> WORD
struct_WINDOWINFO := "
(
{;
   DWORD cbSize;
   LONG  rcWindow[4];
   LONG  rcClient[4];
   DWORD dwStyle;
   DWORD dwExStyle;
   DWORD dwWindowStatus;
   UINT  cxWindowBorders;
   UINT  cyWindowBorders;
   WORD  atomWindowType;
   WORD  wCreatorVersion;
};
)"

; WINDOWPLACEMENT ------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/ms632611%28VS.85%29.aspx
; POINT ptMinPosition, ptMaxPosition -> LONG[2], RECT rcNormalPosition -> LONG[4] 
struct_WINDOWPLACEMENT := "
(
{;
   UINT length;
   UINT flags;
   UINT showCmd;
   LONG ptMinPosition[2];
   LONG ptMaxPosition[2];
   LONG rcNormalPosition[4];
};
)"

; WINDOWPOS ------------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/ms632612%28VS.85%29.aspx
struct_WINDOWPOS := "
(
{;
   HWND hwnd;
   HWND hwndInsertAfter;
   INT  x;
   INT  y;
   INT  cx;
   INT  cy;
   UINT flags;
};
)"

; ======================================================================================================================
; Notification Structures ==============================================================================================
; ======================================================================================================================
; NMHDR ----------------------------------------------------------------------------------------------------------------
; http://msdn.microsoft.com/en-us/library/windows/desktop/bb775514%28v=vs.85%29.aspx
struct_NMHDR := "
(
{;
   HWND     hwndFrom;
   UINT_PTR idFrom;
   UINT     code;
};
)"

The class script is somewhat tested on WinXP and Vista (x86 / U32) and Win 7 (x64 / U64). It may be buggy, so if you find some bugs, report them here, please.

Have fun!


Update 2011-07-24[*:3h48yj5e]Windows data types are completed (at least I hope so)[*:3h48yj5e]Changed class name to "DllStruct" (some reminiscence of AutoIt)Update 2011-08-01[*:3h48yj5e]Added support for set/get fields with multiple occurrences as a whole passing/as an array[*:3h48yj5e]Changed sample scriptUpdate 2011-08-02[*:3h48yj5e]Fixed bug in Init() method (THX Lexikos)[*:3h48yj5e]Added support for named occurrences (e.g. "LONG rcWindow[4];".[*:3h48yj5e]Changed sample scriptUpdate 2011-08-28[*:3h48yj5e]Editorial revisionUpdate 2011-11-14[*:3h48yj5e]Added basic type alignment (hopefully like the VC/VC++ compilers will do by default)[*:3h48yj5e]Added type of fields which names are preceded with "*" will become "UPTR"Update 2011-11-22[*:3h48yj5e]Added structure padding[*:3h48yj5e]Added option "Align n;" to set the alignment from within the "StructString" parameter.Update 2011-12-07[*:3h48yj5e]Fixed broken structure initialization with NULL.Update 2011-12-09[*:3h48yj5e]Fixed erroneous alignment (hopefully).Update 2012-01-21[*:3h48yj5e]Added optional alignment for embedded structures.[*:3h48yj5e]Added option "pad n;" for explicit padding.[*:3h48yj5e]Changed error handling to provide more useful informations.[*:3h48yj5e]Changed structure parser to ignore linebreaks.[*:3h48yj5e]Changed APIStructures.ahk sample script.

#2 maul.esel

maul.esel
  • Members
  • 790 posts

Posted 16 July 2011 - 02:16 PM

Great! Thank you! I needed some time to get it working, but now it does!

Tip: it would be cool if you could add string handling. I added "LPCWSTR" : "PTR" to WinTypes and assigned a pointer to the string.
I didn't look much in the internals of your class, but maybe you could do this "conversion" in the class.

Thanks again
maul.esel

#3 just me

just me
  • Members
  • 1175 posts

Posted 24 July 2011 - 09:57 AM

*minor update*

#4 just me

just me
  • Members
  • 1175 posts

Posted 01 August 2011 - 07:12 AM

*minor update*

#5 Lexikos

Lexikos
  • Administrators
  • 8856 posts

Posted 01 August 2011 - 12:32 PM

I would like to commend you for your useful, well-written script. I have avoided using a certain other struct wrapper because although it works adequately, it seems like it would be a nightmare for anyone other than the original author to maintain. (There is another reason which I won't go into here.)

If I get time, I will review your script and see if I can offer any suggestions. For now...

Init() seems to assume all structures are 16 bytes:
DllCall("Kernel32\RtlZeroMemory", "Ptr", This._Ptr, "Ptr", [color=red]16[/color])
I suppose it should be This._Size.

#6 just me

just me
  • Members
  • 1175 posts

Posted 02 August 2011 - 04:55 AM

Thank you very much indeed!

Of course you're right, the "16" is most likely a left-over from testing the DllCall with my beloved RECT structures and I've fixed this.

*minor update*

#7 just me

just me
  • Members
  • 1175 posts

Posted 28 August 2011 - 10:17 AM

*minor upate - editorial revision*

#8 just me

just me
  • Members
  • 1175 posts

Posted 14 November 2011 - 12:46 PM

[*:22xz3tqp]Added basic type alignment (hopefully like the VC/VC++ compilers will do by default)


Dear C programmers,

I've read some stuff about structure alignment and padding on MSDN, but I'm not sure if I've got it right. For now I think that /Zp8 or #pragma pack(8) is the default, meaning

The alignment of a member will be on a boundary that is either a multiple of 8 or a multiple of the size of the member, whichever is smaller.

If my understanding is right, would it be useful to have an option like "Pack 4;" for the structure creation?

I've also read something about padding to fill up the structure to end on a special boundary, but didn't understand that. When is this necessary?

#9 Lexikos

Lexikos
  • Administrators
  • 8856 posts

Posted 15 November 2011 - 12:40 AM

If a structure has a packing alignment of n, its total size must be a multiple of n. It's most important when you have a contiguous array of structs - since there may be padding between each struct - but can also be important in other cases. For instance, if some external code copies a struct into a pointer you provide, it may copy the padding as well; if the memory you've allocated doesn't include the padding, memory corruption may occur.

#10 just me

just me
  • Members
  • 1175 posts

Posted 16 November 2011 - 05:16 AM

Thx, Lexikos,

padding the capacity can be done easily. But, does padding affect the size to be stored in the (e.g.) cbSize member of a structure?

Edit: Is a multiple of 8 the right default for both 32 as well as 64 bit environments?

#11 Lexikos

Lexikos
  • Administrators
  • 8856 posts

Posted 16 November 2011 - 11:43 AM

But, does padding affect the size to be stored in the (e.g.) cbSize member of a structure?

If cbSize is supposed to contain the size of the structure, then by definition it must include any padding contained by the structure.

Edit: Is a multiple of 8 the right default for both 32 as well as 64 bit environments?

Read your earlier quote. Consider what "the size of the member" is and how it may (or may not) differ between 32- and 64-bit builds.

#12 just me

just me
  • Members
  • 1175 posts

Posted 22 November 2011 - 01:18 PM

*minor update*

#13 Guests

  • Guests

Posted 06 December 2011 - 05:45 PM

How do I define a structure with a union?

This doesn't seem to be working. Thanks.
;http://msdn.microsoft.com/en-us/library/windows/desktop/bb773352%28v=vs.85%29.aspx
def_NOTIFYICONDATA := "
(LTrim Join
	DWORD cbSize;
	HWND  hWnd;
	UINT  uID;
	UINT  uFlags;
	UINT  uCallbackMessage;
	HICON hIcon;
	TCHAR szTip[64];
	DWORD dwState;
	DWORD dwStateMask;
	TCHAR szInfo[256];
	union {
	UINT uTimeout;
	UINT uVersion;
	};
	TCHAR szInfoTitle[64];
	DWORD dwInfoFlags;
	GUID  guidItem;
	HICON hBalloonIcon;
)"
_NOTIFYICONDATA := new DllStruct(def_NOTIFYICONDATA)
msgbox % _NOTIFYICONDATA.GetPtr()


#14 Guests

  • Guests

Posted 07 December 2011 - 12:38 AM

Having hard time using a rect structure. The script crashes. Am I using it correctly?
#include <Class_DllStruct>
def_Rect := "
(LTrim Join
	LONG left;
	LONG top;
	LONG right;
	LONG bottom;
)"	
_Rect := new DllStruct(def_Rect)	

Run, Notepad,,, OutputVarPID
WinWait Untitled - Notepad  

DllCall("GetWindowRect", Ptr, WinExist("ahk_pid " OutputVarPID), Ptr, &_Rect)  
MsgBox % "Left " . _Rect.Left . " Top " . _Rect.Top
    . " Right " . _Rect.Right . " Bottom " . _Rect.Bottom


#15 Guests

  • Guests

Posted 07 December 2011 - 12:47 AM

&_Rect is the address of the wrapper object. You want the address of the structure, which is _Rect.GetPtr().