将指针从非托管代码返回到托管代码

Posted

技术标签:

【中文标题】将指针从非托管代码返回到托管代码【英文标题】:Returning pointers from unmanaged to managed code 【发布时间】:2011-01-21 05:49:12 【问题描述】:

我有一个导出以下函数的非托管 dll:

SomeData* test();

假设 SomeData 为:

typedef struct _Data Data;  
struct _Data  
    int a;  
    int b;  

现在我想从 C# 代码调用这个函数。我开始定义自定义编组所需的 C# 结构,如下所示:

[StructLayout(LayoutKind.Sequential)]  
public class SomeData  
  
    public Int32 a;  
    public Int32 b;  
  

现在,我声明托管函数:

[DllImport("DynamicLibrary.dll", CharSet=CharSet.Auto)]  
[return: MarshalAs(UnmanagedType.LPStruct)]  
public static extern SomeData test();  

在我的主要功能中:

IntPtr ptr = test();  

这样做,我得到 MarchalDirectiveException:“无法封送'返回值':无效的托管/非托管类型组合(Int/UInt 必须与 SysInt 或 SysUInt 配对)。”

我没有在 C# 中为 SomeData 分配内存,因为我希望这个内存是在 C 函数中分配的,我会使用 Marshal.Copy 将它传递给托管内存。

有什么想法吗?谢谢

------------------------ JaredPar 回答后编辑 -------------------- -

事实上,我在处理我的问题的代码时犯了一个错误。我使用的真正托管签名是:

[DllImport("DynamicLibrary.dll", CharSet=CharSet.Auto)] [返回:MarshalAs(UnmanagedType.LPStruct)] 公共静态外部 IntPtr 测试();

JaredPar 的答案仍然相关。为了获得正确的行为,我有 2 个选择:

1) 使用'public static extern IntPtr test();' (没有 MarshalAs 属性)签名,然后像 JaredPar 建议的那样访问返回的指针。

2) 使用'public static extern SomeData test();' (带有 MarshalAs 属性),然后简单地使用 SomeData sd = test();

【问题讨论】:

【参考方案1】:

在声明托管函数时,您需要将指针类型与引用值或IntPtr 值匹配。在这种情况下,LPStruct 修饰符将无济于事。最简单的解决方案是将 test 的返回值转换为 IntPtr 而不是 SomeData,因为本机方法返回一个指针值。然后您可以编写以下包装器

[DllImport("DynamicLibrary.dll", CharSet=CharSet.Auto)]
public static extern IntPtr test();

public static SomeData testWrapper() 
  var ptr = test();
  try 
    return (SomeData)Marshal.PtrToStructure(ptr, typeof(SomeData));
   finally 
    // Free the pointer here if it's allocated memory
  

【讨论】:

正如您在我的编辑中看到的那样,我在写问题时犯了一个错误。您的回答很有帮助并且有效,但我更喜欢使用我的编辑部分的选择 2)。谢谢

以上是关于将指针从非托管代码返回到托管代码的主要内容,如果未能解决你的问题,请参考以下文章

将 HBITMAP 句柄从非托管代码传递到托管代码以创建 System.Drawing.Bitmap 的安全性

无法将结构从非托管库编组到单触引中的托管代码

调试从非托管 C++ 调用的托管 .NET 代码

gcServer 设置未从非托管 exe 传递到托管 dll

从非托管代码更新性能计数器

从非托管 C++ 代码访问 WPF