I have a COM component (in C#) that has the following interface:
namespace InterOp
{
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
[MarshalAs(UnmanagedType.BStr)]
public string name;
[MarshalAs(UnmanagedType.BStr)]
public string surname;
public int age;
}
public interface ITest
{
void SetStringArray(string[] array);
void SetStructArray(MyStruct[] array);
}
public class Test : ITest
{
// string arrays
public void SetStringArray(string[] array)
{
for(int i = 0; i < array.Length; i++)
MessageBox.Show(array[i]); // just do something with the array v开发者_运维技巧alues
}
// struct arrays
public void SetStructArray(MyStruct[] array)
{
for (int i = 0; i < array.Length; i++)
MessageBox.Show(array[i].name + ", " + array[i].surname + " (" + array[i].age.ToString() + ")");// just do something with the array values
}
}
}
Now I want to pass data from C++ to that COM object. I initialized the interface like this:
HRESULT hr = CoInitialize(NULL);
ITest* pTest = NULL;
hr = CoCreateInstance(__uuidof(Test), NULL, CLSCTX_INPROC_SERVER, __uuidof(ITest), (void**)&pTest);
But I can't pass a reference to my array to that method, as it requires a SAFEARRAY* array. I was able to create SAFEARRAYs with arrays with fixed size elements like double, int, char arrays, and it works fine using something like this:
SAFEARRAY* data = SafeArrayCreate(VT_R8, 1, &bound); //8-Byte-Real, 1 dimension
Of course, for my user-defined struct there is no "VT_something", so I don't know how to create a SAFEARRAY to solve this. I tried VT_DISPATCH and VT_BSTR without success.
The actual method to pass the data is this one:
bool SetStructArrayEx(MyInterOp::MyStruct* array, int arraySize, ITest* comObjectInterface)
{
bool retVal = false;
// Create the safearray environment
SAFEARRAYBOUND bound;
bound.lLbound = 0;
bound.cElements = arraySize;
// Init the safearray
SAFEARRAY* data = SafeArrayCreate(VT_DISPATCH, 1, &bound);
// access the safearray data and copy the original data to it
MyInterOp::MyStruct HUGEP* temp;
HRESULT hr = SafeArrayAccessData(data, (void HUGEP* FAR*)&temp);
if(SUCCEEDED(hr))
{
// finally copy the data
for(int i=0; i<arraySize; ++i)
*temp++ = array[i];
comObjectInterface->SetStructArray(data);
SafeArrayUnaccessData(data);
retVal = true;
}
SafeArrayDestroy(data);
return retVal;
}
...and this does not work (Exception in Kernel32.dll when calling SetStructArray
).
Any ideas where I'm wrong? Or what would work?
Thanks, Markus
It looks like your question is the same as this: https://stackoverflow.com/questions/268117/safearray-of-structs
Specifically, see http://vcfaq.mvps.org/com/4.htm:
- Import the struct into C++ via the type library generated from C#
- Call
SafeArrayCreateEx(VT_RECORD)
,SafeArrayAccessData
andSafeArrayUnaccessData
to populate the array
COM has very poor support for structures. At a minimum you need to use IRecordInfo to discover the layout of the structure, the 4th argument to SafeArrayCreateEx(). Not actually sure if the CLR supports this. Or how you obtain the IRecordInfo interface pointer.
Your C++ code otherwise has the right idea. Don't use a struct, use a [ComVisible] class. The struct members can just be properties.
精彩评论