[英]P/Invoke - Marshaling delegate as function pointer + void*
C 中一個相當常見的習慣用法是使用多態閉包將其表示為兩個參數,一個函數指針和 void 指針(作為參數之一傳遞給函數指針。
取自GPGME庫的示例:
typedef gpgme_error_t (*gpgme_passphrase_cb_t) (void *hook,
const char *uid_hint,
const char *passphrase_info,
int prev_was_bad,
int fd);
void gpgme_set_passphrase_cb (gpgme_ctx_t ctx,
gpgme_passphrase_cb_t cb,
void *hook_value);
從概念上講,函數指針加上 void 指針表示與 C# 中的委托(閉包)相同的事物。 在進行這種 P/Invoke 調用時,是否有一種很好的規范方式來編組委托?
在進行這種 P/Invoke 調用時,是否有一種很好的規范方式來編組委托?
您不需要使用void*
參數,因為 C# 委托是一個閉包。 傳遞IntPtr.Zero
作為鈎子值。 您的 C# 委托仍然需要接受void*
參數,但它可以簡單地忽略它,因為它不需要它。
您實際上可以將委托從 C# 傳遞到 C 函數指針。 您應該使用[UnmanagedFunctionPointer]
屬性裝飾此委托。 這就是我們如何包裝一個帶有函數指針的 C 方法:
C方法:
__declspec(dllexport) globle int EnvAddRouterEx(int (*queryFunction)(void*, char*))
P\\Invoke 方法:
[DllImport(clipsDllLocation, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern int EnvAddRouterEx(ClipsRouterQueryFunctionDelegate queryFunction);
P\\Invoke 委托:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int ClipsRouterQueryFunctionDelegate(IntPtr theEnv, string logicalName);
其他答案是正確的,但是,我只想附加一些 C#9 信息:
現在可以在 C# 中使用函數指針如下(需要啟用unsafe
):
static int add(int a, int b) => a + b;
delegate*<int, int, int> pointer = &add;
int result = add(40, 2);
上面代碼片段中的函數指針在內部被處理為nint
(或IntPtr
)並且可以在 P/Invoke 聲明中使用:
[DllImport("my_library.dll")]
static extern int CallMyCallback(delegate*<int, int, int> callback, int arg1, int arg2);
my_library.dll
的示例 C++ 實現可以是:
extern "C" __declspec(dllexport) int CallMyCallback(
int (*callback)(int, int),
int arg1,
int arg2)
{
return callback(arg1, arg2);
}
CallMyCallback
可以按如下方式使用(來自 C#):
static int multiply(int a, int b) => a * b;
int result = CallMyCallback(&multiply, -12, 7);
注意:此語言功能不能與 lambda 表達式或非靜態方法一起使用(實例方法需要傳遞隱式this
指針)。
官方文檔尚不存在,但您可以查看 C#9 GitHub issue/tracker: https : //github.com/dotnet/csharplang/issues/191
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.