首页 > 解决方案 > 将安全数组的安全数组从 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:“指定的数组不是预期的类型。”

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

标签: c#c++nativemarshalling

解决方案


尝试 :

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

看看你收到了什么。objects变量是 a ,而VARIANT不是 a SAFEARRAYVARIANT& objects 包含SAFEARRAY不是SAFEARRAY。_ _

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

啊,注意CallingConventionWINAPIStdCall)。

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

更多的测试让我认为我的解决方案是最简单的。我无法直接接受/传递 aobject[]或 a string[],并且收到objects的是 a object[],其中每个元素都是 a 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;
}

推荐阅读