且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

通过使用COM调用包装从C#到C结构的数组++

更新时间:2021-07-19 23:09:48

与返回数组的问题是,在C ++中,你会看到一个指向struct,没有关于数组大小的信息。你可以尝试元帅它作为一个SAFEARRAY,但IMO,SAFEARRAYS是在颈部疼痛

The problem with returning the array is that in the C++ you will see a pointer to struct and have no information about array size. You can try to marshal it as a SAFEARRAY, but IMO, SAFEARRAYs are pain in the neck.

我喜欢它建模为这样的:

I prefer to model it as this:

[ComVisible(true)]
[Guid("C3E38106-F303-46d9-9EFB-AD8A8CA8644E")]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MyStruct
{
    public int Value;

    // I marshal strings as arrays! see note at the bottom
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string Unit
}

[ComVisible(true),
Guid("BD4E6810-8E8C-460c-B771-E266B6F9122F"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
]
public interface IMyService
{
    int GetData([MarshalAs(UnmanagedType.LPArray)] out MyStruct[] data);
}



客户端代码是这样的:

The client code is like this:

Lib::MyStruct* data;
long size = svc->GetData(&data);

for(size_t i = 0; i < size; ++i)
{
  Lib::MyStruct& current = data[i];
  long val = current.Value;
  bstr_t unit = current.Unit;
  // ...
}                                           

// now you need to release the memory. However, if you marshal
// strings in struct as BSTRs, you need to first release them by
// calling SysFreeString. This is why I prefer to marshal strings
// as arrays whenever I can: you can still easily construct a bstr_t
// in your client code, but you don't need to release them explicitly
CoTaskMemFree(data);



对于有关 SAFEARRAY 评论小号:他们被要求只有当该接口必须是自动化兼容的,即后期绑定,即一个的IDispatch 接口即标记为 ComInterfaceType.InterfaceIsIDispatch 。如果不是这种情况下(我声明即 ComInterfaceType.InterfaceIsIUnknown 界面自定义),使用标准的数组是完全正常的,他们也同样可以支持作为 SAFEARRAY 秒。此外,自定义的结构的 SAFEARRAY s工作带来一些额外的 复杂性我倾向于避免。如果你不需要后期绑定,没有理由与 SAFEARRAY 秒。

With regard to comment about SAFEARRAYs: they are required only if the interface must be automation compliant i.e. late-bound i.e. an IDispatch interface i.e. marked as ComInterfaceType.InterfaceIsIDispatch. If this is not the case (and I declared the interface as custom i.e. ComInterfaceType.InterfaceIsIUnknown) using the standard arrays is perfectly fine and they are equally well supported as SAFEARRAYs. Furthermore, working with SAFEARRAYs of custom structs brings some additional complexity which I prefer to avoid. If you don't need late binding, there is no reason to fight with SAFEARRAYs.

打关于 CComSafeArray ,如记录,它不支持 VT_RECORD 这是需要支持的结构阵列(另一种选择是与 IRecordInfo 元帅为 VT_VARIANT ,但我甚至不会去成)。

With regard to CComSafeArray, as documented, it doesn't support VT_RECORD which is required to support arrays of structs (another option is to marshal it as VT_VARIANT with IRecordInfo but I won't even go into that).