Page 1 of 1

jeeswg's DllCall and structs tutorial

Posted: 16 Apr 2019, 18:40
by jeeswg
==================================================

jeeswg's DllCall and structs tutorial

==================================================

> CONTENTS

> INTRO
> DLLCALL: HELLO WORLD
> DLLCALL: INTRODUCING TYPES
> DLLCALL/NUMGET/NUMPUT: CHAR/SHORT/INT/INT64, SIGNED V. UNSIGNED INTEGERS
> VARSETCAPACITY, NUMGET/NUMPUT
> NUMGET/NUMPUT AND ENDIANNESS
> NUMGET/NUMPUT AND SIGNS
> DLLCALL: PTR
> DLLCALL: FLOAT/DOUBLE
> DLLCALL: PARAMETER SIZES OTHER THAN 1/2/4/8 BYTES
> DLLCALL: CDECL V. STDCALL: CALLING CONVENTIONS
> DLLCALL: THE '*' SUFFIX
> DLLCALL: THE 'STR' TYPE
> DLLCALL: STYLE CHOICES
> TIDBITS ON SPECIFIC FUNCTIONS
> STRUCTS: ALIGNMENT (PADDING, OFFSETS)
> STRUCTS: #IF DIRECTIVE
> STRUCTS: UNION
> STRUCTS: BIT FIELDS
> FIND INFO: FUNCTIONS
> FIND INFO: CONSTANTS/ENUMS
> FIND INFO: STRUCT SIZES/OFFSETS
> FIND INFO: TYPES
> LINKS

==================================================

> INTRO

- A dll (dynamic-link library) typically contains code, data or resources (e.g. images).
- Microsoft Windows makes available dlls (e.g. user32.dll) and functions within those dlls (e.g. MessageBox).
- But you can also use external dlls (e.g. AutoHotkeyMini.dll, SciLexer.dll, sqlite3.dll).

- You can call functions:

Code: Select all

;display a MsgBox
DllCall("user32\MessageBox", "Ptr",0, "Str","hello world", "Str","title", "UInt",0)
- Some functions are more complicated to use, because they require the use of structs, e.g. the 16-byte RECT struct.
- A struct is a type of data structure, it consists of n bytes of data.
- A struct is like having multiple variables in one, e.g. a RECT struct contains 4 window coordinates: left/top/right/bottom.

Code: Select all

;get window position
VarSetCapacity(RECT, 16, 0)
WinGet, hWnd, ID, A
DllCall("user32\GetWindowRect", "Ptr",hWnd, "Ptr",&RECT)
vWinX := NumGet(&RECT, 0, "Int")
vWinY := NumGet(&RECT, 4, "Int")
vWinR := NumGet(&RECT, 8, "Int")
vWinB := NumGet(&RECT, 12, "Int")
vWinW := vWinR - vWinX
vWinH := vWinB - vWinY
MsgBox, % Format("x{} y{} w{} h{}", vWinX, vWinY, vWinW, vWinH)
- Key AutoHotkey functions that will be covered: VarSetCapacity, NumGet, NumPut, DllCall.
- Some concepts that will be covered:
- Types: integers (Char/Short/Int/Int64, 1/2/4/8 bytes).
- Types: signedness (signed integers v. unsigned integers).
- Types: endianness (little endian v. big endian).
- Types: floating-point numbers (Float/Double, 4/8 bytes).
- The cdecl and stdcall calling conventions.
- Tidbits on specific functions.
- Structs: padding.
- Structs: bit fields.
- How to translate MSDN pages to AutoHotkey code.
- Machine code functions.

- The main challenge of using DllCall is data types:
- For the GetWindowRect function, we have to know that HWND/LPRECT/BOOL translate to Ptr/Ptr/Int in AutoHotkey.
- For the RECT struct, we have to know that LONG translates to Int in AutoHotkey.
- Some of these types are listed on classic MSDN pages, some can be inferred, some can be found in the header files (.h files) that come with Visual Studio, and some can be reported by using C++ in Visual Studio.

- Another challenge is struct sizes/offsets.
- Structs sometimes have padding (gaps) between members, and/or at the end of the struct.
- E.g. the RECT struct has no padding.
- E.g. the FORMATETC struct has 2 or 10 padding bytes (32-bit or 64-bit) (described in a later section).
- Certain struct members will vary in size depending on whether the exe is 64-bit/32-bit, or Unicode/ANSI.
- Struct sizes/offsets can be calculated by hand, or programmatically by using C++.

- Another challenge is obtaining the values of certain constants.
- E.g. GENERIC_READ and GENERIC_WRITE, used by the function CreateFileW/CreateFileA.
- Some of the values for constants are listed on MSDN pages, some can be found in header files (.h files) that come with Visual Studio.

==================================================

> DLLCALL: HELLO WORLD

- Some examples to illustrate how to use the DllCall function.
- These are shown to give the reader a feel for using DllCall.
- VarSetCapacity / NumGet / NumPut / DllCall will be explained lower down.
- Note the '&' operator which is used to pass the address of a variable (instead of its contents). E.g. vNum := 3, &vNum returns the location of the variable in memory, vNum returns the value 3.
- In other contexts '&' is used as bitwise-and (where numbers are compared using similar logic to AND gates).

- Display a MsgBox.

Code: Select all

q:: ;display a MsgBox

DllCall("user32\MessageBox", "Ptr",0, "Str","hello world", "Str","title", "UInt",0)

;using variables
vPrompt := "hello world 2"
vTitle := "title"
DllCall("user32\MessageBox", "Ptr",0, "Str",vPrompt, "Str",vTitle, "UInt",0)

;using variables, and using Ptr instead of Str
;note: '&' means pass the address of the variable rather than the variable itself
;note: in other situations '&' can be used as bitwise-and (where numbers are compared using similar logic to AND gates)
vPrompt := "hello world 3"
vTitle := "title"
DllCall("user32\MessageBox", "Ptr",0, "Ptr",&vPrompt, "Ptr",&vTitle, "UInt",0)

return
- Get/set the Notepad window position/size (if it exists).

Code: Select all

q:: ;get the Notepad window position/size (if it exists)
WinGet, hWnd, ID, ahk_class Notepad
;hWnd := DllCall("user32\FindWindow", "Str","Notepad", "Ptr",0, "Ptr")
if !hWnd
	return
VarSetCapacity(RECT, 16, 0)
DllCall("user32\GetWindowRect", "Ptr",hWnd, "Ptr",&RECT)
vWinX := NumGet(&RECT, 0, "Int")
vWinY := NumGet(&RECT, 4, "Int")
vWinW := NumGet(&RECT, 8, "Int") - vWinX
vWinH := NumGet(&RECT, 12, "Int") - vWinY
vWinPos := Format("x{} y{} w{} h{}", vWinX, vWinY, vWinW, vWinH)
MsgBox, % vWinPos
return

w:: ;set the Notepad window position/size (if it exists)
WinGet, hWnd, ID, ahk_class Notepad
;hWnd := DllCall("user32\FindWindow", "Str","Notepad", "Ptr",0, "Ptr")
if !hWnd
	return
vWinX := 300, vWinY := 300
vWinW := 300, vWinH := 300
DllCall("user32\MoveWindow", "Ptr",hWnd, "Int",vWinX, "Int",vWinY, "Int",vWinW, "Int",vWinH, "Int",1)
return
For more examples, see:
DllCall() - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/DllCall.htm
AutoHotkey via DllCall: AutoHotkey functions as custom functions - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=37871

==================================================

> DLLCALL: INTRODUCING TYPES

- Some code lines demonstrating the nature of parameters used with DllCall.
- These are shown to give you a feel for the DllCall function and its parameters.

Code: Select all

;parameter descriptions:
;vFunc: the function to call
;vType/vArg: input data: type/argument pairs (e.g. types: Int) (e.g. arguments: numbers/strings)
;vRetType: a return type (this can be omitted if the return type is Int)

;some code lines demonstrating the nature of parameters used with DllCall:
DllCall(vFunc, vRetType)
DllCall(vFunc, vType1,vArg1, vRetType)
DllCall(vFunc, vType1,vArg1, vType2,vArg2, vRetType)
DllCall(vFunc, vType1,vArg1, vType2,vArg2, vType3,vArg3, vRetType)
;note: it is not necessary to group type/argument pairs together,
;but it can make the DllCall lines easier to read/check/correct

;the same lines again, but with the parameters spread out evenly:
DllCall(vFunc, vRetType)
DllCall(vFunc, vType1, vArg1, vRetType)
DllCall(vFunc, vType1, vArg1, vType2, vArg2, vRetType)
DllCall(vFunc, vType1, vArg1, vType2, vArg2, vType3, vArg3, vRetType)

;the MessageBox example from earlier:
DllCall("user32\MessageBox", "Ptr",0, "Str","hello world", "Str","title", "UInt",0)
- Lists of possible types that can be used with DllCall.
- These are shown to introduce the names of the possible types, and give you a feel for the total number of types and how they are grouped.

Code: Select all

Possible types:
Char/UChar (1 byte)
Short/UShort (2 bytes)
Int/UInt (4 bytes)
Int64/UInt64 (8 bytes)
Ptr/UPtr (4 bytes for a 32-bit exe, 8 bytes for a 64-bit exe)
Float (4 bytes)
Double (8 bytes)
[note: each of the above can be prefixed with 'Cdecl ' e.g. 'Cdecl Ptr']
[note: 'Cdecl' by itself is equivalent to 'Cdecl Int']
[note: in AHK 64-bit: Ptr/UPtr are equivalent to Int64/UInt64]
[note: in AHK 32-bit: Ptr/UPtr are equivalent to Int/UInt]

