DllCall directly on a struct

Get help with using AutoHotkey and its commands and hotkeys
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

DllCall directly on a struct

30 Dec 2018, 16:27

- I was wondering whether it was possible to apply DllCall directly to a binary struct containing parameter info (instead of specifying parameters and types). The rationale was twofold: it may be useful in some rare circumstances, and, to understand how DllCall/the stack works. (Some of this info is important for understanding ASM.)
- I suppose there would be 3 scenarios, x32 stdcall, x32 cdecl, x64.
- I'm not quite certain of the exact details yet, here's some initial code. So please correct/expand on anything.
- Winapi functions in 32-bit are often quite neat with all parameters Int-sized (4 bytes). So I tried to think of a function containing a Char or Short. Two examples are: comdlg32\GetFileTitle (stdcall) and msvcrt\iswspace (cdecl).
- There are many Winapi functions listed here:
WinApi
https://hotkeyit.github.io/v2/docs/commands/WinApi.htm

Code: Select all

;q:: ;test DllCall based on a struct (x32 stdcall)
vPath := A_ScriptFullPath
VarSetCapacity(vDName, 260 << !!A_IsUnicode, 0)
DllCall("comdlg32\GetFileTitle", Str,vPath, Str,vDName, UShort,260, Short) ;get display name
MsgBox, % vDName

;x32 only
vPath := A_ScriptFullPath
VarSetCapacity(vDName, 260 << !!A_IsUnicode, 0)
VarSetCapacity(vData, vSize:=4+4+2)
NumPut(&vPath, &vData, 0, "UInt")
NumPut(&vDName, &vData, 4, "UInt")
NumPut(260, &vData, 8, "UShort")
oArray := []
;Loop, % vSize
;	oArray.Push("UChar", NumGet(&vData, A_Index-1, "UChar"))
;Loop, % Ceil(vSize / 2)
;	oArray.Push("UShort", NumGet(&vData, A_Index*2-2, "UShort"))
Loop, % Ceil(vSize / 4)
	oArray.Push("UInt", NumGet(&vData, A_Index*4-4, "UInt"))
oArray.Push("Short") ;return type
DllCall("comdlg32\GetFileTitle", oArray*) ;get display name
MsgBox, % vDName
return

;==================================================

;q:: ;test DllCall based on a struct (x64 stdcall)
vPath := A_ScriptFullPath
VarSetCapacity(vDName, 260 << !!A_IsUnicode, 0)
DllCall("comdlg32\GetFileTitle", Str,vPath, Str,vDName, UShort,260, Short) ;get display name
MsgBox, % vDName

;x64 only (not working)
vPath := A_ScriptFullPath
VarSetCapacity(vDName, 260 << !!A_IsUnicode, 0)
VarSetCapacity(vData, vSize:=8+8+2)
NumPut(&vPath, &vData, 0, "UInt64")
NumPut(&vDName, &vData, 8, "UInt64")
NumPut(260, &vData, 16, "UShort")
oArray := []
;Loop, % vSize
;	oArray.Push("UChar", NumGet(&vData, A_Index-1, "UChar"))
;Loop, % Ceil(vSize / 2)
;	oArray.Push("UShort", NumGet(&vData, A_Index*2-2, "UShort"))
;Loop, % Ceil(vSize / 4)
;	oArray.Push("UInt", NumGet(&vData, A_Index*4-4, "UInt"))
Loop, % Ceil(vSize / 8)
	oArray.Push("UInt64", NumGet(&vData, A_Index*8-8, "UInt64"))
oArray.Push("Short") ;return type
DllCall("comdlg32\GetFileTitle", oArray*) ;get display name
MsgBox, % vDName
return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4031
Joined: 17 Jul 2016, 01:02
Contact:

Re: DllCall directly on a struct

04 Jan 2019, 03:35

- I was wondering whether it was possible to apply DllCall directly to a binary struct containing parameter info
No.
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: DllCall directly on a struct

04 Jan 2019, 06:10

- Hello Helgef. Did you see my code above, one of the examples worked.
- The idea is to specify a pointer to a struct (containing all of the input parameter data, and padding where appropriate), the size of a struct, and a return type.
- In x32 you would chop up data into Ints (plus a remainder parameter if needed). And likewise in x64 chop up data into Int64s (plus a remainder parameter if needed).
- Could you mention any concrete examples where such an approach couldn't work? Thanks.
- Previous threads suggest that at least with x32 stdcall this could be possible.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4031
Joined: 17 Jul 2016, 01:02
Contact:

