Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

StructParser (for C/C++ structs)


  • Please log in to reply
21 replies to this topic
Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Someone recently stated how amazing my understanding of structs is, so I'm posting this to prove them wrong. :lol:

This script parses a C/C++ struct definition and outputs AutoHotkey code to set or get each field in the struct.

Known Issues:
[*:3ijb7m8a]Only types defined in ParseField() are supported.
[*:3ijb7m8a]Nested structs are not supported, except for RECT and POINT.
[*:3ijb7m8a]1-byte field alignment is assumed. This isn't an issue for most structures defined at MSDN, which have explicit padding where necessary.
[*:3ijb7m8a]Bit fields such as BOOL fFocused:1; are not supported.
[*:3ijb7m8a]Not really an issue: for convenience, any type beginning with "LP" is assumed to be a pointer type.If the script encounters a field that it cannot parse, it will prompt the user for the name, size (in bytes) and (optional) NumGet-compatible type of the field.

The generated code assumes the struct's data is stored in an AutoHotkey variable. To operate on a struct by address, the code must be adjusted.
; put value into struct var
NumPut(value, struct, offset, type)
; put value into struct by address (+0 or similar required)
NumPut(value, pointer_to_struct+0, offset, type)
Requires: Anchor (in your function library, or add an #include.)
#NoEnv
SendMode Input
SetWorkingDir %A_ScriptDir%

Gui, +Resize +MinSize250x80

Gui, Margin, 7, 7
Gui, Font,, Courier New
Gui, Add, Edit, R9 W400 vInputText
Gui, Font
Gui, Add, Button, vParseButton gParseStruct Y+2, &Parse
Gui, Add, Button, vCopyButton gParseAndCopy X+0, && &Copy
Gui, Add, Checkbox, vGenGetters Checked X+5 YP+5, &Get
Gui, Add, Checkbox, vGenSetters Checked X+0, &Set
Gui, Add, Checkbox, vGenCtor    Checked X+0, &VarSetCapacity
Gui, Add, Edit, vStructName X+0 YP-4, struct
Gui, Font,, Courier New
Gui, Add, Edit, XM Y+4 R12 W400 vOutputText

GuiControlGet, StructName, Pos
GuiControl, Move, StructName, % "W" 414-StructNameX-7

Gui, Show, W414, Struct Parser

DelimNameAndTypeWithNewline := false

return

ParseAndCopy:
    gosub ParseStruct
    Clipboard := RegExReplace(OutputText, "(?<!`r)`n", "`r`n")
return

ParseStruct:
    Gui, Submit, NoHide
    
    OutputText =
    OutputGetters =
    OutputSetters =
    
    if StructName =
        StructName = struct
    
    ; Remove comments and directives from consideration.
    InputText := RegExReplace(InputText, "`a)//.*|#.*|/\*.*?\*/")
    
    pos := 1
    namespace = ; short string to keep track of unions, etc.
    indent =
    
    indent_amount = 4
    
    offset = 0
    
    ; for union support
    union_offsets = ; (list)
    union_ends = ; (list)
    
    Loop
    {
        i := RegExMatch(InputText, "s).*?({|;|})", t, pos)
        if ! i
            break
        
        ; Move cursor to end of this bit.
        pos := i+StrLen(t)
        
        ; Remove leading whitespace (previous regex excludes trailing space.)
        t := RegExReplace(t, "(?:^\s+)")
        
        if (t1 = "{")
        {   ; Enter block.
            
            ; Mark the beginning of the block.
            if GenGetters
                OutputGetters .= indent . "; " . t . "`n"
            if GenSetters
                OutputSetters .= indent . "; " . t . "`n"

            ; Indent output.
            Loop, %indent_amount%
                indent .= " "
            
            ; Add block info to namespace, if relevant.
            if (RegExMatch(t, "^union(?:\s+(\w*))s*{$")) {
                last_block = u
                StackPush(union_offsets, offset)
                StackPush(union_ends, offset)
            } else
                last_block = .
                
            namespace .= last_block
        }
        else if (t1 = "}")
        {   ; Exit block.

            ; Un-indent output.
            indent := SubStr(indent, 1, -indent_amount)
            
            ; Mark the end of the block.
            if GenGetters
                OutputGetters .= indent . "; }`n"
            if GenSetters
                OutputSetters .= indent . "; }`n"

            if (last_block = "u")
            {   ; End union.
                offset := StackPop(union_ends, offset)
                StackPop(union_offsets)
            }

            ; Remove block info char from namespace.
            if (StrLen(namespace))
            {
                StringTrimRight, namespace, namespace, 1
                ; Update current immediate block.
                last_block := SubStr(namespace, 0) ; 0=last char
            }
            else
                last_block := ""

            ; Omit declarations following a block (e.g. "struct x { int y } declarations;")
            i := RegExMatch(InputText, "s).*?({|;|})", t, pos)
            if (t1 = ";")
                pos := i+StrLen(t)
        }
        else if (t != ";")
        {
            if ! ParseField(t)
                return
        }    

        if (t != "{" && last_block = "u")
        {   ; Determine end of union by its largest item.
            end := StackPop(union_ends)
            if (end="" or offset > end)
                end := offset
            StackPush(union_ends, end)
            ; Reset offset to beginning of union.
            offset := StackPeek(union_offsets, offset)
        }
    }
    if GenCtor
        OutputText .= "VarSetCapacity(" StructName ", " offset ", 0)`n`n"
    if GenGetters
        OutputText .= OutputGetters "`n"
    if GenSetters
        OutputText .= OutputSetters "`n"

;     OutputText .= "`nStdOut( "
;     Loop, Parse, NameList, `,
;     {
;         if (A_Index>1)
;             OutputText = %OutputText% ", %A_LoopField%=" %A_LoopField%
;         else
;             OutputText = %OutputText%"%A_LoopField%=" %A_LoopField%
;     }
;     OutputText .= " )"
    
    GuiControl,, OutputText, %OutputText%
    return
    

AppendField(name, size, type="", length="") ; length=array length
{
    local delim, pos, text, StructName_
    
    if StructName != struct
        StructName_ = %StructName%_
    
    if (name && type)
    {
        if length =
        {
            text = %StructName_%%name%
            if GenGetters
                OutputGetters .= indent text " := NumGet(" StructName ", " offset ", """ type """)`n"
            if GenSetters
                OutputSetters .= indent "NumPut(" text ", " StructName ", " offset ", """ type """)`n"
            NameList .= (NameList ? "," : "") . name
        }
        else
        {
            if GenGetters
            {
                OutputGetters .= indent StructName_ name "[%index%] := NumGet(" StructName ", " offset " + index"
                if size != 1
                    OutputGetters .= "*" size
                OutputGetters .= ", """ type """)"
                    . (DelimNameAndTypeWithNewline ? "`n  " indent : ", ") . StructName_ name "_length := " length "`n"
            }
            if GenSetters
            {
                OutputSetters .= indent "NumPut(" StructName_ name "[%index%], " StructName ", " offset " + index"
                if size != 1
                    OutputSetters .= "*" size
                OutputSetters .= ", """ type """)`n"
            }
        }
    }
    else
    {
        text := indent StructName "_" name "_ptr := &" StructName " + " offset
        if (type)
            text .= (DelimNameAndTypeWithNewline ? "`n  " indent : ", ")
                . StructName "_" name "_type := """ type """"
        if (length)
            text .= (DelimNameAndTypeWithNewline ? "`n  " indent : ", ")
                . StructName "_" name "_length := " length
        if GenGetters
            OutputGetters .= text "`n"
        if GenSetters
            OutputSetters .= text "`n"
    }
    if length
        offset += size*length
    else
        offset += size
}

StackPush(ByRef stack, item)
{
    if stack
        stack .= ","
    stack .= item
}

StackPop(ByRef stack, defval="")
{
    if (pos := InStr(stack, ","))
    {
        item := SubStr(stack, pos+1)
        stack := SubStr(stack, 1, pos-1)
        return item
    }
    else if stack
    {
        item := stack
        stack := ""
        return item
    }
    return defval
}

StackPeek(ByRef stack, defval="")
{
    if (pos := InStr(stack, ","))
        return SubStr(stack, pos+1)
    else if stack
        return stack
    else
        return defval
}

; Parses a field string "type identifier[arraylength];"
; Returns:
;   name
;   size - size, in bytes, of the field.
;   type - NumGet/DllCall-compatible type string.
ParseField(t)
{
    local m, mtype, msep, mname, mlength, userlength
        , typelist, name, size, type
    
    static Types = "Int,UInt,Short,UShort,Char,UChar,Int64,Float,Double"
         , IntSize = 4      , IntTypes = "int,INT,LONG"
         , UIntSize = 4     , UIntTypes = "unsigned int,unsigned long,UINT,ULONG,DWORD,COLORREF,HANDLE,HBITMAP,HBRUSH,HDC,HICON,HISTANCE,HMENU,HWND,ULONG_PTR,LPARAM,WPARAM"
         , ShortSize = 2    , ShortTypes = "short"
         , UShortSize = 2   , UShortTypes = "unsigned short,WORD,ATOM,USHORT,WCHAR"
         , CharSize = 1     , CharTypes = "char"
         , UCharSize = 1    , UCharTypes = "unsigned char,byte,BYTE,UCHAR,TCHAR" ; assume compiled with multi-byte, not unicode...
         , Int64Size = 8    , Int64Types = "int64,LONGLONG,ULONGLONG"
         , FloatSize = 4    , FloatTypes = "FLOAT"
         , DoubleSize = 8   , DoubleTypes = "DOUBLE"
    
    if (RegExMatch(t, "^\s*(?<type>(?:[\w]+[ \t])*[\w]+)(?<sep>[\s\*]+)(?<name>\w+)(?:\[(?<length>\w+)\])?\s*;", m))
    {
        name := mname
        
        if (mtype = "bool") {
            if (mtype=="BOOL") ; BOOL is an alias for int.
                AppendField(name, 4, "Int")
            else ; bool (C++) is 1 byte.
                AppendField(name, 1, "UChar")
            return true
        }
        if (InStr(msep, "*") or SubStr(mtype,1,2)="LP")
        {   ; Add 32-bit pointer to something.
            AppendField(name, 4, "UInt")
            return true
        }
        if ((mtype = "POINTL" or mtype = "POINT") && mlength="")
        {   ; Add individual X and Y fields.
            AppendField(name . "_X", 4, "Int")
            AppendField(name . "_Y", 4, "Int")
            return true
        }
        if (mtype = "RECT" && mlength="")
        {   ; Add individual struct fields.
            AppendField(name . "_left",     4, "Int")
            AppendField(name . "_top",      4, "Int")
            AppendField(name . "_right",    4, "Int")
            AppendField(name . "_bottom",   4, "Int")
            return true
        }
        
        if StartsWith(mtype, "LP") && !EndsWith(mtype, "STR")
        {
            type := "UInt", size := 4
        }
        else
        {   ; Get DllCall-compatible type if possible.
            Loop, Parse, Types, `,
            {
                typelist := %A_LoopField%Types
                if mtype in %typelist%
                {
                    type := A_LoopField
                    size := %A_LoopField%Size
                    break
                }
            }
        }
    
        if mlength
        if mlength is not integer
        {
            if ! RegExMatch(mlength, "\W")
                if DEF_%mlength%
                    mlength := DEF_%mlength%
            
            if mlength is not integer
            {
mlength_TryAgain:
                InputBox, userlength, Constant, Unknown value '%mlength%' used as array length. Please enter the value of '%mlength%'.,,, 150
                if userlength =
                    return
                if userlength is not integer
                    goto mlength_TryAgain
                
                DEF_%mlength% := mlength := userlength
            }
        }
    }
    
    if ! size
    {
        ; Ask the user to enter details for this field.
        size = 4
        type = UInt ; Most common.
        if ! PromptForStuff(t, name, size, type)
            return false
    }
    
    AppendField(name, size, type, mlength)
    return true
}

PromptForStuff(t, ByRef name, ByRef size, ByRef type, message="")
{
    static retval, tname, tsize, ttype
    
    if ! message
        message = Failed to parse field:`n  %t%`n`nPlease enter the necessary details.

    Gui, 2:Default
    Gui, +Owner1
    Gui, +LabelPFS +LastFound
    Gui, Add, Text, W300, %message%
    Gui, Add, Text, , Name:
    Gui, Add, Edit, W300 vtname, %name%
    Gui, Add, Text, , Size:
    Gui, Add, Edit, W300 vtsize, %size%
    Gui, Add, Text, , NumGet-compatible Type:
    Gui, Add, Edit, W300 vttype, %type%
    Gui, Add, Button, gPFSOK Default, OK
    
    retval = 0
    
    Gui, Show,, Parse Error
    Gui, 1:+Disabled
    
    WinWaitClose, % "ahk_id " . WinExist()
    
    name := tname
    size := tsize
    type := ttype
    
    Gui, 1:Default
    Gui, 1:-Disabled
    Gui, 1:Show
    
    Gui, 2:Destroy
    
    return retval

PFSEscape:
    Gui, 2:Cancel
    return
PFSOK:
    Gui, 2:Submit
    retval = 1
    return
}
    

GuiEscape:
GuiClose:
    ExitApp

GuiSize:
;     Anchor("InputText" ,  "w h0.5")
;     , Anchor("ParseButton", "y0.5"), Anchor("GenGetters", "y0.5")
;     , Anchor("GenGetters", "y0.5"), Anchor("GenSetters", "y0.5")
;     , Anchor("GenCtor", "y0.5"), Anchor("StructName", "y0.5 w1")
;     , Anchor("OutputText",  "w h0.5 y0.5")
    ; InputText is rarely used for more than a paste target,
    ; so resize only OutputText vertically.
    Anchor("InputText", "w")
    , Anchor("StructName", "w")
    , Anchor("OutputText", "h w")
    return


; Case-sensitive by default because we're parsing C++ code.
EndsWith(A, B, CaseSensitive=true)
{
    if (StrLen(A) < StrLen(B))
        return false
    
    return CaseSensitive
        ? (SubStr(A, -(StrLen(B)-1)) == B)
        : (SubStr(A, -(StrLen(B)-1)) = B)
}

StartsWith(A, B, CaseSensitive=true)
{
    if (StrLen(A) < StrLen(B))
        return false
    
    return CaseSensitive
        ? (SubStr(A, 1, StrLen(B)) == B)
        : (SubStr(A, 1, StrLen(B)) = B)
}

Spaces(count)
{
    Loop, %count%
        str .= " "
    return str
}
Covered by Lexikos' default copyright license.

Usage:
Copy and paste a C/C++ structure definition into the top field, then click Parse. If a parsing error occurs, you will be prompted to enter the correct details. If all goes well, AutoHotkey code will appear in the bottom field. This code may or may not be directly usable in script.

[& Copy] does the same as [Parse], but also copies the result to the clipboard.

The small edit field in the middle defines the name of the struct variable for the generated code. If not "struct", it will be prepended to the name of each field (with "_" as a delimiter.) For the example output below, this field was set to "wp".

Example input:
typedef struct _WINDOWPLACEMENT {
    UINT length;
    UINT flags;
    UINT showCmd;
    POINT ptMinPosition;
    POINT ptMaxPosition;
    RECT rcNormalPosition;
} WINDOWPLACEMENT;
Example output:
VarSetCapacity(wp, 44, 0)

; typedef struct _WINDOWPLACEMENT {
    wp_length := NumGet(wp, 0, "UInt")
    wp_flags := NumGet(wp, 4, "UInt")
    wp_showCmd := NumGet(wp, 8, "UInt")
    wp_ptMinPosition_X := NumGet(wp, 12, "Int")
    wp_ptMinPosition_Y := NumGet(wp, 16, "Int")
    wp_ptMaxPosition_X := NumGet(wp, 20, "Int")
    wp_ptMaxPosition_Y := NumGet(wp, 24, "Int")
    wp_rcNormalPosition_left := NumGet(wp, 28, "Int")
    wp_rcNormalPosition_top := NumGet(wp, 32, "Int")
    wp_rcNormalPosition_right := NumGet(wp, 36, "Int")
    wp_rcNormalPosition_bottom := NumGet(wp, 40, "Int")
; }

; typedef struct _WINDOWPLACEMENT {
    NumPut(wp_length, wp, 0, "UInt")
    NumPut(wp_flags, wp, 4, "UInt")
    NumPut(wp_showCmd, wp, 8, "UInt")
    NumPut(wp_ptMinPosition_X, wp, 12, "Int")
    NumPut(wp_ptMinPosition_Y, wp, 16, "Int")
    NumPut(wp_ptMaxPosition_X, wp, 20, "Int")
    NumPut(wp_ptMaxPosition_Y, wp, 24, "Int")
    NumPut(wp_rcNormalPosition_left, wp, 28, "Int")
    NumPut(wp_rcNormalPosition_top, wp, 32, "Int")
    NumPut(wp_rcNormalPosition_right, wp, 36, "Int")
    NumPut(wp_rcNormalPosition_bottom, wp, 40, "Int")
; }
Final notes:
For the curious, I wrote this script while trying to work with the :twisted: DEVMODE structure.

I generally only use the code it generates as a reference.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: Jul 29 2016 12:40 AM
  • Joined: 24 May 2006
ROFLMAO :D

I will definitely use this. When I get high ocasionaly, there is no chance on the world to correctly calcuate struct offsets 8)

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
Structs beyond the complexity of a RECT is something I seldom use, but for when such occasions do arise this will definitely be a lot helpful.
I would suggest adding a copy to clipboard feature. If you can, support creating structs would also be an added bonus :)

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Update: StructParser now generates more useful code. :D

I would suggest adding a copy to clipboard feature.

I've added a [& Copy] button. :)

If you can, support creating structs would also be an added bonus :)

I suppose it is most common to store the struct's data in a variable, so creating a struct is simply calling VarSetCapacity. StructParser now generates
VarSetCapacity(struct, [color=green]size[/color], 0)
instead of
SIZE := [color=green]size[/color]


  • Guests
  • Last active:
  • Joined: --

For the curious, I wrote this script while trying to work with the :twisted: DEVMODE structure.

:lol: OTOH, what about MENUBARINFO?

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

:lol: OTOH, what about MENUBARINFO?

Ha! The only problem with MENUBARINFO is this mysterious syntax:
BOOL  fBarFocused[color=red]:1[/color];
  BOOL  fFocused[color=red]:1[/color];
fBarFocused and fFocused are bit fields. I'm not sure how I could generate code to access them, given that NumGet can get a minimum of one byte.

  • Guests
  • Last active:
  • Joined: --

I'm not sure how I could generate code to access them, given that NumGet can get a minimum of one byte.

Although it could be retrieved using BitWise operators, I don't think it's worth the effort since BitFields are rarely found as you said, like archaic structures in DDE. What I was more concerned about was that the script output an (incorrect) size nevertheless, e.g., 36 which should be 32 for MENUBARINFO. Maybe better leave them to the users when BitFileds are involved.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

What I was more concerned about was that the script output an (incorrect) size nevertheless, e.g., 36 which should be 32 for MENUBARINFO.

StructParser will not successfully parse a bitfield. Although the size field of the Parse Error dialog defaults to 4 (edit: since this may give a false impression, it now has no default value), the very presence of the dialog indicates that the results may be incorrect.

heresy
  • Members
  • 291 posts
  • Last active: Sep 26 2008 10:47 PM
  • Joined: 11 Mar 2008
wow Lexikos
why you didn't mention it to me when i was asking you Dllcall thing
this is definitely huge! especially for non-programmers like me
this helper MUST be documented in DllCall page of help file in my opinion
many thanks Lexikos
gratefully
Easy WinAPI - Dive into Windows API World
Benchmark your AutoHotkey skills at PlayAHK.com

Thrawn
  • Members
  • 30 posts
  • Last active: Apr 05 2011 06:07 PM
  • Joined: 12 May 2008
Dude! Perfect!
Thx a lot.

Azerty
  • Members
  • 72 posts
  • Last active: Jan 16 2009 10:08 AM
  • Joined: 19 Dec 2006
Thanks Lexicos

I went past it until now, but never too late :)

Good work
Assembler-coded MCode.ahk ASCII85.ahk library

Drugwash
  • Members
  • 1078 posts
  • Last active: May 24 2016 04:20 PM
  • Joined: 07 Sep 2008
Thank you for this script, Lexicos.

I've skimmed through the code but it's a bit over my head right now and I'd rather not mess with it. :oops:

Problem is, I got a few issues with certain structs.

First issue is about struct naming. The structs I've stumbled upon are all named by the trailing string at the end of the declaration (see MUUID below). I wonder, wouldn't it be better to just automatically parse the original name and use it in the output, as long as no struct name has been input in the edit field?
Currently, leaving that field blank will not even throw an error about an unnamed variable. :?

Then, first struct looks like this (as defined in the C header):
typedef struct _MUUID {
  unsigned long a;
  unsigned short b;
  unsigned short c;
  unsigned char d[8];
} MUUID;
The result should be:
ULong
UShort
UShort
UChar
UChar
UChar
UChar
UChar
UChar
UChar
UChar


however the script's output is:
; typedef struct _MUUID {
    NumPut(MUUID_a, MUUID, 0, "Int")
    NumPut(MUUID_b, MUUID, 4, "Short")
    NumPut(MUUID_c, MUUID, 6, "Short")
    MUUID_d_ptr := &MUUID + 8, MUUID_d_type := "Char", MUUID_d_length := 8
; }
As you can see, types don't match (the unsigned declaration is not considered) and the last 8 chars that could've been separate fields are all defined by a single pointer field. This output would be kinda hard to process when nested structs will be implemented. And I hope it will, because the next struct I need to parse does have the first one nested. :(

The second struct looks like this:
typedef struct {
	int cbSize;
	char *shortName;
	DWORD version;
	char *description;
	char *author;
	char *authorEmail;
	char *copyright;
	char *homepage;
	BYTE flags;
	int replacesDefaultModule;
	MUUID uuid;
} PLUGININFOEX;
As you see, the previously defined MUUID struct is placed at the end of this struct and currently your script cannot correctly process it. :cry:

Finally, related to these structs, I'd need a little bit of help, since I'm not familiar with WinAPI, DllCalls, etc: how do I retrieve a string in AHK from a currently loaded dll, when I only have a pointer to that string (as you see, most values in the PLUGININFOEX struct are pointers to strings)?

Thank you in advance for any help.

(AHK 1.0.48.05 and Win98SE) forever | My scripts are here


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

Currently, leaving that field blank will not even throw an error about an unnamed variable. :?

Why should it?

The result should be:
ULong

UInt.

the last 8 chars that could've been separate fields are all defined by a single pointer field.

What if the array has 100000 elements? It is not practical to generate a field for each element. More code is needed to access a given element of the array. For instance:
item := NumGet(MUUID_d_ptr+index, 0, "uchar")

As you see, the previously defined MUUID struct is placed at the end of this struct and currently your script cannot correctly process it. :cry:

Instead, you could replace the uuid field with a definition of the MUUID struct.

how do I retrieve a string in AHK from a currently loaded dll, when I only have a pointer to that string (as you see, most values in the PLUGININFOEX struct are pointers to strings)?

Use the Str return type. For instance,
str := DllCall("MulDiv","int",ptr,"int",1,"int",1,"str")
MulDiv(x,1,1) returns x unmodified, but AutoHotkey interprets it as a string.


I don't intend to maintain this script. I may rewrite it, but not soon.

Drugwash
  • Members
  • 1078 posts
  • Last active: May 24 2016 04:20 PM
  • Joined: 07 Sep 2008

Currently, leaving that field blank will not even throw an error about an unnamed variable. :?

Why should it?

Because the resulting struct will be unnamed otherwise and any originally unnamed members will look like _a, _b, etc - not so elegant IMHO.

The result should be:
ULong

UInt.

Semantics. The idea was the missing U. :wink:

the last 8 chars that could've been separate fields are all defined by a single pointer field.

What if the array has 100000 elements? It is not practical to generate a field for each element. More code is needed to access a given element of the array. For instance:
item := NumGet(MUUID_d_ptr+index, 0, "uchar")

True. Could this output type be user-configurable, then?

As you see, the previously defined MUUID struct is placed at the end of this struct and currently your script cannot correctly process it. :cry:

Instead, you could replace the uuid field with a definition of the MUUID struct.

That structure is taken directly from the C header, unmodified. The idea would be that at some point, this script could parse such header and retrieve any given struct (nested structs included) without (major) user intervention.

how do I retrieve a string in AHK from a currently loaded dll, when I only have a pointer to that string (as you see, most values in the PLUGININFOEX struct are pointers to strings)?

Use the Str return type. For instance,
str := DllCall("MulDiv","int",ptr,"int",1,"int",1,"str")
MulDiv(x,1,1) returns x unmodified, but AutoHotkey interprets it as a string.

I didn't quite get this one... :? I found something about lstrcpy but haven't gotten around putting it to work.

I don't intend to maintain this script. I may rewrite it, but not soon.

Too bad, would've been of real help. Maybe I'll fiddle with it locally, time and knowledge allowing.
Thank you for replying.

[EDIT]
Calling MulDiv throws an invalid page fault error. :(

(AHK 1.0.48.05 and Win98SE) forever | My scripts are here


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

The idea would be that at some point, this script could parse such header and retrieve any given struct (nested structs included) without (major) user intervention.

StructParser was originally a small tool I hacked up to parse the DEVMODE structure. Rather than waste time adding features to it, I plan to write I proper header-parser. This would parse everything in the header file, allowing a script to get whatever information it needs.

Because the resulting struct will be unnamed otherwise and any originally unnamed members will look like _a, _b, etc - not so elegant IMHO.

If you leave the default name, "struct", no prefix is included in the output variable names (except for arrays, oops). If you specifically delete the name, you get invalid code like NumGet(, 0, "Int").

The name prefix is intended to be the kind of name you'd give a variable, not necessarily the name of the structure. For instance, a POINT structure representing the mouse position could be named mouse_pos, so StructParser would generate mouse_pos_x and mouse_pos_y.

That said, it has occurred to me that deriving the default name from the struct definition would be more intuitive. I do not use the generated variable names, so this was never a priority.

True. Could this output type be user-configurable, then?

I don't see why not.

Calling MulDiv throws an invalid page fault error.

Only if you pass it an invalid pointer or pointer to an invalid string (i.e. one that is not null-terminated.).
var = hello
ptr := &var
MsgBox % DllCall("MulDiv","int",ptr,"int",1,"int",1,"str")