Possible types (further) (all of these are subtle variations of Ptr):
Char*/UChar* [or: CharP/UCharP]
Short*/UShort* [or: ShortP/UShortP]
Int*/UInt* [or: IntP/UIntP]
Int64*/UInt64* [or: Int64P/UInt64P]
Ptr*/UPtr* [or: PtrP/UPtrP]
Float* [or: FloatP]
Double* [or: DoubleP]
Str/AStr/WStr
[note: in AHK Unicode: Str is equivalent to WStr]
[note: in AHK ANSI: Str is equivalent to AStr]
[note: these types are not strictly necessary, we could use Ptr, but add convenience]
- Ideas to be discussed lower down are:
- Basic types: Char/Short/Int/Int64, Float/Double, Ptr.
- The 'U' prefix (referring to unsigned integers). I.e. signed v. unsigned integers.
- The calling conventions: Cdecl and Stdcall. The vast majority of Winapi functions use Stdcall, some use Cdecl.
- The '*' suffix (referring to pointers). (And its equivalent, the 'P' suffix.)
- The 'Str' type (for passing strings).

==================================================

> DLLCALL/NUMGET/NUMPUT: CHAR/SHORT/INT/INT64, SIGNED V. UNSIGNED INTEGERS

- The functions, DllCall, NumGet and NumPut support the following integer types: Char/Short/Int/Int64 (signed) and UChar/UShort/UInt/UInt64 (unsigned).
- Signed integers can be negative, 0 or positive.
- Unsigned integers can be 0 or positive.
- Here are their size ranges:
- Char (1 byte = 8 bits) (-128 to 127 = -0x80 to 0x7F)
- UChar (1 byte = 8 bits) (0 to 255 = 0x0 to 0xFF)
- Short (2 bytes = 16 bits) (-32768 to 32767 = -0x8000 to 0x7FFF)
- UShort (2 bytes = 16 bits) (0 to 65535 = 0x0 to 0xFFFF)
- Int (4 bytes = 32 bits) (-2147483648 to 2147483647 = -0x80000000 to 0x7FFFFFFF)
- UInt (4 bytes = 32 bits) (0 to 4294967295 = 0x0 to 0xFFFFFFFF)
- Int64 (8 bytes = 64 bits) (-9223372036854775808 to 9223372036854775807 = -0x8000000000000000 to 0x7FFFFFFFFFFFFFFF)
- UInt64 (8 bytes = 64 bits) (0 to 18446744073709551615 = 0x0 to 0xFFFFFFFFFFFFFFFF)

- Note: AutoHotkey can't natively store integers as UInt64, they are stored as Int64.
- However, you can use the Format function to display an Int64 as a UInt64.

Code: Select all

;display an Int64 as a UInt64
MsgBox, % Format("0x{:X}", -1) ;0xFFFFFFFFFFFFFFFF
MsgBox, % Format("{:u}", -1) ;18446744073709551615
==================================================

> VARSETCAPACITY, NUMGET/NUMPUT

- VarSetCapacity can be used to create a variable of a certain size in bytes.
- A fill byte can be specified, i.e. each byte is given a particular value (between 0 and 255).
- NumGet can be used to retrieve the value of a byte or group of bytes at a specific address. The group of bytes can have size 1/2/4/8.
- NumPut can be used to set the value of a byte or group of bytes at a specific address. Again, the group of bytes can have size 1/2/4/8.
- Rather than specify the number of bytes, we use one of 4 keywords:
- Char (1 byte), Short (2 bytes), Int (4 bytes), Int64 (8 bytes), or UChar/UShort/UInt/UInt64.

Code: Select all

q:: ;demonstrate VarSetCapacity and NumGet

;create a variable 8 bytes in size:
;note: since we did not specify a 'fill byte', the numbers will be 'random'
;(whatever data happened to be at the location)
;(note: when I tested, it was all zeros for sizes of 126 or below, do not rely on this)
VarSetCapacity(vData, 8)

MsgBox, % NumGet(&vData, 0, "UChar") ;read the 1st byte
MsgBox, % NumGet(&vData, 1, "UChar") ;read the 2nd byte
MsgBox, % NumGet(&vData, 2, "UChar") ;read the 3rd byte

;create a variable 8 bytes in size, where every byte equals 16:
VarSetCapacity(vData, 8, 16)

MsgBox, % NumGet(&vData, 0, "UChar") ;16 ;read the 1st byte
MsgBox, % NumGet(&vData, 1, "UChar") ;16 ;read the 2nd byte
MsgBox, % NumGet(&vData, 2, "UChar") ;16 ;read the 3rd byte

MsgBox, % NumGet(&vData, 0, "UChar") ;16 ;read the 1st byte
MsgBox, % NumGet(&vData, 0, "UShort") ;4112 ;read the 1st 2 bytes
MsgBox, % NumGet(&vData, 0, "UInt") ;269488144 ;read the 1st 4 bytes
MsgBox, % NumGet(&vData, 0, "UInt64") ;1157442765409226768 ;read the 1st 8 bytes

;the numbers are easier to understand if we display them as hex
VarSetCapacity(vData, 8, 16)

vNum1 := NumGet(&vData, 0, "UChar") ;16 ;read the 1st byte
vNum2 := NumGet(&vData, 0, "UShort") ;4112 ;read the 1st 2 bytes
vNum3 := NumGet(&vData, 0, "UInt") ;269488144 ;read the 1st 4 bytes
vNum4 := NumGet(&vData, 0, "UInt64") ;1157442765409226768 ;read the 1st 8 bytes
MsgBox, % Format("0x{:X}", vNum1) ;0x10
MsgBox, % Format("0x{:X}", vNum2) ;0x1010
MsgBox, % Format("0x{:X}", vNum3) ;0x10101010
MsgBox, % Format("0x{:X}", vNum4) ;0x1010101010101010

;we set the value of a byte via NumPut, and confirm it via NumGet
VarSetCapacity(vData, 8)

NumPut(123, &vData, 0, "UChar")
MsgBox, % NumGet(&vData, 0, "UChar") ;123
return
==================================================

> NUMGET/NUMPUT AND ENDIANNESS

- Introducing big endian and little endian:
- In big endian order, the earlier digits represent bigger values e.g. '123' is '1 hundred, 2 tens, 3 units'. If we interpreted '123' in little endian order, it would be '1 unit, 2 tens, 3 hundreds'.
- Little endian order works contrary to what we're used to with numbers, but is very common in IT.
- In IT, we tend to use big endian/little endian, not for digits, but for bytes. E.g. in most of the programs/standards I've dealt with, 4-byte Ints have been stored in little endian order.
- If you write the bytes 'A0 B0 C0 D0' to an address, and use NumGet with Int to retrieve the value, you get 0xD0C0B0A0, the order of the bytes is reversed. This is because NumGet (and NumPut) use little endian order.
- Since using Char(/UChar) with NumGet/NumPut only reads/writes 1 byte, there is no effect, but for Short/Int/Int64(/UShort/UInt/UInt64), the bytes are returned in little endian order.

Code: Select all

;write the bytes 'A0 B0 C0 D0 E0 F0 10 20'
VarSetCapacity(vOutput, 8, 0)
NumPut(0xA0, &vData, 0, "UChar")
NumPut(0xB0, &vData, 1, "UChar")
NumPut(0xC0, &vData, 2, "UChar")
NumPut(0xD0, &vData, 3, "UChar")
NumPut(0xE0, &vData, 4, "UChar")
NumPut(0xF0, &vData, 5, "UChar")
NumPut(0x10, &vData, 6, "UChar")
NumPut(0x20, &vData, 7, "UChar")

;write the bytes 'A0 B0 C0 D0 E0 F0 10 20'
;this is equivalent to the 8 uses of NumPut above
VarSetCapacity(vData, 8, 0)
NumPut(0x2010F0E0D0C0B0A0, &vData, 0, "UInt64")

vNum1 := NumGet(&vData, 0, "UChar")
vNum2 := NumGet(&vData, 0, "UShort")
vNum3 := NumGet(&vData, 0, "UInt")
vNum4 := NumGet(&vData, 0, "UInt64")
MsgBox, % Format("0x{:X}", vNum1) ;A0
MsgBox, % Format("0x{:X}", vNum2) ;B0A0
MsgBox, % Format("0x{:X}", vNum3) ;D0C0B0A0
MsgBox, % Format("0x{:X}", vNum4) ;0x2010F0E0D0C0B0A0

;retrieve the bytes in order:
vOutput := ""
Loop 8
	vNum := NumGet(&vData, A_Index-1, "UChar")
	, vOutput .= Format("{:02X}", vNum)
MsgBox, % vOutput
return
==================================================

> NUMGET/NUMPUT AND SIGNS

- NumGet can retrieve different numbers depending on whether the 'U' prefix is used.
- E.g. if the binary data is 0b11111111, using Char will show -1 and using UChar will show 255.
- This is demonstrated in the example below.

Code: Select all

;we write 255 (0xFF) to 8 bytes
VarSetCapacity(vData, 8, 255)
vNum1 := NumGet(&vData, 0, "Char")
vNum2 := NumGet(&vData, 0, "UChar")
vNum3 := NumGet(&vData, 0, "Short")
vNum4 := NumGet(&vData, 0, "UShort")
vNum5 := NumGet(&vData, 0, "Int")
vNum6 := NumGet(&vData, 0, "UInt")
vNum7 := NumGet(&vData, 0, "Int64")
vNum8 := NumGet(&vData, 0, "UInt64")
MsgBox, % vNum1 " " vNum2 ;-1 255
MsgBox, % vNum3 " " vNum4 ;-1 65535
MsgBox, % vNum5 " " vNum6 ;-1 4294967295
;MsgBox, % vNum7 " " vNum8 ;note: AutoHotkey can't natively handle UInt64, so we use Format below
MsgBox, % vNum7 " " Format("{:u}", vNum8) ;-1 18446744073709551615
return
- Note: using/omitting the 'U' prefix is important when using NumGet, i.e. reading a number.
- However, using/omitting the 'U' prefix with NumPut will make no difference, i.e. when you write data, e.g. for Char/UChar, -1 and 255 will both be stored as binary data 0b11111111.

