将安全数组的安全数组从 C++ 中的 VARIANT 编组到 C#

问题描述

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

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

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

我尝试在 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:“指定的数组不是预期的类型。”

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

解决方法

试试:

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

并查看您收到的确切信息。 objects 变量是 VARIANT,而不是 SAFEARRAYVARIANT& objects 包含SAFEARRAY,它不是一个SAFEARRAY

然后在您的代码中,您可以检查其类型并进行转换。

啊,注意 CallingConventionWINAPIStdCall)。

我进行了一些测试,看来基本的 VARIANT “容器”已正确传递。我会说实话,我认为 SAFEARRAYVARIANTSAFEARRAYBSTR 的一种武器,应该被列入日内瓦公约的禁止名单。

更多的测试让我觉得我的解决方案是最简单的。我无法直接接受/传递 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;
}