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

Posted

技术标签:

【中文标题】将安全数组的安全数组从 C++ 中的 VARIANT 编组到 C#【英文标题】:Marshalling safearray of safearray from VARIANT in C++ to C# 【发布时间】:2021-02-02 14:34:52 【问题描述】:

我正在尝试从本地库调用 .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:“指定的数组不是预期的类型。”

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

【问题讨论】:

听起来你有一个字符串数组?也许你需要[Out,In,MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT )] object[][] objects 不要认为你应该使用ref @Charlieface 我试过了。抛出异常 System.Runtime.InteropServices.MarshalDirectiveException: "Cannot marshal 'parameter #2': There is no marshaling support for nested arrays." 好的,object[] 呢? @Charlieface 没有发生错误。但是传递的数组仍然是空的。 【参考方案1】:

试试:

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

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

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

啊,注意CallingConventionWINAPIStdCall)。

我进行了一些测试,似乎基本的VARIANT“容器”已正确通过。实话实说,我认为VARIANT 中的SAFEARRAYBSTR 中的SAFEARRAYs 是应该列入日内瓦公约禁止名单的武器。

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

【讨论】:

StdCall 是默认值,因此该属性不是必需的。 谢谢。有效。如何获取内部数组?转换为字符串 [] 不起作用(屏幕截图:prnt.sc/y1jha8 了解)您需要强制转换为 Array。感谢您的帮助! @Egorosh 你有一个object[]。这与string[] 不同。每个元素都是一个string[]。这和我最后给出的例子一模一样。您可以简单地使用for (int i = 0; i &lt; castcastObjects.Length; i++) string[] temp = (string[])castcastObjects[i]; 提取string[],也可以使用var strings = Array.ConvertAll((object[])objects, x =&gt; (string[])x) @Egorosh 我已经稍微简化了复制代码。你可以使用Array.Copy

以上是关于将安全数组的安全数组从 C++ 中的 VARIANT 编组到 C#的主要内容,如果未能解决你的问题,请参考以下文章

C++:如何安全地释放堆分配的向量数组?

在堆上“分解” c++ 数组是不是安全?

C++ COM [in, out] 安全数组

在托管代码和非托管代码之间传递非托管结构的安全数组

GPT 2 - TypeError:无法根据规则“安全”将数组数据从 dtype('O') 转换为 dtype('int64')

如何将 c++ 变量安全地保存在 RAM 中?