【问题标题】:Pass struct or string array from C++ to C# using COM使用 COM 将结构或字符串数​​组从 C++ 传递到 C#
【发布时间】:2011-01-05 18:02:29
【问题描述】:

我有一个 COM 组件(在 C# 中),它具有以下接口:

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

现在我想将数据从 C++ 传递到该 COM 对象。 我这样初始化界面:

HRESULT hr = CoInitialize(NULL);
ITest* pTest = NULL;
hr = CoCreateInstance(__uuidof(Test), NULL, CLSCTX_INPROC_SERVER, __uuidof(ITest), (void**)&pTest);

但我不能将我的数组的引用传递给该方法,因为它需要一个 SAFEARRAY* 数组。我能够使用具有固定大小元素(如 double、int、char 数组)的数组创建 SAFEARRAY,并且使用以下方法可以正常工作:

SAFEARRAY* data = SafeArrayCreate(VT_R8, 1, &bound); //8-Byte-Real, 1 dimension

当然,对于我的用户定义结构,没有“VT_something”,所以我不知道如何创建一个 SAFEARRAY 来解决这个问题。我试过 VT_DISPATCH 和 VT_BSTR 都没有成功。

传递数据的实际方法是这样的:

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

...这不起作用(调用 SetStructArray 时出现 Kernel32.dll 中的异常)。

任何想法我错了吗?或者什么会起作用?

谢谢, 马库斯

【问题讨论】:

    标签: c# c++ arrays com struct


    【解决方案1】:

    您的问题似乎与此相同:https://stackoverflow.com/questions/268117/safearray-of-structs

    具体见http://vcfaq.mvps.org/com/4.htm:

    • 通过 C# 生成的类型库将结构导入 C++
    • 调用SafeArrayCreateEx(VT_RECORD)SafeArrayAccessDataSafeArrayUnaccessData 来填充数组

    【讨论】:

    • 这看起来很有希望。不幸的是,我从一个问题转到下一个问题:要使用 SafeArrayCreateEx(),我需要使用 GetRecordInfoFromGuids() 来获取 IRecordingInfo。为此,我需要一些我没有的 IDL 文件。似乎通常 .tlb 是从 IDL 文件创建的,但是 VS2005 C# 编译器在没有 IDL 文件的情况下创建 tlb 文件...我会继续尝试...
    • @Markus 您需要 IDL 来做什么——GUID?启动 OleView(打开 Visual Studio 命令提示符并键入 oleview),然后将 .tlb 加载到其中。
    • 谢谢 - 我从注册表中提取了 GUID,但是使用该工具当然要容易得多。我在获取 IRecordInfo 时遇到了问题(实际上让它接受了我的 GUID),但我终于设法解决了所有这些问题——而且效果很好。您发布的链接很有帮助!
    【解决方案2】:

    COM 对结构的支持很差。至少您需要使用 IRecordInfo 来发现结构的布局,即 SafeArrayCreateEx() 的第四个参数。实际上不确定CLR是否支持这一点。或者如何获取IRecordInfo接口指针。

    否则,您的 C++ 代码有正确的想法。不要使用结构,使用 [ComVisible] 类。结构成员可以只是属性。

    【讨论】:

    • 谢谢你,汉斯。获取 IRecordInfo 并不像我想象的那么容易,但它终于奏效了,所以我现在可以使用 SafeArrayCreateEx 并最终传输我的数据。
    猜你喜欢
    • 2011-09-09
    • 2012-09-12
    • 2015-12-23
    • 2011-02-11
    • 1970-01-01
    • 2020-09-11
    • 1970-01-01
    • 1970-01-01
    • 2011-06-01
    相关资源
    最近更新 更多