带有指向结构参数指针的 C# 本机回调

C# native callback with pointer to struct parameter

我正在为本机库编写 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#中的回调被触发,结构体成员具有预期值。

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

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

如果我将 C# 回调签名更改为使用 IntPtr 而不是 "ref application_request" 参数,然后使用以下代码手动封送它,它就可以工作。

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

但我希望签名尽可能精确,而不必自己使用封送拆收器。

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

您的问题是 structchar* 成员。 C# 封送拆收器假定它负责释放该内存。它通过调用 CoTaskMemFree 来实现。我认为很明显内存根本不会被 C# 代码破坏。

改为将该成员编组为 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; 
}

在您的回调方法中,您可以通过调用 Marshal.PtrToStringAnsi.

来读取字符串的值

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