The documentation makes it seem as if calling a dll function with "UChar*" will pass the address of a variable, much like passing a "UInt" with the address-of operator (&) but these are NOT the same. One works and the other throws access violations.
What is the difference? Why would someone use the "UChar*" form? It seems to be broken.

DllCall: "UChar*", buffer vs. "UInt", &
Started by
Jamie
, May 21 2010 02:35 PM
7 replies to this topic
#1
-
Posted 21 May 2010 - 02:35 PM

"UChar*" will push ONE byte on the stack.
"UInt" will push FOUR bytes on the stack.
I think. ?
"UInt" will push FOUR bytes on the stack.
I think. ?
#2
-
Posted 21 May 2010 - 02:41 PM

Wolf
Schön wär's, wenn's schön wär!
Schön wär's, wenn's schön wär!
Pointers take four bytes, at least on a 32 bit system. So they should be equivalent as far as what ends up on the stack.
#3
-
Posted 21 May 2010 - 03:09 PM

You are probably doing something wrong. I just compiled a DLL with a couple of functions — one reads from, the other writes to a variable passed as "UChar *". No errors, the value is read and written correctly.
"UChar *" in AHK is a pointer to a single 8-bit number, not a pointer to an array of 8-bit numbers. Maybe you are trying to pass a string as "UChar *"? I saw that if the variable had been initialized with a string, the function received a pointer to zero and not to the string's first character. The string is probably discarded and the variable reinitialized as a number before passing its pointer to the function.
"UChar *" in AHK is a pointer to a single 8-bit number, not a pointer to an array of 8-bit numbers. Maybe you are trying to pass a string as "UChar *"? I saw that if the variable had been initialized with a string, the function received a pointer to zero and not to the string's first character. The string is probably discarded and the variable reinitialized as a number before passing its pointer to the function.
#4
-
Posted 21 May 2010 - 05:11 PM

