Ok so as I said, here is a simplifed version of my code ;)
The C# code :
Somewhere, these structures are defined :
public struct ST_SUB_DATA
{
public double dSubValue;
[MarshalAs(UnmanagedType.BStr)]
public string tBSTRSubString;
}
public struct ST_DATA
{
public double dValue;
public string tLPSTRString;
[MarshalAs(UnmanagedType.BStr)]
public string tBSTRString;
public ST_SUB_DATA[] lstSubDataArray;
}
Then here is my COM interface :
public interface MyManagedDLLInterface
{
void MySendFunction(ST_DATA a_stData);
void MyReceiveFunction(out ST_DATA a_rstData);
}
public class MyManagedDLL : MyManagedDLLInterface
{
public void MySendFunction(ST_DATA a_stData)
{
// Do something using the a_stData content
}
public void MyReceiveFunction(out ST_DATA a_rstData)
{
a_rstData = new ST_DATA();
a_rstData.dValue = 5.0;
a_rstData.tLPSTRString = "lpstr value";
a_rstData.tBSTRString = "bstr value";
a_rstData.lstSubDataArray = new ST_SUB_DATA[2];
a_rstData.lstSubDataArray[0].dSubValue = 6.0;
a_rstData.lstSubDataArray[0].tBSTRSubString = "bstr value sub 1";
a_rstData.lstSubDataArray[1].dSubValue = 7.0;
a_rstData.lstSubDataArray[1].tBSTRSubString = "bstr value sub 2";
}
}
The C++ code :
For The C++, the structures are :
struct __declspec(uuid("ed018e6a-a784-312a-9c04-3cd86c5d2b48"))
ST_SUB_DATA
{
double dSubValue;
BSTR tBSTRSubString;
};
struct __declspec(uuid("37491c45-d668-3cfa-bff4-080b5e136355"))
ST_DATA
{
double dValue;
LPSTR tLPSTRString;
BSTR tBSTRString;
SAFEARRAY * lstSubDataArray;
};
Then here is the C++ code (I only show the functions call, not the interface instanciation, etc)
Send function :
void MySendFunction()
{
ST_DATA stData = {};
// Double value
stData.dValue = 5.0;
// LPSTR
stData.tLPSTRString = (LPSTR)CoTaskMemAlloc(12);
strcpy(stData.tLPSTRString, "lpstr value");
// BSTR
stData.tBSTRString = SysAllocString(CString("bstr value"));
// SAFEARRAY
IRecordInfoPtr clDataRecordInfo = NULL;
GetRecordInfoFromGuids(__uuidof(MyManagedDLL::__MyManagedDLL), 1, 0, 0, __uuidof(MyManagedDLL::ST_DATA), &clDataRecordInfo);
if (clDataRecordInfo != NULL)
{
SAFEARRAYBOUND clDataSafeArrayBound;
memset(&clDataSafeArrayBound, 0, sizeof(clDataSafeArrayBound));
clDataSafeArrayBound.cElements = 2;
clDataSafeArrayBound.lLbound = 0;
a_rstData.lstSubDataArray = SafeArrayCreateEx(VT_RECORD, 1, &clDataSafeArrayBound, (PVOID)clDataRecordInfo);
if (a_rstData.lstSubDataArray != NULL)
{
void* pvDataTab = NULL;
ST_DATA* pstDataTab = NULL;
::SafeArrayAccessData(a_rstData.lstSubDataArray, &pvDataTab);
pstDataTab = reinterpret_cast<ST_DATA*>(pvDataTab);
if (pstDataTab != NULL)
{
pstDataTab[0].dSubValue = 6.0;
pstDataTab[0].tBSTRSubString = SysAllocString(CString("bstr value sub 1"));
pstDataTab[1].dSubValue = 7.0;
pstDataTab[1].tBSTRSubString = SysAllocString(CString("bstr value sub 2"));
}
::SafeArrayUnaccessData(a_rstData.lstSubDataArray);
}
}
// Call the C# function
clMyManagedDLLInterface->MySendFunction(stData);
// Free
SysFreeString(stData.tBSTRString);
CoTaskMemFree(stData.tLPSTRString);
SafeArrayDestroy(a_rstData.lstSubDataArray);
}
Receive function :
void MyReceiveFunction()
{
ST_DATA stData = {};
// Initializations (the "NULL" sets are maybe useless...)
stData.dValue = 0.0;
stData.tLPSTRString = NULL;
stData.tBSTRString = NULL;
stData.lstSubDataArray = NULL;
// Call the C# function
clMyManagedDLLInterface->MyReceiveFunction(&stData);
// Do something with the data
// - To get the lpstr values in a char array, I just use a simple strcpy
// - To get the bstr values in a char array, I use a _bstr_t intermediate variable (_bstr_t tBSTRString(stData.tBSTRString); strcpy(acCharArray, tBSTRString))
// - To get the value from the safe array, I use SafeArrayAccessData and SafeArrayUnaccessData
// Free
SysFreeString(stData.tBSTRString);
CoTaskMemFree(stData.tLPSTRString);
SafeArrayDestroy(a_rstData.lstSubDataArray);
}
So, does anybody see any problem in this code?
Many thanks for the help!
Alexis