[a103] DllCall/CallbackCreate wrong floating point values Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

[a103] DllCall/CallbackCreate wrong floating point values

Post by swagfag » 13 Jun 2019, 03:53

Code: Select all

timesTwo := CallbackCreate(n => n * 2)

MsgBox DllCall(timesTwo, "Int", 2, "Int") ; 4

MsgBox DllCall(timesTwo, "Float", 2.5, "Float") ; 1.4012984643248171e-43
MsgBox DllCall(timesTwo, "Double", 2.5, "Double") ; 1.6954746370306591e-311
i dont understand why i keep getting the same wrong answer(see comment) regardless of what number i pass in.
how do u pass floats to callbackcreated functions?
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [a103] DllCall/CallbackCreate wrong floating point values

Post by Helgef » 13 Jun 2019, 04:04

callbackcreate do not generally support floats / doubles, only integers.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [a103] DllCall/CallbackCreate wrong floating point values

Post by swagfag » 13 Jun 2019, 04:14

Code: Select all

timesTwo := CallbackCreate(FloatStruct => (
	f := NumGet(FloatStruct, 0, "Float"), 
	NumPut(f * 2, FloatStruct, 0, "Float")
))

FloatStruct := BufferAlloc(4)
NumPut(2.5, FloatStruct, 0, "Float")

DllCall(timesTwo, "Ptr", FloatStruct)

MsgBox NumGet(FloatStruct, 0, "Float") ; 5.0
i came up with this but damn... this seems like an awful lot to have to do just to pass a float
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [a103] DllCall/CallbackCreate wrong floating point values

Post by Helgef » 13 Jun 2019, 04:31

you can use float* instead, eg,

Code: Select all

timesTwo := CallbackCreate(FloatStruct => (
	f := NumGet(FloatStruct, 0, "Float"), 
	NumPut(f * 2, FloatStruct, 0, "Float") - 4
))

msgbox DllCall(timesTwo, 'float*', 2.5, 'float*')
The problem isn't passing floats, dllcall does that just fine, but rather that callbackcreate can't recieve or return floats, in general.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [a103] DllCall/CallbackCreate wrong floating point values

Post by Helgef » 13 Jun 2019, 06:36

i dont understand why i keep getting the same wrong answer(see comment) regardless of what number i pass in.
About x64:
When the callback returns it puts your number in the integer return register, rax. But dllcall will return, to you, what ever happens to be in the float return register, xmm0. For example, if you comment out the first two msgboxes, you might get another result. Likewise, when the callback is called, it will read, for the first four parameters, whatever is in the integer parameter registers, which are rcx, rdx, r8, r9, but when you pass a float / double, these are put in xmm0-3 registers. The twist is, that dllcall always (unconventionally) passes params in both the integer and float registers, that is why the Receiving parameters by address example in the callbackcreate docs work on x64. The reason it passes the params in all registers, is to support calling variadic functions. So don't be fooled by the (poor) example in the docs, that will only work when the caller is dllcall. The example was probably written with the 32 bit calling conventions in mind, for those, it works more generally.

Here is a non-sensical example which currently (at least for me) happens to work, you should be able to figure out what happens with the above information,

Code: Select all

; do not use this code
timesTwo := CallbackCreate(n => (n := numget(&n, 'double')*2, numget(&n, 'double')) )
MsgBox DllCall(timesTwo, "Double", 2.5, "Double")
Cheers.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [a103] DllCall/CallbackCreate wrong floating point values

Post by swagfag » 13 Jun 2019, 10:15

i am running x64, forgot to mention.

after reading
https://lexikos.github.io/v2/docs/commands/CallbackCreate.htm
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/x64-architecture#calling-conventions
https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2019#parameter-passing
https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2019#unprototyped-functions
i kind of get it, but at the same time i kind of dont

apparently, simply inserting floats into registers meant for integers is something u can do. i dont understand how ur "dont-do-this" example works. they present the following:

Code: Select all

func1();
func2() {   // RCX = 2, RDX = XMM1 = 1.0, and R8 = 7
   func1(2, 1.0, 7);
}
extrapolating from that:

Code: Select all

DllCall(timesTwo, "Double", 2.5, "Double")
; after dllcall
; | RCX  | RDX  | R8   | R9   |
; | 2.5  | ???? | ???? | ???? |
; |++++++|++++++|++++++|++++++|
; | XMM0 | XMM1 | XMM2 | XMM3 |
; | 2.5  | ???? | ???? | ???? |
then the NumGets and reassigning inside the callback i dont get. how the fetching from the address works, why it works and how long it will work for and under what circumstances will it cease to work, i dont get. the docs say:
AutoHotkey 64-bit: All incoming parameters are signed 64-bit integers. AutoHotkey does not natively support unsigned 64-bit integers. Smaller types are padded out to 64 bits, while larger types are always passed by address.
but i dont think a float is a "larger type" for it to be passed by address and for u to access with NumGet
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [a103] DllCall/CallbackCreate wrong floating point values  Topic is solved

Post by Helgef » 13 Jun 2019, 16:56

Hi. Good job on the research :thumbsup:. I modify my example an put in comments,

Code: Select all

; do not use this code
timesTwo := CallbackCreate(n => 
(	n := numget(&n, 'double'),						; 'double', 2.5 is passed in the float register, but callbacks doesn't see this, however, since dllcall blindly supports variadic functions, it also passes the float in the integer register, rcx. So n is an integer (64bit) with the same bit representation as the double 2.5, so just read it as double.
	float(n*2),										; do the calculation, pass the result to a float function, eg, numget, abs or float and hope that the result is put in the xmm0 register, i.e., float passing/return register.
	5551234) 	)									; return whatever you like, dllcall never sees this beacuse it only looks in xmm0, since we tell it the return is 'double'
MsgBox DllCall(timesTwo, "Double", 2.5, "Double")	; lets hope the float is still in xmm0!
This example is perhaps even less sensical, but I think it is also less obscure.

Cheers.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [a103] DllCall/CallbackCreate wrong floating point values

Post by swagfag » 14 Jun 2019, 07:51

yeah its more or less clear now what has been happening. thanks

what i didnt know was that, inside the CallbackCreated callback, doing NumGet(&argN, 0, "Type") for N = 1,2,3,4 was accessing the respective general purpose register rcx, rdx, r8, r9
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [a103] DllCall/CallbackCreate wrong floating point values

Post by Helgef » 14 Jun 2019, 08:25

To be nit picky, although I assume you got it, you do not really access the registers directly, AHK just passes the contents of those registers to your callback function's parameters. &argN always gives the address of the (AHK) variable's content.

Cheers.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [a103] DllCall/CallbackCreate wrong floating point values

Post by swagfag » 14 Jun 2019, 12:55

yeah i suppose that is the more accurate thing to say. addressof doesnt access the registers directly per se, rather the contents happen to be in those registers(because dllcall put them in there)
Post Reply

Return to “Ask for Help (v2)”