So here's a function which simply returns its argument:
When invoked as MCode using ("UInt", &var), the return value matches &var. But when invoked as ("UInt*", var), the return value does not match &var. So I am not seeing how it could work to read or write values.
typedef unsigned int uint; uint __stdcall ptrtest(uint *p) { return (uint)p; }Which condenses into this mcode: 8b442404c20400
When invoked as MCode using ("UInt", &var), the return value matches &var. But when invoked as ("UInt*", var), the return value does not match &var. So I am not seeing how it could work to read or write values.
MCode(ptrtest, "8b442404c20400") ; stdcall function just returns its argument SetFormat, IntegerFast, H VarSetCapacity(buf1, 4, 1) rc := DllCall(&ptrtest, "UInt*", buf1) abuf1 := &buf1 MsgBox, return code: %rc% &buf1: %abuf1% errorlevel: %errorlevel% ; says return code: 0x88e7ec &buf1: 0xec0fc4 errorlevel: 0 SetFormat, IntegerFast, DIn what appears to be a different, probably unrelated issue, when I use the ("UInt", &var) method, I can read from the address, but not write to it. (When using ("UInt*", var) I can't read either). Reading will get the correct value (0x01010101 as expected from VarSetCapacity) but writing produces a return code indicating an access violation.
#5
-
Posted 21 May 2010 - 11:24 PM

As far as I know, plain variables declared as "Var = 111" are stored internally as strings, even if they represent numerical values. When you explicitly request a pointer to a number ("Int *", Var), that number is probably allocated in a different place in binary form and you get a pointer to that location. After the function returns, the binary result of its work is converted back to string form and stored back in the original place. But when you request the address of the variable ("UInt", &Var), AHK just gives you what you want, not doing any conversions, probably because in this case it doesn't know what you are going to do with the variable. Of course, those are just my guesses, but they could explain the difference you observed.
Variables created via VarSetCapacity hold their values already in binary form, so you normally would not use "Int *" with them. Here the correct approach is &Var.
Compare these two examples, in both of them the function swaps the values of the variables. For that, of course, it should be able to read from and write to them.
And finally, the source of my machine code function. Sorry, I don't have a C compiler at the moment, it's in assembler:
Variables created via VarSetCapacity hold their values already in binary form, so you normally would not use "Int *" with them. Here the correct approach is &Var.
Compare these two examples, in both of them the function swaps the values of the variables. For that, of course, it should be able to read from and write to them.
Var1 = 111 Var2 = 222 MsgBox, Var1: %Var1%`nVar2: %Var2% MCode(ptrtest,"5589E58B45088B550CFF30FF328F008F025DC20800") DllCall(&ptrtest, "Int *", Var1, "Int *", Var2) MsgBox, Var1: %Var1%`nVar2: %Var2% ;=================== MCode ============================================ MCode(ByRef code, hex) { ; allocate memory and write Machine Code there VarSetCapacity(code,StrLen(hex)//2) Loop % StrLen(hex)//2 NumPut("0x" . SubStr(hex,2*A_Index-1,2), code, A_Index-1, "Char") }
SetFormat, Integer, Hex VarSetCapacity(Var1, 4, 1) VarSetCapacity(Var2, 4, 2) MsgBox, % "Var1: " NumGet(Var1) "`n Var2: " NumGet(Var2) MCode(ptrtest,"5589E58B45088B550CFF30FF328F008F025DC20800") DllCall(&ptrtest, "UInt", &Var1, "UInt", &Var2) MsgBox, % "Var1: " NumGet(Var1) "`n Var2: " NumGet(Var2) ;=================== MCode ============================================ MCode(ByRef code, hex) { ; allocate memory and write Machine Code there VarSetCapacity(code,StrLen(hex)//2) Loop % StrLen(hex)//2 NumPut("0x" . SubStr(hex,2*A_Index-1,2), code, A_Index-1, "Char") }
And finally, the source of my machine code function. Sorry, I don't have a C compiler at the moment, it's in assembler:
ptrTest1: FRAME ptr1, ptr2 mov eax,[ptr1] mov edx,[ptr2] push [eax] push [edx] pop [eax] pop [edx] ret ENDF
#6
-
Posted 22 May 2010 - 04:01 AM

Aha, this would make sense! Thank you, I hadn't thought about that.As far as I know, plain variables declared as "Var = 111" are stored internally as strings, even if they represent numerical values. When you explicitly request a pointer to a number ("Int *", Var), that number is probably allocated in a different place in binary form and you get a pointer to that location. After the function returns, the binary result of its work is converted back to string form and stored back in the original place.
As for the exceptions being generated, I'm still digging into that. Your code works for me, and when I write the equivalent function in C it also works. But if I change it to assign an immediate constant, it returns an exception.
typedef unsigned int uint; uint __stdcall ptrtest(uint *p, uint *q) { uint t = *p; t = 123; *p = *q; *q = t; return t; }
_p$ = 8 ; size = 4 _q$ = 12 ; size = 4 [email protected]@[email protected] PROC ; ptrtest, COMDAT 00000 8b 44 24 08 mov eax, DWORD PTR _q$[esp-4] 00004 8b 08 mov ecx, DWORD PTR [eax] 00006 8b 54 24 04 mov edx, DWORD PTR _p$[esp-4] 0000a 89 0a mov DWORD PTR [edx], ecx 0000c c7 00 7b 00 00 00 mov DWORD PTR [eax], 123 ; 0000007bH 00012 b8 7b 00 00 00 mov eax, 123 ; 0000007bH 00017 c2 08 00 ret 8 [email protected]@[email protected] ENDP ; ptrtest
MCode(ptrtest, "8b4424088b088b542404890a0000cc7007b000000b87b000000c20800")It's frustrating because it should be simple. And doing arithmetic operations such as t=*p+*q gives correct results with no exceptions. I checked the pointer alignment and they're all 4-byte aligned. It's puzzling to me. It seems the immediate assignment is causing the problem somehow.
#7
-
Posted 22 May 2010 - 11:08 AM

AW CRAP
Okay, i found it, the code i was using to copy from assembly code to produce the mcode was injecting some wrong hex bytes into it.
The red highlighted hex digits are not supposed to appear in the machine code anywhere but showed up mysteriously.
8b4424088b088b542404890a0000cc7007b000000b87b000000c20800
When I remove them, it all is good! Sorry for the mistaken problem.
Okay, i found it, the code i was using to copy from assembly code to produce the mcode was injecting some wrong hex bytes into it.
The red highlighted hex digits are not supposed to appear in the machine code anywhere but showed up mysteriously.
8b4424088b088b542404890a0000cc7007b000000b87b000000c20800
When I remove them, it all is good! Sorry for the mistaken problem.
#8
-
Posted 22 May 2010 - 11:29 AM
