简体   繁体   中英

C# cross thread UI operation from C DLL callback

I have C style Dll, which I have to use in C#. I have created a Pinvoke wrapper for the library. Everything is working fine, but one of the C functions accepts a function pointer (Callback). So, in my C# wrapper which is a static class I added following code:

[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);
}

Now, in my C# Form 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);
    ...
}

The moment my callback is invoked, i get an error:

Cross-thread operation not valid

I want the Callback function to run in the UI thread. What changes should I make in my wrapper to achieve this.

I want to keep the Form class clean and wrapper class easy to use for end users. So, if complex code is required to handle this situation I would like to handle that in the wrapper so that end user can easily use my dll.

Also, will the static instance of delegate inside my static wrapper keep it from garbage collection.

You can (mostly) detect whether the callback is going to GUI code, and automatically perform it from the thread associated with the UI code.

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);
}

Another option would be to create a SynchronizationContext based on the thread used to call SetMyCallback , and then make the call using that context.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM