I have C# application that needs to interface with a native (unmanaged) DLL. The native DLL exposes its library through a C++ Class, so I decided to wrap the class with a C++/CLI DLL to be able to use it in the C# app. The problem centers on the implementation of a wrapper method within C++/CLI and how it is called from the managed C# app. The method is called with 2 buffers for the native code to write to even after this method returns. The native library will use a callback function to notify the managed application of new data. So I can't use pin_ptr<> which would be perfect if it had global scope. The C++/CLI DLL and the C# application compiles fine and runs without exceptions. I can see the data from the managed side on the native side. I can write to the data on the native side without issue. However, I cannot see what was natively written on the managed side. I think a copy is made somewhere possibly through the GCHandle.Alloc() call.
The native method has a structure called sHSLPCalibProfileData defined as
typedef struct _sHSLPDataPt
{
float fX;
float fZ;
int iI;
float fJ;
}sHSLPDataPt;
typedef struct _sHSLPCalibProfileData
{
unsigned int uiHeader;
unsigned int uiIO;
unsigned int uiProfileID;
unsigned int uiNbValidPts;
sHSLPDataPt sData[1280];
}sHSLPCalibProfileData;
I created analogous managed structures as follows:
[StructLayoutAttribute(LayoutKind::Explicit, Size = 16)]
public value struct S_HSLPDataPt
{
[FieldOffsetAttribute(0)] float fX;
[FieldOffsetAttribute(4)] float fZ;
[FieldOffsetAttribute(8)] int iI;
[FieldOffsetAttribute(12)] float fJ;
};
[StructLayoutAttribute(LayoutKind::Explicit, Size = 16 + 16 * 1280)]
public value struct S_HSLPCalibProfileData
{
[FieldOffsetAttribute(0)] unsigned int uiHeader;
[FieldOffsetAttribute(4)] unsigned int uiIO;
[FieldOffsetAttribute(8)] unsigned int uiProfileID;
[FieldOffsetAttribute(12)] unsigned int uiNbValidPts;
[FieldOffsetAttribute(16)] array<S_HSLPDataPt> ^sData;
};
The intent with the above was to make the footprint of the native and managed structures the same so that I could use a native pointer without issue.
Here is the implementation of the wrapper method:
int SetDumpingBuffer(unsigned int uiSensorIndex, System::IntPtr ipBuf1, System::IntPtr ipBuf2)
{
int ret = 0;
// get native pointer to managed structure
sHSLPCalibProfileData** p1 = static_cast<sHSLPCalibProfileData**>(ipBuf1.ToPointer());
sHSLPCalibProfileData** p2 = static_cast<sHSLPCalibProfileData**>(ipBuf2.ToPointer());
// native call
ret = pHSLP->SetDumpingBuffer(uiSensorIndex, *p1, *p2, sizeof(sHSLPCalibProfileData));
return ret;
}
Here is a contrived snippet of the implementation of the managed app calling the wrapper method:
private void cbDumping_Click(object sender, EventArgs e)
{
int ret;
HSLPLibWrapper.S_HSLPCalibProfileData Buf1 = new HSLPLibWrapper.S_HSLPCalibProfileData();
Buf1.sData = new S_HSLPDataPt[1280];
HSLPLibWrapper.S_HSLPCalibProfileData Buf2 = new HSLPLibWrapper.S_HSLPCalibProfileData();
Buf2.sData = new S_HSLPDataPt[1280];
GCHandle gch1 = GCHandle.Alloc(Buf1, GCHandleType.Weak);
GCHandle gch2 = GCHandle.Alloc(Buf2, GCHandleType.Weak);
IntPtr ip1 = GCHandle.ToIntPtr(gch1);
IntPtr ip2 = GCHandle.ToIntPtr(gch2);
ret = hslp.SetDumpingBuffer(0, ip1, ip2);
gch1.Free();
gch2.Free();
}
I am fairly new to C++/CLI so if there is a better way to accomplish this I am all ears.