Thursday, November 12, 2009

WCF use control that require STA thread



My WCF service use WebBrowser .NET control from System.Windows.Forms.

This control requires STA thread:
The WebBrowser class can only be used in threads set to single thread apartment (STA) mode. To use this class, ensure that your Main method is marked with the STAThreadAttribute attribute.

and WCF service is always MTA thread.

to use webBrowser control in my service i found the workaround here:

I just reused this code sample to my needs and it works great!
With this implementation you can run control that requires STA thread in WCF services, that are MTA.

create class:

  1:  [AttributeUsage(AttributeTargets.Method)]
   2:  public class STAOperationBehavior : Attribute, System.ServiceModel.Description.IOperationBehavior
   3:      {
   4:      //- @AddBindingParameters -//
   5:   
   6:      public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
   7:      {
   8:      //+ blank
   9:      }
  10:   
  11:      //- @ApplyClientBehavior -//
  12:      public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
  13:      {
  14:      }
  15:   
  16:      //- @ApplyDispatchBehavior -//
  17:      public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
  18:      {    
  19:          dispatchOperation.Invoker = new STAInvoker(dispatchOperation.Invoker);
  20:      }
  21:   
  22:      //- @Validate -//
  23:      public void Validate(OperationDescription operationDescription)
  24:      {
  25:      //+ blank
  26:      }
  27:  }
  28:   
  29:   
  30:  public class STAInvoker : System.ServiceModel.Dispatcher.IOperationInvoker
  31:  {
  32:      //- $InnerOperationInvoker -//
  33:   
  34:      private IOperationInvoker InnerOperationInvoker { get; set; }
  35:      //+
  36:   
  37:      //- @Ctor -//
  38:      public STAInvoker(IOperationInvoker operationInvoker)
  39:      {
  40:          this.InnerOperationInvoker = operationInvoker;
  41:      }
  42:   
  43:      //+
  44:      //- @AllocateInputs -//
  45:      public Object[] AllocateInputs()
  46:      {
  47:          return InnerOperationInvoker.AllocateInputs();
  48:      }
  49:   
  50:      //- @Invoke -//
  51:      public Object Invoke(Object instance, Object[] inputs, out Object[] outputs)
  52:      {
  53:          Object result = null;
  54:          Object[] staOutputs = null;
  55:   
  56:          System.ServiceModel.OperationContext context = System.ServiceModel.OperationContext.Current;
  57:          System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate
  58:          {
  59:              using (System.ServiceModel.OperationContextScope scope = new System.ServiceModel.OperationContextScope(context))
  60:              {
  61:                  result = InnerOperationInvoker.Invoke(instance, inputs, out staOutputs);
  62:              }
  63:          }));
  64:   
  65:          thread.SetApartmentState(System.Threading.ApartmentState.STA);
  66:          thread.Start();
  67:          thread.Join();
  68:          //+
  69:          outputs = staOutputs;
  70:          //+
  71:          return result;
  72:      }
  73:   
  74:      //- @InvokeBegin -//
  75:      public IAsyncResult InvokeBegin(Object instance, Object[] inputs, AsyncCallback callback, Object state)
  76:      {
  77:          return InnerOperationInvoker.InvokeBegin(instance, inputs, callback, state);
  78:      }
  79:   
  80:      //- @InvokeEnd -//
  81:      public Object InvokeEnd(Object instance, out Object[] outputs, IAsyncResult result)
  82:      {
  83:          return InnerOperationInvoker.InvokeEnd(instance, out outputs, result);
  84:      }
  85:   
  86:      //- @IsSynchronous -//
  87:      public bool IsSynchronous
  88:      {
  89:          get { return InnerOperationInvoker.IsSynchronous; }
  90:      }
  91:  }
  92:   

now decorate you service methode with "STAOperationBehavior" attribute, 
and then it should work with controls that require STA.

now my WCF service works with with BrowserControl!

  1:  [STAOperationBehavior]
  2:  public fResultDO Berechne(StoffRequestDO stoffRequestDO)



1 comment:

  1. Copy and paste. But this shit is not working at all.

    ReplyDelete