AutoHotkey icon indicating copy to clipboard operation
AutoHotkey copied to clipboard

v2: Added support for structes that passed by value

Open thqby opened this issue 3 years ago • 6 comments

Currently, the supported invocation methods are as follows.

DllCall(func, structsize := 16, structptr, retsize := 32)
DllCall(func, 'struct16', structptr, 'cdecl struct32')
DllCall(func, '16', structptr, 'cdecl 32')
ComCall(index, thisptr, structsize := 16, structptr, retsize := 32)
...

thqby avatar Jul 09 '22 12:07 thqby

You shouldn't have to pass the struct ptr/size, it should suffice if the struct object has ptr/size properties. (I didn't look at your code, just your documentation.)

It might be better to defer this until the script can define structs, i.e., something like this,

struct s { 
    int a
    int b
}

Anyways, thanks for your contribution 👍

HelgeffegleH avatar Jul 14 '22 07:07 HelgeffegleH

Sometimes, the struct may be part of the buffer. In this case, it is more convenient to pass the ptr of struct directly. Of course, objects with ptr property can also be passed.

thqby avatar Jul 14 '22 08:07 thqby

When passing structs by value to a C function, the compiler must know the exact size of the struct. It cannot vary at runtime. Future optimizations to DllCall could take advantage of this, but (I suppose) only if the struct size can be determined by statically analyzing the code.

The implementation of struct classes would supersede the structXX syntax, but only as a way of specifying the type and size. The underlying implementation would be very similar.

The inconsistency between struct64 and int64 seems odd, yet it wouldn't make sense to specify the number of bits for a struct. I more or less decided to also implement int8, int16, etc. but procrastinated because I was still considering replacing DllCall with something of a "define once, use many times" kind. (I finished considering; now I'm planning.)

Lexikos avatar Jul 16 '22 06:07 Lexikos

What was the reason for setting the DC_BORLAND flag? Isn't it redundant when aRetSize >= 8? Are you expecting aRetSize to sometimes be 3, 5, 6 or 7? Is it actually correct to pass such a value (supposing such a value is legal) the same way as for >= 8 (because I think that's what will happen with your code)?

Lexikos avatar Jul 16 '22 08:07 Lexikos

Yes, it's redundant. It can be determined directly by aRet without this flag. In this case, the following code needs to be removed.

	else if (((aFlags & DC_BORLAND) == 0) && (aRetSize <= 8))
	{
		// Microsoft optimized less than 8-bytes structure passing
        _asm
		{
			mov ecx, DWORD PTR [aRet]
			mov eax, [dwEAX]
			mov DWORD PTR [ecx], eax
			mov edx, [dwEDX]
			mov DWORD PTR [ecx + 4], edx
		}
	}

thqby avatar Jul 16 '22 09:07 thqby

I checked disassembly of some functions returning structs of 3, 5, 6 or 7 bytes, and confirmed that they are returned via the hidden parameter, which answers my previous questions about that. I assume the original DynaCall code merely failed to account for that (it isn't clearly documented for x86, anywhere I could find), and should be checking aRetSize for values 1, 2, 4 and 8, not merely <= 8.

I don't see any reason to add the DC_BORLAND flag when you could instead remove code that isn't needed. This isn't about compatibility with Borland compilers. On the other hand, it would probably be better to have this detail handled within DynaCall, not BIF_DllCall. In other words, remove the size checks from BIF_DllCall and fix the aRetSize check in DynaCall.

If you wish, you can amend the pull request by pushing to your Lexikos-alpha branch, either replacing the previous commit with push -f or adding a new commit.

I recently "ported" a version of PerformDynaCall to x86, and realized that the existing 32-bit parts of DynaCall are somewhat over-complicated. As I am thinking of replacing DllCall and adding struct support (though not in the short term), I may choose not to merge this.

Lexikos avatar Jul 23 '22 03:07 Lexikos