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

时间:2015-10-19 18:13:12

标签: c# c++ c multithreading pinvoke

我有C风格的Dll,我必须在C#中使用它。我为库创建了一个Pinvoke包装器。一切正常,但其中一个C函数接受一个函数指针(Callback)。因此,在我的C#wrapper中,这是一个 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

我希望Callback函数在UI线程中运行。我应该在包装器中进行哪些更改才能实现此目的。

我想保持Form类的清洁和包装类易于最终用户使用。因此,如果需要复杂的代码来处理这种情况,我想在包装器中处理它,以便最终用户可以轻松使用我的dll。

此外,静态包装器中委托的静态实例是否会使其免于垃圾回收。

1 个答案:

答案 0 :(得分:2)

您可以(主要)检测回调是否转到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);
}

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