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

Posted

技术标签:

【中文标题】在托管代码和非托管代码之间传递非托管结构的安全数组【英文标题】:Passing safearray of unmanaged structs between managed and unmanaged code 【发布时间】:2014-01-03 13:36:14 【问题描述】:

我在非托管库 SomeLibrary 的 IDL 中定义了一个结构 MyStructure。我需要从非托管库 (C++) 调用托管代码 (C#) MyManagedLibPtr->RetrieveStuff 来填充和检索这些结构的数组以供调用者使用。问题是我无法在托管端弄清楚 RetrieveStuff 的签名。我想需要一些自定义编组?这是我所拥有的:

“SomeLibrary”的 IDL:

[
uuid(xxxxx-xxxx-xxxx-xxxx-xxxxx)
]
struct MyStructure

  [helpstring("Some string values")] SAFEARRAY(BSTR) moValues;   
  [helpstring("Some other value")] BSTR moValue; 
;

非托管代码(调用者):

SAFEARRAY* saArray = NULL;
MyManagedLibPtr->RetrieveStuff(&saArray);  // <--This is the key part

// The rest is just parsing the results.
// Using SafeArray -wrapper class that handles access/unaccess/etc..
SafeArray<SomeLibrary::MyStructure, VT_RECORD> oResults(saArray); 
for (int i =0; i < oResults.GetSize(0); i++)

  SomeLibrary::MyStructure oStruct = oResults[i];
  // Etc......

在 C# 方面,我尝试了几种不同的解决方案,但没有一个是正确的。这个应该是最甜的,但显然编组自动化还不够甜:

// Interface
[DispId(123)]
void RetrieveStuff(ref SomeLibrary.MyStructure[] roResultArray);

我得到的错误是一些 HRESULT 代码。没有特别检查哪一个,但显然是由不正确的签名或编组引起的。有什么帮助吗?

【问题讨论】:

VT_RECORD 对于 .NET/COM 互操作来说不是一个好主意。在这里查看之前的讨论:***.com/questions/16994495/… 是的。 C++ 代码也是错误的,必须使用 IRecordInfo。将结构更改为具有两个属性的接口,所有问题都消失了。 你们俩可能都是对的,但是在我的应用程序(大约 1,000,000 行代码)中,将常用结构更改为其他东西感觉不太好。因此,即使这不是最好的主意,我仍然想知道上述问题是否可以通过自定义编组来解决。 您是否尝试过在参数上使用 MarshalAs 属性?还是将 TLB 导入到 C# 代码中? @Medinoc "SomeLibrary" 在托管端被正确引用,因为我可以在双方传递和使用 SomeLibrary.MyStructure。问题是传递这些结构的(安全)数组。我已经尝试了 MarshalAs 属性的一些不同变体,但还不能正确使用它。完全不确定,但我想我需要“MarshalAs SafeArray”并使用“ref object”或“ref Array”作为参数类型。但我只是在猜测......这就是我在这里要问的。 【参考方案1】:

所以,我找到了一个几乎符合要求的答案。无论出于何种原因,我无法使用 ref 参数使其工作,但我能够使用 out 进行编组。所以这不是问题的 100% 答案,但该死的接近它,并且可能也会帮助其他人。

托管端正确的接口声明:

// Interface
[DispId(123)]
void RetrieveStuff([Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_RECORD)] out SomeLibrary.MyStructure[] roResultArray);

【讨论】:

以上是关于在托管代码和非托管代码之间传递非托管结构的安全数组的主要内容,如果未能解决你的问题,请参考以下文章

将 BSTR 字符串作为托管代码和非托管代码之间的边界传递(COM 互操作)

在 C# 中传递 IntPtr 指针后,在非托管 C++ 代码中分配数组,编组类型

在 C# 和非托管 C++ 之间传递自定义对象

托管库和非托管库之间有啥区别?

P/Invoke 编组和解组 C# 和非托管 DLL 之间的二维数组、结构和指针

在托管代码中填充非托管数组