- NSIS Discussion
- Pass large array to external DLL using System::Call
Archive: Pass large array to external DLL using System::Call
charlies
12th December 2006 23:11 UTC
Pass large array to external DLL using System::Call
I am trying to call an external DLL that expects a 112 byte array of binary data. The 'C' prototype looks like:
unsigned long ExternalFunction(unsigned long param1,
unsigned char* pParam2,
unsigned long param3,
unsigned long param4,
unsigned long* pParam5);
It is pParam2 that needs to be a fixed buffer of 112 bytes of data. I am calling the function as follows and receiving a valid (but ERROR) return code.
System::Call 'ExternalDLL::ExternalFunction(i,*i,i,i,*v) i (0x12345678,.r0r0,0x4321,10,.r1r1) .r2'
Any suggestions about how to populate $0 with the address of an array of 112 constant, hexadecimal values?
- Charlie
Comm@nder21
13th December 2006 17:49 UTC
try this:
System::Call 'ExternalDLL::ExternalFunction(i 0x12345678, t .r0, i 0x4321, i 10, *i .r1) i .r2'
charlies
13th December 2006 18:47 UTC
comm@nder21,
Thanks for the response, but that did not work either as the data in $0 may have NULLs. I have read brainsucker's System Plug-in doc and have a better idea about how things work, but have yet to get to a working solution. I am presently investigating the allocation of a structure of longlongs as follows:
System::Call '*(l 0x1234567890123456, l ... ) i.s'
Pop $0
with the ... filled in with the remaining 13 longlong values in order to populate an allocated pointer with my 112 bytes of data. I then changed the call to be:
System::Call 'MyDLL::MyFunction(i,i,i,i,*v) i (0x1234567,r0,0x321,10,.r1) .r2'
I chose the parameter type (i r0) based upon the structure passing documentation in brainsucker's doc. Any idea if I am on the right path?
- Charlie
kichik
14th December 2006 20:10 UTC
You are on the right track. The only problem I see in the current code is the last parameter type. `v` is not a valid parameter type, You should use `i`.
charlies
14th December 2006 23:04 UTC
kichik,
Thanks for taking the time to look into my problem. After reading more of brainsucker's docs (and writing a C DLL in order to debug), I was finally to come up with a solution/hack.
First, as you mention '*v' is not valid (except as a void return type I believe). 'v' might have worked. In any case, the Call interface became:
System::Call 'MyDLL::MyFunction(i,i,i,i,i) i (0x1234567,r0,0x321,10,r1) .r2'
Second, I had to slightly modify my method to fill a binary array because of Intel byte ordering (little endian).
System::Call '*(l 0x1234567890123456, l ... ) i.s'
Pop $0
became
System::Call '*(l 0x5634129078563412, l ... ) i.s'
Pop $0
Changing all 14 longlong definitions was a pain, but I could not find any other way to force the bytes into a buffer without incidental padding.
e.g. System::Call '*(i 0x56, i 0x34, i 0x12 ... ) i.s'
Pop $0
yields 0x560000003400000012000000. I could have written a C DLL that performed the binary array declatation for me and called 'MyFunction' from the DLL, but I was hoping for a native NSIS solution. If anyone has a solution to filling a buffer with arbitrary binary data, I'd be happy to try it, but for now, I have a functional solution.
- Charlie
kichik
14th December 2006 23:24 UTC
Use `&i1` instead of just `i` to avoid the padding.
charlies
19th December 2006 21:04 UTC
kichik,
Thanks for the suggestion. Some external changes to my application environment required that my 112 bytes of data be changed, so I decided to try your suggestion. I was eventually able to get it to work, but I could not use the &i1 for all 112 bytes of data in a single call (perhaps too many function parameters?). I ended up using Alloc and then setting 16 bytes at a time, padding the lower bytes as follows:
System::Call '*$0(&i1 0x00, &i1 0x01, ..., &i1 0x0F)'
System::Call '*$0($i16, &i1 0x10, &i1 0x11, ..., &i1 0x1F)'
...
System::Call '*$0($i96, &i1 0x60, &i1 0x61, ..., &i1 0x6F)'
A bit crude, but acceptable and not too difficult to maintain.
Thanks again for your insights.
- Charlie