I need to drop into C++ from C# and bring back a 2D array of a struct. I have everything set up, and if I attach a debugger everything appears to be going right, except my 2D array isn’t marshaling appropriately. If I load it with values before calling the native method, and then view the array from the native side I get lots of “invalid” pointers in my watch window in VS. Then the C++ code goes ahead and loads up the array with values just fine, but during marshaling back to C# I get a memory access violation.
I’d rather not do this as a 1d array.
Here’s my C++ struct and method definition:
struct DoubleStringStruct
{
BSTR Value;
BSTR NumberFormat;
};
HRESULT WINAPI NativeArrayHandler(LONG rMax, LONG cMax, DoubleStringStruct** values)
{
for(LONG rn=1; rn <= rMax; rn++)
{
for (LONG cn = 1; cn <= cMax; cn++)
{
DoubleStringStruct s;
s.Value = _wcsdup(L"Test");
s.NumberFormat = _wcsdup(L"Test");
values[rn][cn] = s;
}
}
return S_OK;
}
and my C# code:
[StructLayout(LayoutKind.Sequential)]
public struct DoubleStringStruct
{
[MarshalAs(UnmanagedType.BStr)]
public string value;
[MarshalAs(UnmanagedType.BStr)]
public string numberFormat;
}
[System.Runtime.InteropServices.DllImport(c_dllName)]
public static extern void NativeArrayHandler(int hMax, int cMax, DoubleStringStruct[,] args);
public void sometMethod()
{
DoubleStringStruct[,] someDSS= new DoubleStringStruct[4,3];
NativeArrayHandler(4, 3, someDSS);
}
Hrm, well Hans Passant helped me get to this answer, so props to him.
There were three problems with my code
1) _wcsdup returns a WCHAR_T * but my struct contains BSTR, which is really a WHCAR *
2) The marshaller doesn’t create a 2d array for us, rather a 1d array which has to be indexed in a funny way. Note below.
3) I need to make sure that any memory I create in the native code gets cleaned up, either by myself or by the Marshaller. For example, just about all the native memory I used in the question never gets freed, resulting in a huge memory leak. Currently when the native code returns to managed code I lose all my native pointers to memory that needs to be freed. I solved this passing a callback when making the call into native code. The native code does its work, makes a functional call back to managed, which returns, and allows the native code to do housekeeping. An easy way to do this housekeeping was to use the power of CComSafeArray’s and CComBSTR’s, which will manage themselves. (I know I should be able to simply pass the CComSafeArray’s to the marshaller and they’ll get cleaned up in the .net code, but I haven’t been able to figure out how to do that).
Unfortunately, the marshaling of a 2D array requires custom marshaling, which results in too many COM calls for my tastes. Consequently, I marshalled a 1D array and indexed it accordingly per Hans Passant’s suggestion. Futhermore, due to time constraints I created an array for each string in
DoubleStringStruct, though I could have madeDoubleStringStructCOMVisible and then I could have marshalled it in a single array.Here’s the final code I ended up with.
And the C#