【发布时间】:2016-01-18 04:45:09
【问题描述】:
我有 C 风格的 Dll,我必须在 C# 中使用它。我为该库创建了一个 Pinvoke 包装器。一切正常,但其中一个 C 函数接受函数指针(回调)。因此,在我的 C# 包装器中,它是一个 static 类,我添加了以下代码:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void CallbackType(uint status);
/* To prevent garbage collection of delegate, need to keep a reference */
static CallbackType privCallback;
[DllImport(DLL_FILE_NAME, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern int SetCallback(uint handle, CallbackType callback);
public static int SetMyCallback(uint handle, CallbackType callback)
{
return SetCallback(handle, callback);
}
现在,在我的 C# Form 类中,
private void RefreshCallback(uint status)
{
this.statusLabel.Text = "Status Code: "+ status.toString();
}
private void myBtn_Click(object sender, EventArgs e)
{
int status, handle = 0;
...
status = MyDllWrapper.SetMyCallback(handle, RefreshCallback);
...
}
我的回调被调用的那一刻,我得到一个错误:
Cross-thread operation not valid
我希望回调函数在 UI 线程中运行。为了实现这一点,我应该在包装器中进行哪些更改。
我想让 Form 类保持干净,并且包装类易于最终用户使用。因此,如果需要复杂的代码来处理这种情况,我想在包装器中处理它,以便最终用户可以轻松使用我的 dll。
另外,我的静态包装器中的静态委托实例是否会阻止它进行垃圾回收。
【问题讨论】:
-
您需要与托管回调完全相同的代码才能访问主线程 Invoke/InvokeRequired 上的 UI(您显然知道) - 请说明为什么这还不够/您正在寻找什么。
-
@AlexeiLevenkov 包装内的一些事件委托系统,函数可以订阅
-
我仍然不明白这是如何特定于本机互操作的。
RefreshCallback如果在非 UI 线程上调用,无论是从本机代码还是托管代码调用,都会遇到完全相同的问题。您可以强制回调以 Ben Voigt suggested 的身份在 UI 线程上运行,但这会大大限制编写回调的灵活性(因为几乎每个人都已经知道如何处理在非 UI 线程上调用代码的情况 - stackoverflow.com/questions/5868783/…)) -
@AlexeiLevenkov 我有 Linux 背景(C/C++),没有太多使用 C#。不知道是不是太明显了。
标签: c# c++ c multithreading pinvoke