将 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
的安全数组,每个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: "Specified array was not of the expected type."
请告诉我在这种情况下如何正确组织数据编组。
尝试:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate int TGetAllObjects(int hModel, ref object objects);
并准确查看您收到的内容。 objects
变量是 VARIANT
,而不是 SAFEARRAY
。 VARIANT& objects
包含SAFEARRAY
,它不是一个SAFEARRAY
.
然后在你的代码中你可以检查它的类型并转换它。
啊,注意 CallingConvention
(WINAPI
是 StdCall
)。
我做了一些测试,基本的 VARIANT
“容器”似乎已正确通过。实话实说,我认为 VARIANT
中的 SAFEARRAY
是 BSTR
中的 SAFEARRAY
一种武器,应该列入日内瓦公约的禁用清单。
更多的测试让我觉得我的解决方案是最简单的。我无法直接 accept/pass object[]
或 string[]
,收到的 objects
是 object[]
,其中每个元素都是 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;
}
我正在尝试从本机库调用 .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: "Specified array was not of the expected type."
请告诉我在这种情况下如何正确组织数据编组。
尝试:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate int TGetAllObjects(int hModel, ref object objects);
并准确查看您收到的内容。 objects
变量是 VARIANT
,而不是 SAFEARRAY
。 VARIANT& objects
包含SAFEARRAY
,它不是一个SAFEARRAY
.
然后在你的代码中你可以检查它的类型并转换它。
啊,注意 CallingConvention
(WINAPI
是 StdCall
)。
我做了一些测试,基本的 VARIANT
“容器”似乎已正确通过。实话实说,我认为 VARIANT
中的 SAFEARRAY
是 BSTR
中的 SAFEARRAY
一种武器,应该列入日内瓦公约的禁用清单。
更多的测试让我觉得我的解决方案是最简单的。我无法直接 accept/pass object[]
或 string[]
,收到的 objects
是 object[]
,其中每个元素都是 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;
}