C#本机回调,带有指向struct参数的指针

时间:2016-10-11 10:57:30

标签: c# c interop pinvoke

我正在为本地库编写C#包装器。它包含这个回调函数:

typedef application_event_result(*application_event_ptr)(application_request* request);

参数定义如下:

typedef struct {
  uint32_t    query;
  const char* client;
  bool        isAuthenticated;
  bool        isGuest;
} application_request;

我已经像这样定义了C#回调委托:

  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  public delegate application_event_result application_event([MarshalAs(UnmanagedType.Struct)]
        ref application_request request);

C#中的结构:

[StructLayout(LayoutKind.Sequential)]
public struct application_request
{
    public UInt32 query;

    [MarshalAs(UnmanagedType.LPStr)]
    public string client;

    [MarshalAs(UnmanagedType.I1)]
    public bool isAuthenticated;

    [MarshalAs(UnmanagedType.I1)]
    public bool isGuest; 
}

所有似乎都能正常工作。触发C#中的回调,结构的成员具有期望值。

但是在返回本机代码时,会触发堆损坏异常(0xc0000374)。

显然,我想避免这种情况。

如果我更改C#回调签名以使用IntPtr而不是" ref application_request"参数,然后使用以下代码手动编组它。

var request = Marshal.PtrToStructure<application_request>(requestptr);

但我希望签名尽可能准确,而不必自己使用Marshaler。

我是否有机会更改回调委托的签名,以便.net可以自动转换结构?

1 个答案:

答案 0 :(得分:1)

您的问题是char*的{​​{1}}成员。 C#封送器假定它负责释放该内存。它通过调用struct来实现。我认为很明显内存并不是要被C#代码破坏。

将该成员改为CoTaskMemFree而不是:

IntPtr

在回调方法中,您可以通过调用[StructLayout(LayoutKind.Sequential)] public struct application_request { public UInt32 query; public IntPtr client; [MarshalAs(UnmanagedType.I1)] public bool isAuthenticated; [MarshalAs(UnmanagedType.I1)] public bool isGuest; } 来读取字符串的值。

通过将成员设为私有并通过属性公开它们来解决此问题。这将允许您封装从指针到字符串的转换。