Code: Select all

;NumPut/NumGet: different values written, same values retrieved
VarSetCapacity(vData1, 1)
VarSetCapacity(vData2, 1)

NumPut(-1, &vData1, 0, "Char")
NumPut(-1, &vData2, 0, "UChar")
NumPut(255, &vData3, 0, "Char")
NumPut(255, &vData4, 0, "UChar")

vOutput1 := vOutput2 := ""
Loop 4
	vOutput1 .= NumGet(&vData%A_Index%, 0, "Char") " "
	, vOutput2 .= NumGet(&vData%A_Index%, 0, "UChar") " "
MsgBox, % vOutput1 "`r`n" vOutput2
==================================================

> DLLCALL: PTR

- In AHK 64-bit: Ptr/UPtr are equivalent to Int64/UInt64.
- In AHK 32-bit: Ptr/UPtr are equivalent to Int/UInt.
- Where Ptr is a pointer-sized signed integer.
- Where UPtr is a pointer-sized unsigned integer.

- As was noted before:
- AutoHotkey can't natively store integers as UInt64, they are stored as Int64.
- However, you can use the Format function to display an Int64 as a UInt64.

==================================================

> DLLCALL: FLOAT/DOUBLE

- Char/Short/Int/Int64, and their unsigned equivalents, UChar/UShort/UInt/UInt64, store integers.
- Float and Double store floating-point numbers, i.e. numbers with an integer part and/or a fractional part e.g. 123, -123.456, 0.456.
- Three components are stored in a Float/Double, a sign bit (0 for positive, 1 for negative), a significand e.g. 1.23456, and an exponent (a positive/0/negative integer) e.g. 2.
- E.g. -1.23456e2 = -1 * 1.23456 * 10**2 = -123.456

- Float (4 bytes = 32 bits) (6 digits of precision) (single-precision floating-point format)
- Double (8 bytes = 64 bits) (15 digits of precision) (double-precision floating-point format)

- Examples of creating floating-point numbers natively in AutoHotkey (a 64-bit double):

Code: Select all

;creating floating-point numbers natively in AutoHotkey

vNum := -123.456 ;without an exponent
MsgBox, % vNum

vNum := -1.23456e2 ;with an exponent
MsgBox, % vNum
MsgBox, % Format("{:.6f}", vNum)

vNum := -123.456e0 ;the 'e0' here is redundant
MsgBox, % vNum
MsgBox, % Format("{:.6f}", vNum)
- Examples of reading/writing a Float/Double:

Code: Select all

;reading/writing a Float/Double
VarSetCapacity(vData, 4, 0)
vNum := 123.0e10
NumPut(vNum, &vData, 0, "Float")
MsgBox, % vNum
MsgBox, % Format("{:0.10f}", vNum)

VarSetCapacity(vData, 8, 0)
vNum := 123.0e10
NumPut(vNum, &vData, 0, "Double")
MsgBox, % vNum
MsgBox, % Format("{:0.10f}", vNum)
The max/min magnitudes that a Float can handle:
(The biggest non-infinity number, and the smallest non-zero number.)
max (normal): approx. 3.402823 * 10**38
min (normal): approx. 1.18 * 10**-38
min (denormal): approx. 1.40 * 10**-45

The max/min magnitudes that a Double can handle:
(The biggest non-infinity number, and the smallest non-zero number.)
max (normal): approx. 1.797693 * 10**308
min (normal): approx. 2.23 * 10**-308
min (denormal): approx. 4.94 * 10**-324

See here for more information about how floats/doubles are stored.
jeeswg's floats and doubles mini-tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=74&t=63650

==================================================

> DLLCALL: PARAMETER SIZES OTHER THAN 1/2/4/8 BYTES

- 99.9% of the time, the functions I have seen accept parameters that are 1/2/4/8 bytes in size.
- If an item (struct) of some other size is needed, a 4/8-byte pointer can be used, to point to that struct.
- I once saw a dll function that specified a RECT (16 bytes) as a direct parameter, rather than the usual pointer to a RECT. See MagSetWindowSource lower down.

==================================================

> DLLCALL: CDECL V. STDCALL: CALLING CONVENTIONS

