来自 C DLL 回调的 C# 跨线程 UI 操作

C# cross thread UI operation from C DLL callback

我有 C 风格的 Dll,我必须在 C# 中使用它。我为图书馆创建了一个 Pinvoke 包装器。一切正常,但其中一个 C 函数接受函数指针(回调)。因此,在我的 static class 的 C# 包装器中,我添加了以下代码:

[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# 表单中 class,

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 线程中的 运行 回调函数。我应该在包装器中进行哪些更改才能实现此目的。

我想保持表单 class 干净并使包装器 class 易于最终用户使用。因此,如果需要复杂的代码来处理这种情况,我想在包装器中处理它,以便最终用户可以轻松使用我的 dll。

此外,我的静态包装器中委托的静态实例是否会阻止垃圾回收。

您可以(大部分)检测回调是否转到 GUI 代码,并从与 UI 代码关联的线程自动执行它。

public static int SetMyCallback(uint handle, CallbackType callback)
{
    var callbackB = callback;
    var syncTarget = callback.Target as System.Windows.Forms.Control;
    if (syncTarget != null)
        callbackB = (v) => syncTarget.Invoke(callback, new object[] { v });
    return SetCallback(handle, callbackB);
}

另一种选择是根据用于调用 SetMyCallback 的线程创建一个 SynchronizationContext,然后使用该上下文进行调用。