AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

StructParser (for C/C++ structs)
Goto page 1, 2  Next
 
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
Lexikos



Joined: 17 Oct 2006
Posts: 7295
Location: Australia

PostPosted: Thu Jan 17, 2008 12:49 pm    Post subject: StructParser (for C/C++ structs) Reply with quote

Someone recently stated how amazing my understanding of structs is, so I'm posting this to prove them wrong. Laughing

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

Known Issues:
  • Only types defined in ParseField() are supported.
  • Nested structs are not supported, except for RECT and POINT.
  • 1-byte field alignment is assumed. This isn't an issue for most structures defined at MSDN, which have explicit padding where necessary.
  • Bit fields such as BOOL fFocused:1; are not supported.
  • 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.
Code:
; 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.)
Code:
#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:
Code:
typedef struct _WINDOWPLACEMENT {
    UINT length;
    UINT flags;
    UINT showCmd;
    POINT ptMinPosition;
    POINT ptMaxPosition;
    RECT rcNormalPosition;
} WINDOWPLACEMENT;
Example output:
Code:
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 Evil DEVMODE structure.

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


Last edited by Lexikos on Tue Dec 30, 2008 12:38 am; edited 5 times in total
Back to top
View user's profile Send private message Visit poster's website
majkinetor



Joined: 24 May 2006
Posts: 4511
Location: Belgrade

PostPosted: Thu Jan 17, 2008 1:43 pm    Post subject: Reply with quote

ROFLMAO :D

I will definitely use this. When I get high ocasionaly, there is no chance on the world to correctly calcuate struct offsets Cool
Back to top
View user's profile Send private message
polyethene



Joined: 11 Aug 2004
Posts: 5248
Location: UK

PostPosted: Thu Jan 17, 2008 3:47 pm    Post subject: Reply with quote

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 Smile
_________________
GitHubScriptsIronAHK Contact by email not private message.
Back to top
View user's profile Send private message Send e-mail Visit poster's website
Lexikos



Joined: 17 Oct 2006
Posts: 7295
Location: Australia

PostPosted: Fri Jan 18, 2008 3:33 am    Post subject: Reply with quote

Update: StructParser now generates more useful code. Very Happy
Titan wrote:
I would suggest adding a copy to clipboard feature.
I've added a [& Copy] button. Smile
Quote:
If you can, support creating structs would also be an added bonus Smile
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
Code:
VarSetCapacity(struct, size, 0)
instead of
Code:
SIZE := size
Back to top
View user's profile Send private message Visit poster's website
Guest






PostPosted: Mon Jan 21, 2008 3:12 pm    Post subject: Reply with quote

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

Laughing OTOH, what about MENUBARINFO?
Back to top
Lexikos



Joined: 17 Oct 2006
Posts: 7295
Location: Australia

PostPosted: Tue Jan 22, 2008 1:07 am    Post subject: Reply with quote

Anonymous wrote:
Laughing OTOH, what about MENUBARINFO?
Ha! The only problem with MENUBARINFO is this mysterious syntax:
Code:
  BOOL  fBarFocused:1;
  BOOL  fFocused:1;
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.
Back to top
View user's profile Send private message Visit poster's website
Guest






PostPosted: Tue Jan 22, 2008 2:29 am    Post subject: Reply with quote

lexikos wrote:
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.
Back to top
Lexikos



Joined: 17 Oct 2006
Posts: 7295
Location: Australia

PostPosted: Tue Jan 22, 2008 4:26 am    Post subject: Reply with quote

Anonymous wrote:
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.
Back to top
View user's profile Send private message Visit poster's website
heresy



Joined: 11 Mar 2008
Posts: 291

PostPosted: Wed May 14, 2008 11:07 pm    Post subject: Reply with quote

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
Back to top
View user's profile Send private message
Thrawn



Joined: 12 May 2008
Posts: 30
Location: Germany

PostPosted: Thu May 15, 2008 1:05 pm    Post subject: Reply with quote

Dude! Perfect!
Thx a lot.
Back to top
View user's profile Send private message
Azerty



Joined: 19 Dec 2006
Posts: 72
Location: France

PostPosted: Fri Aug 01, 2008 9:23 am    Post subject: Reply with quote

Thanks Lexicos

I went past it until now, but never too late Smile

Good work
_________________
Assembler-coded MCode.ahk ASCII85.ahk library
Back to top
View user's profile Send private message
Drugwash



Joined: 07 Sep 2008
Posts: 921
Location: Ploiesti, RO

PostPosted: Sun Sep 28, 2008 8:27 pm    Post subject: Reply with quote

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. Embarassed

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. Confused

Then, first struct looks like this (as defined in the C header):
Code:
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:
Code:
; 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. Sad

The second struct looks like this:
Code:
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. Crying or Very sad

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 tools by Drugwash
Back to top
View user's profile Send private message Visit poster's website Yahoo Messenger
Lexikos



Joined: 17 Oct 2006
Posts: 7295
Location: Australia

PostPosted: Sun Sep 28, 2008 9:49 pm    Post subject: Reply with quote

Drugwash wrote:
Currently, leaving that field blank will not even throw an error about an unnamed variable. Confused
Why should it?
Quote:
The result should be:
ULong
UInt.
Quote:
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:
Code:
item := NumGet(MUUID_d_ptr+index, 0, "uchar")

Quote:
As you see, the previously defined MUUID struct is placed at the end of this struct and currently your script cannot correctly process it. Crying or Very sad
Instead, you could replace the uuid field with a definition of the MUUID struct.
Quote:
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,
Code:
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.
Back to top
View user's profile Send private message Visit poster's website
Drugwash



Joined: 07 Sep 2008
Posts: 921
Location: Ploiesti, RO

PostPosted: Sun Sep 28, 2008 11:01 pm    Post subject: Reply with quote

Lexikos wrote:
Drugwash wrote:
Currently, leaving that field blank will not even throw an error about an unnamed variable. Confused
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.
Quote:
Quote:
The result should be:
ULong
UInt.
Semantics. The idea was the missing U. Wink
Quote:
Quote:
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:
Code:
item := NumGet(MUUID_d_ptr+index, 0, "uchar")
True. Could this output type be user-configurable, then?
Quote:
Quote:
As you see, the previously defined MUUID struct is placed at the end of this struct and currently your script cannot correctly process it. Crying or Very sad
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.
Quote:
Quote:
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,
Code:
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... Confused I found something about lstrcpy but haven't gotten around putting it to work.
Quote:
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. Sad
_________________
AHK tools by Drugwash
Back to top
View user's profile Send private message Visit poster's website Yahoo Messenger
Lexikos



Joined: 17 Oct 2006
Posts: 7295
Location: Australia

PostPosted: Mon Sep 29, 2008 7:46 am    Post subject: Reply with quote

Drugwash wrote:
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.
Drugwash wrote:
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.
Quote:
True. Could this output type be user-configurable, then?
I don't see why not.
Quote:
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.).
Code:
var = hello
ptr := &var
MsgBox % DllCall("MulDiv","int",ptr,"int",1,"int",1,"str")
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group