- For some functions, you need to specify 'Cdecl' in the return type parameter.
- Incorrectly specifying/omitting Cdecl can crash your script.
- (The difference relates to the stack, a place where information is temporarily stored. Stdcall does some tidying up for you, that Cdecl doesn't do.)

- Functions that are typically Stdcall:
- Winapi functions.
- Exceptions: msvcrt functions and user32\wsprintf. (There may be others.)

- Functions that are typically Cdecl:
- The Winapi msvcrt functions.
- (In general, machine code functions and non-Winapi functions that I've seen on the AutoHotkey forum.)
- (E.g. the non-Winapi functions can be from third-party dll files.)

- Some Cdecl/Stdcall examples:

Code: Select all

;some Cdecl/Stdcall examples

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

;4-quadrant atan
;note: Y first, X second
JEE_ATan2(vNumY, vNumX)
{
	local
	return DllCall("msvcrt\atan2", "Double",vNumY, "Double",vNumX, "Cdecl Double")
}

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

;e.g. MsgBox, % JEE_BinCmp(&vData1, &vData2, vSize)

;returns: 0 if same, positive if data 1 is greater, negative if data 2 is greater
JEE_BinCmp(vAddr1, vAddr2, vSize, ByRef vOffset:="")
{
	local
	vOffset := DllCall("ntdll\RtlCompareMemory", "Ptr",vAddr1, "Ptr",vAddr2, "UPtr",vSize, "UPtr")
	if (vOffset = vSize)
		return 0
	return DllCall("msvcrt\memcmp", "Ptr",vAddr1+vOffset, "Ptr",vAddr2+vOffset, "UPtr",1, "Cdecl Int")
}

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

;returns: 0 or positive (number of bytes that match)
JEE_BinCmpAlt(vAddr1, vAddr2, vSize)
{
	return DllCall("ntdll\RtlCompareMemory", "Ptr",vAddr1, "Ptr",vAddr2, "UPtr",vSize, "UPtr")
}

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

;returns vAddrDest
JEE_BinCopy(vAddrDest, vAddrSource, vSize)
{
	return DllCall("msvcrt\memmove", "Ptr",vAddrDest, "Ptr",vAddrSource, "UPtr",vSize, "Cdecl Ptr")
}

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

;no return value
JEE_BinCopyAlt(vAddrDest, vAddrSource, vSize)
{
	DllCall("kernel32\RtlMoveMemory", "Ptr",vAddrDest, "Ptr",vAddrSource, "UPtr",vSize)
}

;==================================================
[more information on how cdecl/stdcall differ]
DllCall: Cdecl - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=37551

==================================================

> DLLCALL: THE '*' SUFFIX

- There are various 'XXX*' types. The 'XXXP' types are equivalent.
- E.g. Int* / UInt* / Ptr* (equivalent to IntP / UIntP / PtrP).
- None of the XXX*/XXXP types are strictly necessary, but are a convenience. You can use Ptr instead of XXX*.
- They typically avoid you having to use VarSetCapacity and NumGet.

- The example below demonstrates the difference between using Ptr and XXX*/XXXP.
- With Ptr, you create a variable with sufficient capacity, pass the address of the variable to DllCall, and use NumGet to retrieve the value.
- With XXX*/XXXP, you pass a variable name (or a value) to DllCall, AutoHotkey will handle the rest for you.
- (Note: if you use XXX*/XXXP and pass a value (rather than a variable name), AutoHotkey will create a temporary variable and pass its address when calling the function. If any data is written to that address, you won't be able to retrieve it.)

Code: Select all

;using Int*:
;	DllCall("kernel32\IsWow64Process", "Ptr",hProc, "Int*",vIsWow64Process)

;using Ptr:
;	VarSetCapacity(vData, 4, 0)
;	DllCall("kernel32\IsWow64Process", "Ptr",hProc, "Ptr",&vData)
;	vIsWow64Process := NumGet(&vData, 0, "Int")

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

q:: ;check if the active window is 64-bit/32-bit (using Int*)
WinGet, vPID, PID, A
if !A_Is64bitOS
	vWinIs64 := 0
else
{
	;PROCESS_QUERY_INFORMATION := 0x400
	hProc := DllCall("kernel32\OpenProcess", "UInt",0x400, "Int",0, "UInt",vPID, "Ptr")
	DllCall("kernel32\IsWow64Process", "Ptr",hProc, "Int*",vIsWow64Process)
	DllCall("kernel32\CloseHandle", "Ptr",hProc)
	vWinIs64 := !vIsWow64Process
}
MsgBox, % "active window is 64-bit? " (vWinIs64 ? "y" : "n")
return

w:: ;check if the active window is 64-bit/32-bit (using Ptr)
WinGet, vPID, PID, A
if !A_Is64bitOS
	vWinIs64 := 0
else
{
	;PROCESS_QUERY_INFORMATION := 0x400
	hProc := DllCall("kernel32\OpenProcess", "UInt",0x400, "Int",0, "UInt",vPID, "Ptr")
	VarSetCapacity(vData, 4, 0)
	DllCall("kernel32\IsWow64Process", "Ptr",hProc, "Ptr",&vData)
	vIsWow64Process := NumGet(&vData, 0, "Int")
	DllCall("kernel32\CloseHandle", "Ptr",hProc)
	vWinIs64 := !vIsWow64Process
}
MsgBox, % "active window is 64-bit? " (vWinIs64 ? "y" : "n")
return
- Whichever of the two approaches you use, you are essentially passing an address.
- The address is usually written to, but sometimes it is both written to and read from.

- Sometimes an address is used to receive output e.g.:
- a handle/pointer
- a byte/character count
- a value e.g. on/off state, width/height, date/time, colour (BGR/RGB)

- Sometimes an address is used both for input and output e.g. with RegEnumValue.

- Here are some example Winapi functions, that feature parameters where the XXX* types can be used:
- (Taken from some of my main scripts/libraries.)

Code: Select all

;Winapi functions that are commonly used with (U)Char*/(U)Short*/(U)Int*/(U)Int64*/(U)Ptr*/Float*/Double*
advapi32\GetTokenInformation
advapi32\LookupPrivilegeValue
advapi32\OpenProcessToken
advapi32\RegEnumKeyEx
advapi32\RegEnumValue
advapi32\RegOpenKeyEx
crypt32\CryptBinaryToString
crypt32\CryptStringToBinary
dwmapi\DwmIsCompositionEnabled
gdiplus\GdipBitmapGetPixel
gdiplus\GdipCreateBitmapFromHBITMAP
gdiplus\GdipCreateBitmapFromStream
gdiplus\GdipCreateHBITMAPFromBitmap
gdiplus\GdipGetImageHeight
gdiplus\GdipGetImageWidth
gdiplus\GdiplusStartup
imagehlp\ImageDirectoryEntryToData
kernel32\FileTimeToLocalFileTime
kernel32\FileTimeToSystemTime
kernel32\GetBinaryType
kernel32\GetDiskFreeSpaceEx
kernel32\GetFullPathName
kernel32\GetProcessTimes
kernel32\GetSystemTimeAsFileTime
kernel32\IsWow64Process
kernel32\LocalFileTimeToFileTime
kernel32\QueryPerformanceCounter
kernel32\QueryPerformanceFrequency
kernel32\ReadFile
kernel32\SetFilePointerEx
kernel32\SystemTimeToFileTime
kernel32\Wow64DisableWow64FsRedirection
kernel32\WriteFile
ole32\CreateStreamOnHGlobal
ole32\StringFromCLSID
oleacc\AccessibleChildren
oleacc\AccessibleObjectFromEvent
oleacc\AccessibleObjectFromPoint
oleacc\AccessibleObjectFromWindow
oleacc\ObjectFromLresult
oleacc\WindowFromAccessibleObject
oleaut32\SafeArrayPtrOfIndex
powrprof\PowerGetActiveScheme
powrprof\PowerReadACValueIndex
powrprof\PowerReadDCValueIndex
propsys\PSGetNameFromPropertyKey
propsys\PSGetPropertyDescriptionByName
psapi\EnumProcesses
shell32\ExtractIconEx
shell32\SHParseDisplayName
shlwapi\AssocQueryString
shlwapi\UrlCreateFromPath
user32\GetWindowThreadProcessId
user32\SetSysColors
version\VerQueryValue
wininet\InternetQueryDataAvailable
wininet\InternetReadFile
winmm\midiOutGetVolume
winmm\midiOutOpen
- (Here is an example of Int64* applied to a variable/number. Looking through my scripts I couldn't find a good example of XXX*/XXXP applied directly to a value (and not a variable), but there may be some good use for it.)

Code: Select all

q:: ;Int64* applied to a variable/number
;convert 1 Jan 2000 (UTC) to local time

;Int64* applied to a variable:
;vIntervals1 := 125911584000000000
;vIntervals2 := 0
;DllCall("kernel32\FileTimeToLocalFileTime", "Int64*",vIntervals1, "Int64*",vIntervals2)

;Int64* applied to a number:
vIntervals2 := 0
DllCall("kernel32\FileTimeToLocalFileTime", "Int64*",125911584000000000, "Int64*",vIntervals2)

VarSetCapacity(SYSTEMTIME2, 16, 0)
DllCall("kernel32\FileTimeToSystemTime", "Int64*",vIntervals2, "Ptr",&SYSTEMTIME2)
vDate := ""
Loop 7
{
	if !(A_Index = 3)
		vDate .= Format("{:02}", NumGet(&SYSTEMTIME2, A_Index*2-2, "UShort"))
}
MsgBox, % vDate
return
==================================================

> DLLCALL: THE 'STR' TYPE

- The Str/AStr/WStr types are similar to the XXX*/XXXP types.
- The three types are not strictly necessary, but are a convenience. You can use Ptr instead.
- They typically avoid you having to assign strings to variables, and having to use VarSetCapacity -1.

- The examples below demonstrate the difference between using Ptr and Str/AStr/WStr.
- With Ptr, you create a variable with sufficient capacity, pass the address of the variable to DllCall, and use VarSetCapacity -1 to update the string length.
- With Str/AStr/WStr, you pass a variable name or literal string to DllCall, AutoHotkey will handle the rest for you. If the variable is going to be written to, the variable must have a sufficient capacity (specified via VarSetCapacity) before using DllCall.
- A key difference between XXX*/XXXP (where XXX is an integer/Double/Float) and Str/AStr/WStr, is that each 'XXX' type has a known size of between 1 and 8 bytes. If your DllCall is going to write a string to the variable that you specify via Str/AStr/WStr, you must prepare a sufficient capacity.

- An example of sending a string.

Code: Select all

q:: ;create a MsgBox (using Str/AStr/WStr)
;note: all 3 of these will work on both AHK ANSI and AHK Unicode

;create a MsgBox (using Str/AStr/WStr directly)
DllCall("user32\MessageBox", "Ptr",0, "Str","prompt", "Str","title", "UInt",0)
DllCall("user32\MessageBoxA", "Ptr",0, "AStr","prompt", "AStr","title", "UInt",0)
DllCall("user32\MessageBoxW", "Ptr",0, "WStr","prompt", "WStr","title", "UInt",0)

;create a MsgBox (using Str/AStr/WStr, passing variables)
vText := "prompt"
vTitle := "prompt"
DllCall("user32\MessageBox", "Ptr",0, "Str",vText, "Str",vTitle, "UInt",0)
DllCall("user32\MessageBoxA", "Ptr",0, "AStr",vText, "AStr",vTitle, "UInt",0)
DllCall("user32\MessageBoxW", "Ptr",0, "WStr",vText, "WStr",vTitle, "UInt",0)
return

w:: ;create a MsgBox (using Ptr)
;note: all 3 of these will work on both AHK ANSI and AHK Unicode

vText := "prompt"
if A_IsUnicode
{
	VarSetCapacity(vTextA, StrLen(vText)+1)
	StrPut(vText, &vTextA, "CP0")
	vTextW := vText
}
else
{
	vTextA := vText
	VarSetCapacity(vTextW, StrLen(vText)*2+2)
	StrPut(vText, &vTextW, "UTF-16")
}

DllCall("user32\MessageBox", "Ptr",0, "Ptr",&vText, "Str","title", "UInt",0)
DllCall("user32\MessageBoxA", "Ptr",0, "Ptr",&vTextA, "AStr","title", "UInt",0)
DllCall("user32\MessageBoxW", "Ptr",0, "Ptr",&vTextW, "WStr","title", "UInt",0)
return
- An example of receiving a string.

Code: Select all

q:: ;get the recent dir

;get the recent dir (using Str)
;CSIDL_RECENT := 8
VarSetCapacity(vDirRecent, 260*2, 0)
DllCall("shell32\SHGetFolderPath", "Ptr",0, "Int",8, "Ptr",0, "UInt",0, "Str",vDirRecent)
MsgBox, % vDirRecent

;get the recent dir (using Ptr and VarSetCapacity -1)
;CSIDL_RECENT := 8
VarSetCapacity(vDirRecent, 260*2, 0)
DllCall("shell32\SHGetFolderPath", "Ptr",0, "Int",8, "Ptr",0, "UInt",0, "Ptr",&vDirRecent)
VarSetCapacity(vDirRecent, -1)
MsgBox, % vDirRecent

;get the recent dir (using Ptr and StrGet)
;CSIDL_RECENT := 8
VarSetCapacity(vDirRecent, 260*2, 0)
DllCall("shell32\SHGetFolderPath", "Ptr",0, "Int",8, "Ptr",0, "UInt",0, "Ptr",&vDirRecent)
MsgBox, % StrGet(&vDirRecent)

return
==================================================

> DLLCALL: STYLE CHOICES

- There are 6 key style choices that relate to DllCall:
- Summarised as: CGKU/.dll/Int/*/"/pairing.
- These are described in more detail below.

Code: Select all

;6 style choices for DllCall

;my preferred choices example:
;DllCall("kernel32\IsWow64Process", "Ptr",hProc, "Int*",vIsWow64Process)
;with all of the alternative options chosen (minus omit extension):
;DllCall("IsWow64Process", Ptr, hProc, IntP, vIsWow64Process, Int)
;with all of the alternative options chosen (minus omit function name):
;DllCall("kernel32.dll\IsWow64Process", Ptr, hProc, IntP, vIsWow64Process, Int)

;function name: omit if possible (comctl32/gdi32/kernel32/user32)/always specify
;note: for these 4 dlls, the dll name can be omitted:
;(I prefer to specify it for easier translation to other languages)
;(also, some users erroneously omit the dll name when it's required, so for debugging it's simpler to always specify it)
;(also, always including it makes it easier to parse/list dll functions used in scripts)
;(note: I tend to use lower-case dll names, for consistency)
DllCall("kernel32\IsWow64Process", "Ptr",hProc, "Int*",vIsWow64Process)
DllCall("IsWow64Process", "Ptr",hProc, "Int*",vIsWow64Process)

;extension: omit/use
DllCall("kernel32\IsWow64Process", "Ptr",hProc, "Int*",vIsWow64Process)
DllCall("kernel32.dll\IsWow64Process", "Ptr",hProc, "Int*",vIsWow64Process)

;return type: omit if possible (Int)/always specify
;(if the return type is omitted, DllCall assumes Int)
DllCall("kernel32\IsWow64Process", "Ptr",hProc, "Int*",vIsWow64Process)
DllCall("kernel32\IsWow64Process", "Ptr",hProc, "Int*",vIsWow64Process, "Int")

;P or *: use P/use *
;(I use * because it is more standard than P)
;(if I omitted double quotes, I would use P, because you can't omit double quotes with *)
;note: AHK v2 requires quotes
DllCall("kernel32\IsWow64Process", "Ptr",hProc, "Int*",vIsWow64Process)
DllCall("kernel32\IsWow64Process", Ptr,hProc, IntP,vIsWow64Process)

;double quotes for types: omit if possible/use
;from the AHK v1 documentation: Note: When specifying an argument type or return type that does not contain a space or asterisk, the quotes around it may be omitted.
;I use double quotes for forwards compatibility with AHK v2
DllCall("kernel32\IsWow64Process", "Ptr",hProc, "Int*",vIsWow64Process)
DllCall("kernel32\IsWow64Process", Ptr,hProc, IntP,vIsWow64Process)

;type/argument pairs: group/don't group
;(I group them for greater readability)
DllCall("kernel32\IsWow64Process", "Ptr",hProc, "Int*",vIsWow64Process)
DllCall("kernel32\IsWow64Process", "Ptr", hProc, "Int*", vIsWow64Process)
- One thing to be aware of is that 'W'/'A' can be omitted from the end of function names:
DllCall() - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/DllCall.htm
If no function can be found by the given name, an A (ANSI) or W (Unicode) suffix is automatically appended based on which version of AutoHotkey is running the script.
- Typically if a function ends in capital 'W'/'A', then it has a partner function ending in the other letter.
- Generally speaking 'FuncW/FuncA' pairs are very common.
- Here are some exceptions e.g. 'FuncW/Func' pairs.

Code: Select all

;functions with W/A equivalents

;note: a trailing W/A is not necessarily a reference to wide (Unicode)/ANSI

;no W equivalent
gdi32\LineDDA
gdiplus\GdipCreateTextureIA
oleaut32\LHashValOfNameSysA
shell32\SHGetDiskFreeSpaceA
shlwapi\StrFormatByteSize64A
user32\CharNextExA
user32\CharPrevExA

;no A equivalent
advapi32\CreateProcessWithLogonW
advapi32\CreateProcessWithTokenW
advapi32\LogonUserExExW
comctl32\AddMRUStringW
comctl32\CreateMRUListW
comctl32\EnumMRUListW
comctl32\Str_SetPtrW
kernel32\FindFirstFileNameTransactedW
kernel32\FindFirstFileNameW
kernel32\FindFirstStreamTransactedW
kernel32\FindFirstStreamW
kernel32\FindNextFileNameW
kernel32\FindNextStreamW
kernel32\GetVolumeInformationByHandleW
kernel32\Module32FirstW
kernel32\Module32NextW
kernel32\Process32FirstW
kernel32\Process32NextW
kernel32\QueryActCtxSettingsW
kernel32\QueryActCtxW
kernel32\ReadDirectoryChangesW
kernel32\SleepConditionVariableSRW
psapi\GetMappedFileNameW
psapi\GetModuleBaseNameW
psapi\GetProcessImageFileNameW
shell32\CommandLineToArgvW
shell32\SHCreateFileExtractIconW
shell32\SHCreateProcessAsUserW
shell32\SHEnumerateUnreadMailAccountsW
shell32\SHExtractIconsW
shell32\SHGetUnreadMailCountW
shell32\SHOpenPropSheetW
shell32\SHSetUnreadMailCountW
shell32\SHStartNetConnectionDialogW
shlwapi\SHRegGetIntW
shlwapi\StrCatChainW
shlwapi\StrCatW
shlwapi\StrChrNIW
shlwapi\StrChrNW
shlwapi\StrCmpIW
shlwapi\StrCmpLogicalW
shlwapi\StrCmpW
shlwapi\StrCpyNW
shlwapi\StrCpyW
shlwapi\StrStrNIW
shlwapi\StrStrNW
shlwapi\UrlFixupW
version\GetFileVersionInfoExW
version\GetFileVersionInfoSizeExW
wininet\PrivacyGetZonePreferenceW
wininet\PrivacySetZonePreferenceW
ws2_32\FreeAddrInfoExW
ws2_32\FreeAddrInfoW
ws2_32\GetAddrInfoW
ws2_32\GetNameInfoW
ws2_32\InetNtopW
ws2_32\InetPtonW

;some other pairs:
msvcrt\_splitpath, msvcrt\_wsplitpath
msvcrt\_strrev, msvcrt\_wcsrev
msvcrt\isspace, msvcrt\iswspace
msvcrt\sprintf, user32\wsprintf [note: user32]
msvcrt\strlen, msvcrt\wcslen
msvcrt\strstr, msvcrt\wcsstr

- It can be useful to know where a dll is located e.g. gdiplus.dll.
[dll get path]
list of dll functions with parameter types for DllCall - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=74&t=30832&p=167332#p167332

- It can be useful to see the list of functions for a dll.
[dll get functions]
DllListExports() - List of Function exports of a DLL - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=4563

- Winapi functions are typically UpperCamelCase. The main exceptions are functions in msvcrt.dll which often use lower-case names.

- This link lists many common Winapi functions and parameter info for each one.
WinApi
https://hotkeyit.github.io/v2/docs/commands/WinApi.htm
- It lists functions from the following dlls:
advapi32, comctl32, comdlg32, crypt32, gdi32, gdiplus, glu32, hid, kernel32, ole32, oleacc, oleaut32, opengl32, rasapi32, rasdlg, rasman, shell32, shlwapi, tapi32, user32, userenv, uxtheme, version, winhttp, wininet, winmm, ws2_32
- Some other commons dlls:
atl, bcrypt, dwmapi, getuname, imagehlp, iphlpapi, magnification, msvcrt, ntdll, powrprof, propsys, psapi, secur32

==================================================

> TIDBITS ON SPECIFIC FUNCTIONS

- For reference, here's an example of a standard MSDN page for a function:
MessageBox function | Microsoft Docs
https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-messagebox

- gdiplus.dll functions:
- When investigating dlls and functions, the following 2 folders are good sources for dll files:
C:\Windows\System32
C:\Windows\SysWOW64
- When I tested on Windows 7, I noticed that gdiplus.dll was not there.
- See 'dll get path' above to find the path.
- Also, unlike other Winapi functions, the gdiplus functions don't have individual MSDN pages, but are instead grouped together. E.g.:
Bitmap Functions | Microsoft Docs
https://docs.microsoft.com/en-us/windows/desktop/gdiplus/-gdiplus-bitmap-flat
Text Functions | Microsoft Docs
https://docs.microsoft.com/en-us/windows/desktop/gdiplus/-gdiplus-text-flat

- GetProcAddress (kernel32.dll):
- The second type must be AStr, and not Str.

Code: Select all

;load dll example
if !hModuleHH
{
	hModuleHH := DllCall("kernel32\LoadLibrary", "Str","HHCTRL.OCX", "Ptr")
	pHtmlHelp := DllCall("kernel32\GetProcAddress", "Ptr",hModuleHH, "AStr","HtmlHelp" (A_IsUnicode?"W":"A"), "Ptr")
}

- 'LongPtr' functions (user32.dll):
- GetClassLong/SetClassLong and GetWindowLong/SetWindowLong each return a Long (Int).
- GetClassLongPtr/SetClassLongPtr and GetWindowLongPtr/SetWindowLongPtr each return a Ptr.
- The XXXLong functions are not reliable on 64-bit AutoHotkey, since any 64-bit pointers would be truncated to 32 bits.
- The XXXLongPtr functions are not available to 32-bit AutoHotkey, since they do not exist on the 32-bit version of user32.dll.
- Thus, depending on the bitness of AutoHotkey, you should use the 'LongPtr' or 'Long' function as appropriate. There is an example below.

Code: Select all

;GetWindowLongPtr (Ptr) v. GetWindowLong (Int)

WinGet, hWnd, ID, A

;64-bit and 32-bit compatible (one-liner)
MsgBox, % vWinStyle := DllCall("user32\GetWindowLong" (A_PtrSize=8?"Ptr":""), "Ptr",hWnd, "Int",-16, "Ptr") ;GWL_STYLE := -16

;64-bit and 32-bit compatible (multi-liner)
if (A_PtrSize = 8) ;64-bit only
	MsgBox, % vWinStyle := DllCall("user32\GetWindowLongPtr", "Ptr",hWnd, "Int",-16, "Ptr") ;GWL_STYLE := -16
else ;32-bit only
	MsgBox, % vWinStyle := DllCall("user32\GetWindowLong", "Ptr",hWnd, "Int",-16, "Ptr") ;GWL_STYLE := -16

- msvcrt.dll functions:
- Unlike functions in virtually all the other Winapi dlls (that I've come across), these functions require Cdecl.
- Also, the msvcrt functions do not have standard MSDN pages, unlike other Winapi functions. E.g. atan2.

- ntdll.dll functions:
- ntdll.dll has various useful undocumented functions e.g.:
ntdll\NtQueryTimerResolution
ntdll\NtReadVirtualMemory
ntdll\NtWow64QueryInformationProcess64 [32-bit dll only]
ntdll\NtWow64ReadVirtualMemory64 [32-bit dll only]
ntdll\RtlComputeCrc32
ntdll\ZwDelayExecution
- See 'dll get functions' to list the functions in a dll.

- 'Point' functions:
- E.g. AccessibleObjectFromPoint (oleacc.dll), ChildWindowFromPoint/WindowFromPoint (user32.dll).
- These accept an 8-byte value composed of 2 4-byte coordinates.
- The code for this can be fiddly, although it could be made easier by a MakeUInt64 function.

Code: Select all

;combine UInts/Ints to make a UInt64

q:: ;test MakeUInt64 function
vPosX := A_ScreenWidth/2
vPosY := A_ScreenHeight/2
hWnd1 := DllCall("user32\WindowFromPoint", "UInt64",(vPosX&0xFFFFFFFF)|(vPosY<<32), "Ptr")
hWnd2 := DllCall("user32\WindowFromPoint", "UInt64",MakeUInt64(vPosX,vPosY), "Ptr")
WinGetClass, vWinClass1, % "ahk_id " hWnd1
WinGetClass, vWinClass2, % "ahk_id " hWnd2
MsgBox, % vWinClass1 "`r`n" vWinClass2
return

;where vLow and vHigh are both Int (4+4=8)
;note: will return an Int64 since AHK does not handle UInt64 natively
MakeUInt64(vLow, vHigh)
{
	return (vLow&0xFFFFFFFF) | ((vHigh&0xFFFFFFFF)<<32)
}

- wsprintf (user32.dll):
- wsprintf is unusual since it is a Winapi function that uses Cdecl. (Other exceptions are the msvcrt.dll functions. There may be other exceptions.)
- Perhaps this is because it can accept an unlimited number of parameters.
wsprintfA function | Microsoft Docs
https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-wsprintfa

Code: Select all

q:: ;test wsprintf/sprintf
VarSetCapacity(vOutput, 12*(A_IsUnicode?2:1), 0)
DllCall(A_IsUnicode?"user32\wsprintf":"msvcrt\sprintf", "Str",vOutput, "Str","%s %s %s", "Str","abc", "Str","def", "Str","ghi")
VarSetCapacity(vOutput, -1)
MsgBox, % vOutput
return

- MagSetWindowSource (magnification.dll):
- MagSetWindowSource specifies a RECT (16 bytes) as a direct parameter, rather than the usual pointer to a RECT.
- For 32-bit dll calls, the data of the RECT is split across multiple DllCall parameters.
- For 64-bit dll calls, a pointer to the RECT is used.
- There is more info here:
Magnification API - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=31748
Passing structure to function - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=60442&p=255359#p255359

- PostMessage/SendMessage (user32.dll):
- AutoHotkey has built-in PostMessage/SendMessage commands/functions.
- Window messages are used for all sorts of purposes, including getting/setting GUI control states/text, one common message is WM_COMMAND (0x111), used to invoke context menu items.
List of Windows Messages - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=74&t=39218
GUIs via DllCall: text functions (get/set internal/external control text) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=40514
Get Info from Context Menu (x64/x32 compatible) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=31971

==================================================

> STRUCTS: ALIGNMENT (PADDING, OFFSETS)

- Sometimes DllCall requires that we pass a pointer to a struct, and so we must prepare the struct.
- We obtain a definition for the struct.
- We look at the list of members, obtain the size of each member, and add them up, and we prepare a variable of the appropriate size.
- However, there are some rules regarding padding. Possible gaps between parameters, and a possible gap at the end of the struct. The size of a struct includes these padding bytes.

- The basic rules are the same for both 64-bit (x64) and 32-bit (x32).
- An n-byte parameter must start at an n-byte offset.
- The overall size of the struct must be divisible by the size of the biggest parameter.
- Note: because of these alignment rules you can get a fair number of gaps in a struct, including at the end of the struct.
- Note: parameters are 1/2/4/8 bytes in size.
- Note: some items are made of smaller items, e.g. a RECT has size 16, but is treated as 4 consecutive parameters (4-byte Ints). When a RECT is inside another struct, it starts at an offset that is a multiple of 4 bytes, and the size of a struct that contains a RECT must be a multiple of 4 bytes.

- More specifically:
- Shorts/UShorts start at offsets divisible by 2. The size of a struct containing a (U)Short, will be a multiple of 2.
- Ints/UInts start at offsets divisible by 4. The size of a struct containing a (U)Int, will be a multiple of 4.
- Int64s/UInt64s start at offsets divisible by 8. The size of a struct containing a (U)Int64, will be a multiple of 8.
- In x32, Ptrs/UPtrs have size 4, so start at offsets divisible by 4. The size of a struct containing a 4-byte (U)Ptr, will be a multiple of 4.
- In x64, Ptrs/UPtrs have size 8, so start at offsets divisible by 8. The size of a struct containing an 8-byte (U)Ptr, will be a multiple of 8.

- Notes:
- Asterisk. If an asterisk is present in a struct definition, then that normally means the parameter is a Ptr type. (You can also have **, a pointer to a pointer.)
- Square brackets. A parameter can have a value in square brackets, e.g. '[3]' means that that parameter appears 3 times, e.g. '[MAX_PATH]', where MAX_PATH = 260, means that that parameter appears 260 times, you get an 'array' of parameters.

- An example of struct alignment:

Code: Select all

e.g. MEMORY_BASIC_INFORMATION
for a 64-bit application (x64), its size is 48 bytes
for a 32-bit application (x32), its size is 28 bytes

typedef struct _MEMORY_BASIC_INFORMATION {
  PVOID  BaseAddress;
  PVOID  AllocationBase;
  DWORD  AllocationProtect;
  SIZE_T RegionSize;
  DWORD  State;
  DWORD  Protect;
  DWORD  Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

first we get the parameter types:
PVOID: Ptr
DWORD: UInt
SIZE_T: UPtr
so: Ptr, Ptr, UInt, UPtr, UInt, UInt, UInt

then we get the parameter sizes:
for x64: 8, 8, 4, 8, 4, 4, 4 (sum: 40)
for x32: 4, 4, 4, 4, 4, 4, 4 (sum: 28)

however for both x64 and x32 applications:
an n-byte parameter must start an n-byte offset,
i.e. at an offset that is a multiple of n,
we add padding to the x64 struct:
for x64: 8, 8, 4(+4), 8, 4, 4, 4 (sum: 44)
for x32: 4, 4, 4,     4, 4, 4, 4 (sum: 28)

also, the size of a structure, must be a multiple
of the size of the largest parameter,
we add padding at the end of the x64 struct:
for x64: 8, 8, 4(+4), 8, 4, 4, 4(+4) (sum: 48)
for x32: 4, 4, 4,     4, 4, 4, 4     (sum: 28)

parameter offsets:
for x64: 0, 8, 16, 24, 32, 36, 40 (sum: 48)
for x32: 0, 4, 8,  12, 16, 20, 24 (sum: 28)

note: the two rules about multiples only apply to raw parameters
of size: 1, 2, 4, 8 bytes

- Sometimes different packing rules are used. E.g. resulting in no gaps.

==================================================

> STRUCTS: #IF DIRECTIVE

- Note: this is different from the AutoHotkey #If directive. The AutoHotkey #If directive specifies window criteria, for the whether the hotkeys underneath it should execute or not.
- In C++, the #if directive determines whether certain code will be used in the final exe. I.e. if a condition is not met, it is as though that code is commented out.
- Some structs will differ depending on an #if directive, e.g. based on the Windows version number, here is an example from the old MSDN page for the LVITEM struct:

Code: Select all

//LVITEM structure
//https://web.archive.org/web/20110925062850/http://msdn.microsoft.com:80/en-us/library/windows/desktop/bb774760(v=vs.85).aspx

typedef struct {
  UINT   mask;
  int    iItem;
  int    iSubItem;
  UINT   state;
  UINT   stateMask;
  LPTSTR pszText;
  int    cchTextMax;
  int    iImage;
  LPARAM lParam;
#if (_WIN32_IE >= 0x0300)
  int    iIndent;
#endif
#if (_WIN32_WINNT >= 0x0501)
  int    iGroupId;
  UINT   cColumns;
  UINT   puColumns;
#endif
#if (_WIN32_WINNT >= 0x0600)
  int    piColFmt;
  int    iGroup;
#endif
} LVITEM, *LPLVITEM;
==================================================

> STRUCTS: UNION

- Here is a definition of a union.
Unions | Microsoft Docs
https://docs.microsoft.com/en-us/cpp/cpp/unions?view=vs-2017
A union is a user-defined type in which all members share the same memory location. This means that at any given time a union can contain no more than one object from its list of members. It also means that no matter how many members a union has, it always uses only enough memory to store the largest member.
- So a union is a bit like a drop-down list, you choose one of the possible items.
- The size of the union will be the size of its largest member.
- The parameter offset will be based on the member with the highest alignment requirements.

- There is more info here:
structs: unions - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=60931

- An example:

Code: Select all

typedef struct _OVERLAPPED {
  ULONG_PTR Internal;
  ULONG_PTR InternalHigh;
  union {
    struct {
      DWORD Offset;
      DWORD OffsetHigh;
    } DUMMYSTRUCTNAME;
    PVOID Pointer;
  } DUMMYUNIONNAME;
  HANDLE    hEvent;
} OVERLAPPED, *LPOVERLAPPED;

in x64: the size of the union is Max(4+4, 8) = 8
in x32: the size of the union is Max(4+4, 4) = 8
in x64: since the union could contain an 8-byte parameter, it must start at an 8-byte offset
in x32: since the union could contain an 4-byte parameter, it must start at a 4-byte offset
in x64: since the struct could contain an 8-byte parameter, its size must be a multiple of 8 bytes
in x32: since the struct could contain a 4-byte parameter, its size must be a multiple of 4 bytes

size (x64): 8 + 8 + Max(4+4,8) + 8 = 32
size (x32): 4 + 4 + Max(4+4,4) + 4 = 20
- Another example:

Code: Select all

typedef struct _STRRET {
  UINT  uType;
  union {
    LPWSTR pOleStr;
    UINT   uOffset;
    CHAR   cStr[MAX_PATH];
  };
} STRRET, *LPSTRRET;

in x64: the size of the union is Max(8,4,260) = 260
in x32: the size of the union is Max(4,4,260) = 260
in x64: since the union could contain an 8-byte parameter, it must start at an 8-byte offset
in x32: since the union could contain a 4-byte parameter, it must start at a 4-byte offset
in x64: since the struct could contain an 8-byte parameter, its size must be a multiple of 8 bytes
in x32: since the struct could contain a 4-byte parameter, its size must be a multiple of 4 bytes

size (x64): 4+(4) + Max(8,4,260)+(4) = 272
size (x32): 4 + Max(4,4,260) = 264
==================================================

> STRUCTS: BIT FIELDS

- Usually we are only concerned about raw parameters (of size 1/2/4/8 bytes) or structs.
- But occasionally information is specified about the individual bits of a parameter.

- An example of bit fields:

Code: Select all

e.g. MENUBARINFO
for a 64-bit application (x64), its size is 48 bytes
for a 32-bit application (x32), its size is 32 bytes

typedef struct tagMENUBARINFO {
  DWORD cbSize;
  RECT  rcBar;
  HMENU hMenu;
  HWND  hwndMenu;
  BOOL  fBarFocused : 1;
  BOOL  fFocused : 1;
} MENUBARINFO, *PMENUBARINFO, *LPMENUBARINFO;

note the use of bit fields for fBarFocused/fFocused,
which appear in the *same* BOOL (not separate BOOLs)

here is AHK code to retrieve the 2 values from a MENUBARINFO struct:
fBarFocused := 1 & NumGet(&MENUBARINFO, A_PtrSize=8?40:28, "UInt")
fFocused := !!(2 & NumGet(&MENUBARINFO, A_PtrSize=8?40:28, "UInt"))

what follows are some notes on calculating the struct size/offsets:

first we get the parameter types:
DWORD: UInt
RECT: Int+Int+Int+Int
HMENU: Ptr
HWND: Ptr
BOOL: Int

so: UInt, Int+Int+Int+Int, Ptr, Ptr, Int

then we get the parameter sizes:
for x64: 4, 4+4+4+4, 8, 8, 4 (sum: 40)
for x32: 4, 4+4+4+4, 4, 4, 4 (sum: 32)

however for both x64 and x32 applications:
an n-byte parameter must start an n-byte offset,
i.e. at an offset that is a multiple of n,
we add padding to the x64 struct:
for x64: 4, 4+4+4+4(+4), 8, 8, 4 (sum: 44)
for x32: 4, 4+4+4+4,     4, 4, 4 (sum: 32)

also, the size of a structure, must be a multiple
of the size of the largest parameter,
we add padding at the end of the x64 struct:
for x64: 4, 4+4+4+4(+4), 8, 8, 4(+4) (sum: 48)
for x32: 4, 4+4+4+4,     4, 4, 4     (sum: 32)

parameter offsets:
for x64: 0, 4, 24, 32, 40 (sum: 48)
for x32: 0, 4, 20, 24, 28 (sum: 32)

note: the two rules about multiples only apply to raw parameters
of size: 1, 2, 4, 8 bytes
==================================================

> FIND INFO: FUNCTIONS

- Generally you can find an MSDN page for a Winapi function.
- As mentioned above, unlike other Winapi functions, the gdiplus functions don't have individual MSDN pages, but are instead grouped together.
- Also mentioned above, the msvcrt functions do not have standard MSDN pages.
- Also mentioned above, some functions (e.g. in ntdll.dll) are undocumented, any information you find is unofficial.

==================================================

> FIND INFO: CONSTANTS/ENUMS

- On MSDN pages, sometimes constants are mentioned.
- E.g. GENERIC_READ and GENERIC_WRITE, used by the function CreateFileW/CreateFileA.
- Some of the values for constants are listed on MSDN pages, some can be found in header files (.h files) that come with Visual Studio.
- If a constant is not listed on MSDN, you can look it up, by searching within .h files in a folder such as:
C:\Program Files (x86)\Windows Kits\8.1
that comes with Visual Studio Express for Windows Desktop.
- On an enum (a list of constants), either the constant values are specified explicitly, otherwise, they are typically the integers from 0 onwards.

==================================================

> FIND INFO: STRUCT SIZES/OFFSETS

- Winapi structs typically have their own pages on MSDN.
- It was demonstrated how to calculate the struct sizes/member offsets for structs above, in 'STRUCTS: ALIGNMENT (PADDING, OFFSETS)'.
- There are tips below for determining the AHK equivalent for a parameter type, in 'FIND INFO: TYPES'.

- There is a list of common structs, and their sizes/offsets here:
list of structs with parameters (sizes and types) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=74&t=30497
- Note: a small number of structs do not appear to be consistent with the rules for calculating sizes/offsets, and are marked with '[CHECK]'.

- Here is some C++ code to demonstrate how to retrieve size/offset info programmatically.
C++: structs: get struct sizes, get member offsets/sizes - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=75&t=61987

- Usually, if you are using AHK 64-bit, you should use the 64-bit rules for structs.
- And if you are using AHK 32-bit, you should use the 32-bit rules for structs.
- However, sometimes you must prepare a struct to match an external process.
- (The rules for 64-bit/32-bit are the same, but the sizes of Ptr/UPtr will differ.)
- E.g. for the functions here, that retrieve text from external GUIs, the structs must match the bitness of the external processes:
GUIs via DllCall: text functions (get/set internal/external control text) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=40514

==================================================

> FIND INFO: TYPES

- The AHK equivalents for many common Windows data types, are listed here:

Code: Select all

;list of AHK equivalents for Windows data types

;source:
;Windows Data Types - Windows applications | Microsoft Docs
;https://docs.microsoft.com/en-us/windows/desktop/winprog/windows-data-types

UShort	ATOM
Int	BOOL
UChar	BOOLEAN
UChar	BYTE
Char	CCHAR
Char	CHAR
UInt	COLORREF
UInt	DWORD
UInt64	DWORDLONG
UPtr	DWORD_PTR
UInt	DWORD32
UInt64	DWORD64
Float	FLOAT
Ptr	HACCEL
Int/Short	HALF_PTR
Ptr	HANDLE
Ptr	HBITMAP
Ptr	HBRUSH
Ptr	HCOLORSPACE
Ptr	HCONV
Ptr	HCONVLIST
Ptr	HCURSOR
Ptr	HDC
Ptr	HDDEDATA
Ptr	HDESK
Ptr	HDROP
Ptr	HDWP
Ptr	HENHMETAFILE
Int	HFILE
Ptr	HFONT
Ptr	HGDIOBJ
Ptr	HGLOBAL
Ptr	HHOOK
Ptr	HICON
Ptr	HINSTANCE
Ptr	HKEY
Ptr	HKL
Ptr	HLOCAL
Ptr	HMENU
Ptr	HMETAFILE
Ptr	HMODULE
Ptr	HMONITOR
Ptr	HPALETTE
Ptr	HPEN
Int	HRESULT
Ptr	HRGN
Ptr	HRSRC
Ptr	HSZ
Ptr	HWINSTA
Ptr	HWND
Int	INT
Ptr	INT_PTR
Char	INT8
Short	INT16
Int	INT32
Int64	INT64
UShort	LANGID
UInt	LCID
UInt	LCTYPE
UInt	LGRPID
Int	LONG
Int64	LONGLONG
Ptr	LONG_PTR
Int	LONG32
Int64	LONG64
Ptr	LPARAM
Ptr	LPBOOL
Ptr	LPBYTE
Ptr	LPCOLORREF
Ptr	LPCSTR
Ptr	LPCTSTR
Ptr	LPCVOID
Ptr	LPCWSTR
Ptr	LPDWORD
Ptr	LPHANDLE
Ptr	LPINT
Ptr	LPLONG
Ptr	LPSTR
Ptr	LPTSTR
Ptr	LPVOID
Ptr	LPWORD
Ptr	LPWSTR
Ptr	LRESULT
Ptr	PBOOL
Ptr	PBOOLEAN
Ptr	PBYTE
Ptr	PCHAR
Ptr	PCSTR
Ptr	PCTSTR
Ptr	PCWSTR
Ptr	PDWORD
Ptr	PDWORDLONG
Ptr	PDWORD_PTR
Ptr	PDWORD32
Ptr	PDWORD64
Ptr	PFLOAT
Ptr	PHALF_PTR
Ptr	PHANDLE
Ptr	PHKEY
Ptr	PINT
Ptr	PINT_PTR
Ptr	PINT8
Ptr	PINT16
Ptr	PINT32
Ptr	PINT64
Ptr	PLCID
Ptr	PLONG
Ptr	PLONGLONG
Ptr	PLONG_PTR
Ptr	PLONG32
Ptr	PLONG64
Int	POINTER_32
Int64	POINTER_64
Ptr	POINTER_SIGNED
UPtr	POINTER_UNSIGNED
Ptr	PSHORT
Ptr	PSIZE_T
Ptr	PSSIZE_T
Ptr	PSTR
Ptr	PTBYTE
Ptr	PTCHAR
Ptr	PTSTR
Ptr	PUCHAR
Ptr	PUHALF_PTR
Ptr	PUINT
Ptr	PUINT_PTR
Ptr	PUINT8
Ptr	PUINT16
Ptr	PUINT32
Ptr	PUINT64
Ptr	PULONG
Ptr	PULONGLONG
Ptr	PULONG_PTR
Ptr	PULONG32
Ptr	PULONG64
Ptr	PUSHORT
Ptr	PVOID
Ptr	PWCHAR
Ptr	PWORD
Ptr	PWSTR
UInt64	QWORD
Ptr	SC_HANDLE
Ptr	SC_LOCK
Ptr	SERVICE_STATUS_HANDLE
Short	SHORT
UPtr	SIZE_T
Ptr	SSIZE_T
UShort/UChar	TBYTE
UShort/Char	TCHAR
UChar	UCHAR
UInt/UShort	UHALF_PTR
UInt	UINT
UPtr	UINT_PTR
UChar	UINT8
UShort	UINT16
UInt	UINT32
UInt64	UINT64
UInt	ULONG
UInt64	ULONGLONG
UPtr	ULONG_PTR
UInt	ULONG32
UInt64	ULONG64
UShort	USHORT
Int64	USN
UShort	WCHAR
UShort	WORD
UPtr	WPARAM

;note: omitted: APIENTRY, CALLBACK, CONST, UNICODE_STRING, VOID, WINAPI
;note: HALF_PTR/UHALF_PTR depend on the whether the process is 64-bit/32-bit
;note: TBYTE/TCHAR depend on the whether the process is Unicode/ANSI
;note: LONGLONG/ULONGLONG check _M_IX86, and could be Double, according to the MSDN page
;note: error on MSDN page: 'typedef HANDLE WINSTA;', it should be 'HWINSTA'

;see also:
;Windows Data Types for AHK - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=6&t=7342
;AutoHotkey_MSDN_Types/src at master · jNizM/AutoHotkey_MSDN_Types · GitHub
;https://github.com/jNizM/AutoHotkey_MSDN_Types/tree/master/src
- Some things to note re. the list:
- Although AutoHotkey stores 'UInt64' numbers as 'Int64', it can accept 'UInt64' when using DllCall/NumGet/NumPut, and can display them using Format. Also, the 'U' information is useful to know when using other programming languages.
- Types beginning with H (handle) are typically Ptr (2 notable exceptions are HFILE/HRESULT, which are Int).
- Types beginning with P/LP (pointer/long pointer) are typically Ptr.
- Characters/strings can be Unicode ('Wide') or ANSI, a 'TCHAR' or '...TSTR' is Unicode or ANSI depending on the process.
- BOOL is Int (4 bytes), whereas BOOLEAN is UChar (1 byte). In principle, a boolean value is 1 or 0 (true or false), and only needs 1 *bit* (1/8 of a byte) to store it.

- Some definitions for certain parameters:

Code: Select all

;HALF_PTR/UHALF_PTR/TBYTE/TCHAR
vHalfPtr := (A_PtrSize=8) ? "Int" : "Short"
vUHalfPtr := (A_PtrSize=8) ? "UInt" : "UShort"
vTByte := A_IsUnicode ? "UShort" : "UChar"
vTChar := A_IsUnicode ? "UShort" : "Char" ;official
vTChar := A_IsUnicode ? "UShort" : "UChar" ;unofficial, but arguably preferable

;note: when retrieving the character number, for a character,
;you typically want a value that is 0 or positive,
;for this reason, if a TCHAR/CHAR is expected in a function,
;it may be better to force the number to be unsigned,
;rather than signed
- Further notes:
- An enum is a number taken from a list. An enum is typically an Int. If the number 0x80000000 is present in the enum, the enum is probably a UInt (and not an Int).
- If a parameter type is unfamiliar, you can look it up, by searching within .h files in a folder such as:
C:\Program Files (x86)\Windows Kits\8.1
that comes with Visual Studio Express for Windows Desktop.
- Visual Studio can be used to retrieve the size and signedness of a parameter type. E.g. by using sizeof, and by assigning a value and checking whether it is reported as positive/negative.
- The use of * can be confusing in C++:
E.g. typedef UINT *PUINT;
Would be more intuitively written like this:
E.g. typedef UINT* PUINT;
I.e. the type PUINT is equivalent to UINT* (a pointer to a UINT).

- Be wary of forum examples:
- Examples on the forum are not always correct, e.g. many scripts use Int/UInt (AutoHotkey v1.0 was 32-bit only) instead of Ptr/UPtr (64-bit/32-bit two-way compatible).
- Also, when writing, e.g. using NumPut, sign doesn't matter, i.e. Int/UInt are interchangeable, thus some people incorrectly use/omit the 'U'.
- However, when reading, e.g. using NumGet, or retrieving the return value of DllCall, using/omitting the 'U' could give you the wrong result.

- Rules of thumb:
- Generally speaking H/P/LP/* indicate a Ptr (2 exceptions are HFILE/HRESULT).
- SIZE_T appears sometimes, which is a UPtr.
- Otherwise it's usually an Int/UInt.

- This list tries to collect the AHK parameter types for multiple common dlls and their functions (although not msvcrt.dll or ntdll.dll).
WinApi
https://hotkeyit.github.io/v2/docs/commands/WinApi.htm
- This script aims to retrieve the parameter sizes for those parameter types:
C++: DllCall: get parameter types/sizes - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=75&t=39426

- I've been using this script to correct the parameter types (and syntax) whenever I use DllCall in my scripts.
DllCall converter/cleaner (e.g. x32 to x64/x32 two-way compatible) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=31365

==================================================

> LINKS

[machine code functions]
[instead of calling an existing function from a dll, we can put some machine code in memory, and run that via DllCall]
C++: C++ to machine code via TDM-GCC - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=75&t=49554
ASM: ASM (assembly language) to machine code via FASM (flat assembler) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=79&t=49638
resources for learning assembly language - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=17&t=57651

[discussing the default values for NumPut/NumGet/DllCall]
DllCall description - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=60306

[Cdecl v. Stdcall]
DllCall: Cdecl - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=37551

[DllCall converter][script to correct DllCall lines][fixes parameter types based on a list]
DllCall converter/cleaner (e.g. x32 to x64/x32 two-way compatible) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=31365

[getting list info for a DllCall converter/tidier][checking Winapi function info via C++]
C++: DllCall: get parameter types/sizes - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=23&t=39426
WinApi
https://hotkeyit.github.io/v2/docs/commands/WinApi.htm

[ list of structs][some info on DllCall in the first post]
list of structs with parameters (sizes and types) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=74&t=30497

[some info on DllCall/structs]
which languages should I learn to learn DllCall and Windows API Functions? - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=59780&p=252092#p252092
key dll issues: calls and structs - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=26406

[AutoHotkey via DllCall][recreate functionality via DllCall]
AutoHotkey via DllCall: AutoHotkey functions as custom functions - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=37871

[GUIs via DllCall][recreate functionality via DllCall]
[note: the 'list windows/child windows' script shows an example of a callback function]
GUIs via DllCall: MsgBox - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=30688
GUIs via DllCall: control zoo - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=32106
GUIs via DllCall: list windows/child windows (controls) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=36405
GUIs via DllCall: text functions (get/set internal/external control text) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=40514

[floats/doubles]
jeeswg's floats and doubles mini-tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=74&t=63650
IEEE 754 - Wikipedia
https://en.wikipedia.org/wiki/IEEE_754
Single-precision floating-point format - Wikipedia
https://en.wikipedia.org/wiki/Single-precision_floating-point_format
Double-precision floating-point format - Wikipedia
https://en.wikipedia.org/wiki/Double-precision_floating-point_format

[ lists of dll functions]
list of dll functions with parameter types for DllCall - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=74&t=30832&p=167332#p167332
list of handy dll functions - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=34017
WinApi, DllCalls & AHK - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=406
WinAPI - GetXxxFormat Functions - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=10082

[Windows messages for use with PostMessage/SendMessage]
List of Windows Messages - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=39218

[an example of using DllCall with a COM interface (ITaskbarList)]
DllCall() - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/DllCall.htm

[some discussions]
key dll issues: calls and structs - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=26406
DllCall: IntP/UIntP, Int64P/UInt64P, PtrP/UPtrP - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=39906
isspace versus iswspace - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=59265
Passing structure to function - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=60442
structs: unions - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=60931

==================================================

Re: jeeswg's DllCall and structs tutorial

Posted: 16 Apr 2019, 20:08
by Klarion
N.I.C.E.
I'll check it out with SQLite

Re: jeeswg's DllCall and structs tutorial

Posted: 22 Nov 2019, 17:39
by iwrk4dedpr
:clap: :clap: :clap:
Hot damn I got me some reading and testing tonight. Thanks for taking the time .... and posting information like this.

Re: jeeswg's DllCall and structs tutorial

Posted: 25 Nov 2019, 22:58
by AHKStudent
Thank you, this is the best tutorial on these topics I ever saw

Re: jeeswg's DllCall and structs tutorial

Posted: 05 May 2020, 17:30
by hasantr
Thanks this is very good. Dllcall wanted to learn, but there seemed to be not enough supply. This is awesome.

Re: jeeswg's DllCall and structs tutorial

Posted: 11 Sep 2020, 23:00
by SOTE
This is another one of those good undervalued posts by jeeswg.

Re: jeeswg's DllCall and structs tutorial

Posted: 21 Feb 2022, 04:47
by serg
Thank you for this helpful and succinct tutorial!