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

7
我有一个未管理的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异常:“无法编组'return value':无效的托管/非托管类型组合(Int/UInt必须与SysInt或SysUInt配对)。 ”
在C#中,我没有为SomeData分配内存,因为我希望该内存在C函数中分配,然后我将使用Marshal.Copy将其传递到托管内存。
有什么想法吗?谢谢
------------------------ JaredPar回答后编辑 --------------------
实际上,当我将代码复制到我的问题中时,我犯了一个错误。我正在使用的真正托管签名是:
[DllImport("DynamicLibrary.dll", CharSet = CharSet.Auto)] [return:MarshalAs(UnmanagedType.LPStruct)] public static extern IntPtr test();
JaredPar的答案仍然相关。要获得正确的行为,我有两个选择:
1)使用“public static extern IntPtr test();”(不带MarshalAs属性的)签名,然后像JaredPar建议的那样访问返回的指针。
2)使用“public static extern SomeData test();”(带有MarshalAs属性)然后简单地使用SomeData sd = test();
1个回答

10

在声明托管函数时,需要将指针类型与引用值或 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种选择。谢谢。 - Zé Carlos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接