Re: DllCall directly on a struct

04 Jan 2019, 12:01

Did you see my code above, one of the examples worked.
Whatever you did, you didn't pass a pointer to a struct of parameters, causing dllcall to pass its memebers as parameters to the function.
The idea is to specify a pointer to a struct (containing all of the input parameter data), the size of a struct, and a return type.
Dllcall doesn't accept a pointer to the parameters, you must pass each parameter individually and specify its type. If dllcall accepted a pointer to the parameters it wouldn't be a pointer to a struct, it would be a pointer to an array.

Cheers.
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: DllCall directly on a struct

04 Jan 2019, 12:45

All of these examples appear to be working. The question is whether there are situations where this will fail.

Code: Select all

q:: ;test DllCall
vPath := A_ScriptFullPath
VarSetCapacity(vDName, 260 << !!A_IsUnicode, 0)
DllCall("comdlg32\GetFileTitle", Str,vPath, Str,vDName, UShort,260, Short) ;get display name
MsgBox, % vDName

MsgBox, % !!DllCall("msvcrt\iswspace", UShort,32, "Cdecl")

MsgBox, % !!DllCall("msvcrt\iswspace", UShort,33, "Cdecl")
return

w:: ;test DllCall based on a struct
vPath := A_ScriptFullPath
VarSetCapacity(vDName, 260 << !!A_IsUnicode, 0)
if (A_PtrSize = 8)
{
	VarSetCapacity(vData, vSize:=8+8+2)
	NumPut(&vPath, &vData, 0, "UInt64")
	NumPut(&vDName, &vData, 8, "UInt64")
	NumPut(260, &vData, 16, "UShort")
}
else
{
	VarSetCapacity(vData, vSize:=4+4+2)
	NumPut(&vPath, &vData, 0, "UInt")
	NumPut(&vDName, &vData, 4, "UInt")
	NumPut(260, &vData, 8, "UShort")
}
DllCallStruct("comdlg32\GetFileTitle", &vData, vSize, "Short") ;get display name
VarSetCapacity(vDName, -1)
MsgBox, % vDName

VarSetCapacity(vData, vSize:=2)
NumPut(32, &vData, 0, "UShort")
MsgBox, % !!DllCallStruct("msvcrt\iswspace", &vData, vSize, "Cdecl")

VarSetCapacity(vData, vSize:=2)
NumPut(33, &vData, 0, "UShort")
MsgBox, % !!DllCallStruct("msvcrt\iswspace", &vData, vSize, "Cdecl")
return

;==================================================

DllCallStruct(vDllName, vAddr, vSize, vRetType) ;get display name
{
	static oRem := ["UChar", "UShort", "", "UInt"]
	static vNum := A_PtrSize=8?8:4
	static vType := A_PtrSize=8?"UInt64":"UInt"
	oArray := []
	vOffset := 0
	Loop, % Floor(vSize / vNum)
		oArray.Push(vType, NumGet(vAddr+0, vOffset, vType))
		, vOffset += vNum
	if (vTypeLast := oRem[Mod(vSize, vNum)])
		oArray.Push(vTypeLast, NumGet(vAddr+0, vOffset, vTypeLast))
	oArray.Push(vRetType) ;return type
	;MsgBox, % JEE_ObjList(oArray)
	return DllCall(vDllName, oArray*) ;get display name
}

;==================================================
Links (32-bit cdecl and stdcall, see '006 Calling Conventions Part 2' in particular):
005 Calling Conventions Part 1 - YouTube
https://www.youtube.com/watch?v=BM1aIq-SPDU
006 Calling Conventions Part 2 - YouTube
https://www.youtube.com/watch?v=OQ2iBks7Lyo
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4031
Joined: 17 Jul 2016, 01:02
Contact:

Re: DllCall directly on a struct

04 Jan 2019, 13:10

It seems you either changed the subject or didn't describe the situation very well. Still, you must pass address to arrays, not structs. Also, you have a few wrong sizes for your structs it seems.

Return to “Ask For Help”

Who is online

Users browsing this forum: Albireo, Bing [Bot], Google [Bot], Odlanir, pn4265 and 255 guests