0

In a plugin I'm creating in C# with .NET 4.0 for an existing third-party application, I created a thread to have a service listen on a named pipe.

In the function that I set as the OperationContract, I need to to call functions that deal with the UI, which sometimes create new UI controls downstream after they are called. Namely I get the following error:

"Controls created on one thread cannot be parented to a control on a different thread"

I figured out that I should use a BeginInvoke from the OperationContract function to access the UI, but my problem is that I do not know from where I do get the object from which to call. I tried quite a few (this.BeginInvoke, Application.Current.BeginInkove) but have not been able to figure it out, so I'm not able to even compile it.

The code below is the schema of the service that is created from a thread different from the main UI thread. I need to know how to call BeginInvoke to run some code from the main UI thread from the function called OperationContractForService(string value):

using System;
using System.Windows;
using System.Windows.Forms;
using System.ServiceModel;
using System.Threading;

.
.
.

[ServiceContract]
public interface IInterfaceForService
{
    [OperationContract]
    string OperationContractForService(string value);
}

public class InterfaceForService : IInterfaceForService
{

    public string OperationContractForService(string value)
    {
        // .
        // Some code that may create controls in the UI in the main thread
        // How do I call BeginInvoke from here?
        // .
   }

}

How can this be done?

dcastro
  • 59,520
  • 20
  • 126
  • 147
Pep
  • 1,905
  • 2
  • 24
  • 39

2 Answers2

1

If your library's methods are called from the UI thread, you can capture the current context like this:

var UiContext = TaskScheduler.FromCurrentSynchronizationContext();

Then you can launch your thread, and when you want to run code back on the UI thread, create a task and schedule it to run on the captured context:

Task t = new Task(() => 
                      //update UI
                  );

t.Start(uiContext);

Or better yet, create a TaskFactory with the above scheduled and use it to create new tasks whenever you need to update the UI:

TaskFactory tf = new TaskFactory(uiContext);

tf.StartNew(() => { ... });
dcastro
  • 59,520
  • 20
  • 126
  • 147
  • I can pass the uiContext to the Worker class with the DoWork for the theead(which creates the ServiceHost and loops until a varible is set to close the thread, but I do not know how I can pass the uiContext to the InterfaceForService object that is created by the WorkerClass, because I'm not sure how I can access the InterfaceForServiceObject that is created by "new ServiceHost" or "host.AddServicePoint" (I do not know which one creates the InterfaceForService and how I can get a reference to the InterfaceForService object). – Pep Dec 08 '13 at 02:50
  • @Pep So you're using WCF? I believe you need to implement `IInstanceProvider`. This post will help you inject a value into your service implementation: http://stackoverflow.com/questions/2454850/how-do-i-pass-values-to-the-constructor-on-my-wcf-service – dcastro Dec 08 '13 at 08:39
  • Thanks, yes it was the case, I'm using WCF and the service is created by the background thread (and kept alive for the life span of the background thread). – Pep Dec 08 '13 at 12:10
  • Yes it helped. I grabbed the UIContext in the main thread, put it in a static variable of the service class and then started the TaskScheduler with the UI context. Thank you very much, I am a complete noob with C# and was overwhelmed by the complexity (perhaps I also contributed to the complexity by choosing to do named pipes IPC with WCF). – Pep Dec 08 '13 at 23:25
0

You mention you've tried Application.Current.BeginInvoke, but you're missing a bit: it's Application.Current.Dispatcher.BeginInvoke. Does that work?

To be honest, though, I'd keep any knowledge of UI threads well out of your WCF layer. If something called by that OperationContract creates a UI element, let it call onto the UI thread itself.

If that's infeasible, I'd make your InterfaceForService capable of being told which TaskScheduler to dispatch onto, but make some other layer responsible for determining which TaskScheduler corresponds to the UI thread.

canton7
  • 24,246
  • 2
  • 38
  • 53
  • I'm afraid that's not feasible because I'm running plugin that is called at initialization by a third-party application, which owns the UI. At initialization time, I set up a service. Later on, when the service is called, I need to call functions exposed by the third-party application, which in its turn may need to create UI controls on the interface of the third party application (i.e. I do not directly create UI controls, that third-party application API calls may need to create them for me). – Pep Dec 08 '13 at 14:03
  • Aha, fair enough. In that case, you can capture the current SynchronizationContext on initialisation (as per @dcastro's answer), or use Application.Current.Dispatcher.BeginInvoke. – canton7 Dec 08 '13 at 14:05