Archive: Pass large array to external DLL using System::Call


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


try this:

System::Call 'ExternalDLL::ExternalFunction(i 0x12345678, t .r0, i 0x4321, i 10, *i .r1) i .r2'


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


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`.


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


Use `&i1` instead of just `i` to avoid the padding.


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