将 safearray 的 safearray 从 C++ 中的 VARIANT 编组到 C#

Marshalling safearray of safearray from VARIANT in C++ to C#

我正在尝试从本机库调用 .NET 中的函数,如下所示:

typedef int (WINAPI* TGetAllObjects)(int hModel, VARIANT& objects);

如果在C++客户端中调用,调试器显示对象是VARIANT的安全数组,每个VARIANTBSTR的安全数组。

我尝试在 C# 中实现如下:

public delegate TRetCode TGetAllObjects(int hModel, 
    [Out,In,MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT )] ref MyStruct[] objects);

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
    [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
    public string[] Objects;
}

调用方法时报错:

System.Runtime.InteropServices.SafeArrayTypeMismatchException: "Specified array was not of the expected type."

请告诉我在这种情况下如何正确组织数据编组。

尝试:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate int TGetAllObjects(int hModel, ref object objects); 

并准确查看您收到的内容。 objects 变量是 VARIANT,而不是 SAFEARRAYVARIANT& objects包含SAFEARRAY,它不是一个SAFEARRAY.

然后在你的代码中你可以检查它的类型并转换它。

啊,注意 CallingConventionWINAPIStdCall)。

我做了一些测试,基本的 VARIANT“容器”似乎已正确通过。实话实说,我认为 VARIANT 中的 SAFEARRAYBSTR 中的 SAFEARRAY 一种武器,应该列入日内瓦公约的禁用清单。

更多的测试让我觉得我的解决方案是最简单的。我无法直接 accept/pass object[]string[],收到的 objectsobject[],其中每个元素都是 string[].类似于:

var objects = new object[] { new string[] { "A", "B" }, new string[] { "C", "D" } };

你的“选角”问题

SAFEARRAY 可以具有第一个元素的索引 != 0(这是为了支持 VB 使用基于 1 的数组,数组,其中第一个元素为 1)。 .NET 在“奇怪数组”类别中确实支持这一点。多维数组 (string[1, 2]) 和基于非 0 的数组属于此类,处理起来很麻烦。基于非零的数组更是如此。

根据您的情况转换数组的代码(“转换”的意思是:将其复制到标准的 .NET 数组数组):

object[] castedObjects = (object[])objects;
string[][] strings = new string[castedObjects.Length][];

for (int i = 0; i < castedObjects.Length; i++)
{
    Array ar = (Array)castedObjects[i];
    string[] ar2 = new string[ar.Length];
    Array.Copy(ar, ar2, ar2.Length);
    strings[i] = ar2;
}