我有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。
此外,静态包装器中委托的静态实例是否会使其免于垃圾回收。
答案 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
,然后使用该上下文进行调用。