.NET Forum / .NET Framework / Interop / October 2005
C# passing managed byte array to C++ function in unmanaged code
|
|
Thread rating:  |
tsnyder@digital-ego.com - 18 Oct 2005 17:02 GMT Hello All,
I've recently come accross a problem when trying to work with a unmanaged dll compiled in C++ (Visual Studio 6.0). When calling the dll from my C# application primitive data works okay for converting PLONG, INT, BOOL to the right function signature. For example:
C++ Function: LOGGER_API BOOL APIENTRY ConnectToDevice(void); LOGGER_API LONG APIENTRY GetDeviceVersion(int iTargetMicro); LOGGER_API LONG APIENTRY GetDeviceDataSize(void);
C#: [DllImport("Logger.dll")] private static extern bool ConnectToDevice(); [DllImport("Logger.dll")] private static extern int GetDeviceVersion(int TargetMicro); [DllImport("Logger.dll")] private static extern int GetDeviceDataSize();
All calls invoke the dll perfectly and I get back the right response. However, there is one particular method that has a method signature:
C++ BOOL GetDeviceData(LPBYTE HrmInputData, PLONG HrmDataSize, LPBYTE GPSData, PLONG GPSDataSize);
The api docs for this method/function read: 'on successful upload the API will copy the uploaded device data into the memory space indicated by the two addresses being passes to this API as parameters. The size of the uploaded data will also be placed into the variables whose addresses have been passed.'
So I went ahead and made this method signature in C# to call the unmanaged code:
[DllImport ("Logger.dll", SetLastError=true)] public static extern bool GetDeviceData( [In,Out] byte[] array, ref System.IntPtr hrmDataSize, [In,Out] byte[] gpsData, ref System.IntPtr gpsDataSize);
C# Calls To UnManaged: byte[] hrmData = new byte[deviceDataSize]; byte[] gpsData = new byte[deviceDataSize]; System.IntPtr hrmDataSize = new System.IntPtr(0); System.IntPtr gpsDataSize = new System.IntPtr(0); TimexHRMGPS.GetDeviceData(hrmData, ref hrmDataSize, gpsData, ref gpsDataSize);
The problem resides here, but its rather confusing. The call works because I see the dll get executed and a progress bar from the dll showing the status of the upload displays. On return however, I have the right data for HRM data Size, and GPS data Size BUT NOT any data in my byte[] array, its just an empty array. I tried using 'ref byte[]' to simulate what I suspect it wants (a pointer to memory) and even 'Marshal.AllocCoTaskMem(hrmDataSize)' and locking it.
I am probably missing something retarded as I haven't used Interop before today, but given my success with other functions I am not quite sure what the method signature should read?
Sorry for the long winded explanation, I do hope you are able to help as I have hit a wall.
Terrance A. Snyder
tsnyder@digital-ego.com - 18 Oct 2005 17:54 GMT Well I seem to have something now:
Changed Declaration To: // ---------------------------------------------------------------------------- [DllImport ("Logger.dll", SetLastError=true)] public static extern bool GetDeviceData( ref IntPtr array, ref System.IntPtr hrmDataSize, ref System.IntPtr gpsData, ref System.IntPtr gpsDataSize); // ----------------------------------------------------------------------------
IntPtrs instead of byte[] or ref byte[]. And changed the calls to: // ---------------------------------------------------------------------------- IntPtr unmanagedArray = Marshal.AllocCoTaskMem(deviceDataSize); byte[] hrmData = new byte[deviceDataSize];
byte[] gpsData = new byte[deviceDataSize]; IntPtr gpsArray = Marshal.AllocCoTaskMem(deviceDataSize);
System.IntPtr hrmDataSize = new System.IntPtr(0); System.IntPtr gpsDataSize = new System.IntPtr(0);
TimeGPSHRM.GetDeviceData(ref unmanagedArray, ref hrmDataSize, ref gpsArray, ref gpsDataSize);
Marshal.Copy(unmanagedArray, hrmData, 0, deviceDataSize); Marshal.Copy(gpsArray, gpsData, 0, deviceDataSize); // ----------------------------------------------------------------------------
While now it looks like I have data, it doesn't seem to be complete, it looks truncated. One step at a time I supposed. Any help would be appreciated.
tsnyder@digital-ego.com - 20 Oct 2005 16:37 GMT Well seeing as there is very little activity on this, I'll help those who might come across a similar problem. The final working code I have for consuming LPBYTE from managed C# is the following:
public static void Main(string[] args) { ... IntPtr hrmArray = Marshal.AllocCoTaskMem(deviceDataSize); IntPtr gpsArray = Marshal.AllocCoTaskMem(deviceDataSize);
IntPtr hrmDataSize = new IntPtr(0); IntPtr gpsDataSize = new IntPtr(0);
timexHRM.GetDeviceData(hrmArray, ref hrmDataSize, gpsArray, ref gpsDataSize);
byte[] hrmBytes = new byte[hrmDataSize.ToInt32()]; byte[] gpsBytes = new byte[gpsDataSize.ToInt32()];
Marshal.Copy(hrmArray, hrmBytes, 0, hrmDataSize.ToInt32()); Marshal.Copy(gpsArray, gpsBytes, 0, gpsDataSize.ToInt32()); .... }
class timexHRM { ... [DllImport ("Logger.dll", SetLastError=true)] public static extern unsafe bool GetDeviceData( [In,Out] System.IntPtr hrmArray, ref System.IntPtr hrmDataSize, [In,Out] System.IntPtr gpsData, ref System.IntPtr gpsDataSize); ... }
Dragon - 20 Oct 2005 17:23 GMT Hi Terrance,
Just an idea: If a parameter is of type LPBYTE why not declare it as ~ ref Byte HrfInputData ~ (Sorry for any syntax errors - I virtually don't know C#) and pass first element of an array to function?
If this still doesn't work, then correct your second attempt, i.e. remove ref in ~ ref IntPtr array ~. IntPtr is already a pointer so it doesn't make much sense to pass it ByRef.
BTW,
- Why do you use IntPtr for hrmDataSize & gpsDataSize params? PLONG is long* so wouldn't it be more correct (and convenient) to use Int32 for this? - Why do you use Marshal.AllocCoTaskMem()? Your routine seems to do nothing with COM, so you should probably use Marshal.AllocHGlobal().
Hope this helps, Roman
> Well I seem to have something now: > [quoted text clipped - 31 lines] > looks truncated. One step at a time I supposed. Any help would be > appreciated. tsnyder@digital-ego.com - 21 Oct 2005 14:24 GMT Like I said before, I've never used Interop before but I will try your suggestions and give you the results. Thank you very much for your insight.
tsnyder@digital-ego.com - 25 Oct 2005 16:39 GMT doesn't work, the returned values are now 0 for everything. kind of made it worse :)
tsnyder@digital-ego.com - 26 Oct 2005 16:13 GMT figured out the solution for those that want access:
IntPtr dataSize = Marshal.AllocHGlobal(4); // PLONG IntPtr dataBytes = Marshal.AllocHGlobal([deviceSize]); // LPBYTE device.GetEEPROMData(dataBytes, dataSize); // call the unmanaged function
// -- total size reported from the function int rDataSize = Marshal.ReadInt32(dataSize); // read from pointer Marshal.FreeHGlobal(dataSize); // free pointer
// -- get data downloaded from device byte[] dataDump = new byte[rDataSize]; // read through entire pointer byte by byte and put into managed array for (int pos=0; pos < rDataSize ; pos++) { dataDump[pos] = Marshal.ReadByte(dataBytes, pos); } Marshal.FreeHGlobal(dataBytes); // free pointer .... [DllImport("Logger.dll")] public static extern bool GetEEPROMData(IntPtr data, IntPtr dataSize); ....
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|