1

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 values
        }

        // 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

2 Answers 2

2

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 and SafeArrayUnaccessData to populate the array
Sign up to request clarification or add additional context in comments.

3 Comments

That looks promising. Unfortunately I get from one problem to the next: to use SafeArrayCreateEx() I need to use GetRecordInfoFromGuids() to get IRecordingInfo. And for that I need some IDL file that I don't have. It seems that usually the .tlb is created from the IDL file, but the VS2005 C# compiler creates the tlb file without an IDL file... I'll keep on trying...
@Markus What do you need the IDL for -- the GUID? Start OleView (open a Visual Studio Command Prompt and type oleview), then load the .tlb into it.
Thanks - I extracted the GUID from the registry, but with that tool it is much easier of course. I had problems getting the IRecordInfo (and there actually getting it to accept my GUID), but I finally managed to solve all this - and it works fine. The link you posted was very helpful!
1

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.

1 Comment

Thank you, Hans. Getting IRecordInfo was not as easy as I thought, but it finally worked, so I can use SafeArrayCreateEx now and finally transfer my data.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.