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

本文关键字:本机 回调 指针 参数 结构 | 更新日期: 2023-09-27 18:12:44

我正在为一个本机库编写一个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可以自动转换结构?

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

您的问题是struct中的char*成员。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来读取字符串的值。

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