3

Sorry for long title, I don't know even the way on how to express the question

I'm using a library which run a callback from a different context from the main thread (is a C Library), I created the callback in C# and when gets called I would like to just raise an event.

However because I don't know what will be inside the event, I would like to find a way to invoke the method without the problem of locks and so on (otherwise the third party user will have to handle this inside the event, very ugly)

Are there any way to do this? I can be totally on the wrong way but I'm thinking about winforms way to handle different threads (the .Invoke thing)

Otherwise I can send a message to the message loop of the window, but I don't know a lot about message passing and if I can send "custom" messages like this

Example:

private uint lgLcdOnConfigureCB(int connection, System.IntPtr pContext)
{
    OnConfigure(EventArgs.Empty);
    return 0U;
}

this callback is called from another program which I don't have control over, I would like to run OnConfigure method in the main thread (the one that handles my winform), how to do it? Or in other words, I would like to run OnConfigure without the need of thinking about locks

Edit 1:

I have a problem with this exception:

CallbackOnCollectedDelegate retrived Message: Callback run on delegate 'G19dotNet!G19dotNet.LgLcd+lgLcdOnSoftButtonsCB::Invoke' collected in GarbageCollector. During unmanaged code delegates should be ensured will never be deleted until you are sure they will never be called

Edit 2:

Issue resolved by myself, thanks to Stackoverflow which always helps me! For future reference: Defining a delegate as a function pointer

Community
  • 1
  • 1
Francesco Belladonna
  • 11,361
  • 12
  • 77
  • 147

4 Answers4

7

If you're using WinForms and you want to execute something on the UI thread, you need to call either Invoke or BeginInvoke on some control (be it a Button or a Form or whatever) that was created on that thread. You'll need a reference to it in order to do this.

For example, with your code and assuming that you have a reference to a form called form:

private uint lgLcdOnConfigureCB(int connection, System.IntPtr pContext)
{
    form.Invoke(new MethodInvoker(() => OnConfigure(EventArgs.Empty)));
    return 0U;
}
Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
3

Before you call the 3rd party function, get a reference to Dispatcher.CurrentDispatcher. In the callback function, use dispatcher.Invoke.

What you end up with will look something like this:

class MyClass
{
    private Dispatcher dispatcher;
    public void runThirdParty()
    {
        this.dispatcher = Dispatcher.CurrentDispatcher;
        callThirdPartyFunction(myCallBack);
    }

    public void myCallBack()
    {
        this.dispatcher.Invoke(new Action(() =>
        {
            //code to run here.
        }));
    }
 }
Greg Sansom
  • 20,442
  • 6
  • 58
  • 76
  • Dispatcher is only for WPF; he seems to be using WinForms. – Adam Robinson Jan 05 '11 at 01:19
  • 1
    @Adam Robinson: Dispatcher is not just for WPF - it will also work in Console Application or WinForms. Just add a reference to System.Windows.Threading. – Greg Sansom Jan 05 '11 at 01:26
  • @Greg: Dispatcher was not designed to work with WinForms, even though it can. However, you cannot use it in the manner above. `Dispatcher.CurrentDispatcher` will only return the proper instance when *that call is run on the UI thread*. In order to use it, you'll have to retrieve and store the value for `Dispatcher.CurrentDispatcher` within code that's executing on the UI thread. It's much simpler to use the correct thread synchronization mechanism, `Control.Invoke` and `Control.BeginInvoke`. – Adam Robinson Jan 05 '11 at 01:35
  • I know it's stupid but... I can't find System.Windows.Threading (WindowsBase.dll), what should I reference for this namespace? I searched for it a lot but I can't find it. I'm using .NET 4.0. **After Adam Robinson edit:** Thanks I'm checking it – Francesco Belladonna Jan 05 '11 at 01:35
  • 1
    @Adam: You are right, but I answered the question before the OP edited and WinForms. @Fire-Dragon-DoL: Use Adams suggestion, it is better suited to WinForms. I will delete this answer. – Greg Sansom Jan 05 '11 at 01:39
  • @Adam Robinson: Thanks, however I'm inside a Component and not a control... I'm searching for a way to "obtain" the control which I added it too, I'm sure there is something like this. @Greg Sansom: No problem, thanks for interest – Francesco Belladonna Jan 05 '11 at 01:49
  • @Adam Robinson: Solved the last problem with this: http://stackoverflow.com/questions/371464/get-components-parent-form/371829#371829 Thanks for everything – Francesco Belladonna Jan 05 '11 at 02:15
  • @Fire-Dragon-DoL: Out of curiosity, why accept this answer instead of mine, which is applicable to the solution that you found? – Adam Robinson Jan 05 '11 at 02:26
  • I tried to delete it but I can't because it is accepted. Moving on :) – Greg Sansom Jan 05 '11 at 02:39
3

There is a pattern for this called Event-based Asynchronous Pattern. The article linked is a great overview on how to use it. The AsyncOperation class is the key to this pattern.

This pattern might not fit perfectly with your problem that you are trying to solve, but it might give you some insights into the problem.

Brian
  • 37,399
  • 24
  • 94
  • 109
0

Thanks to Adam Robinson answer, I added a nice little utility function on my form:

   private void runOnUIThread(Action function)
   {
       this.Invoke(new MethodInvoker(function));
   }

And I'm using it like this:

   runOnUIThread(() =>
   {
       example_lbl_status.Text = "Active";
   });
Zohar
  • 1,820
  • 1
  • 18
  • 16