Scott Seely

Sponsors

The Lounge

Wicked Cool Jobs

Syndication

News

  • INETA Community Champions
Calling an STA COM Object from a WCF Operation

Note-- This code in this post has been updated on 4-3-2010 to include updates to handle exceptions.

One of the things that many people are still doing is making use of old COM objects that run in STA (single threaded apartment) threads. Back in October, 2006, Jeff Prosise wrote how to do this from ASMX. Not too long after that, I had a chance to teach for Wintellect and Jeff asked me to show him how to do the same in WCF. That information went up on a post for a consulting company that didn’t make it through the latest recession. For better or worse, that post was referenced a fair number of times and now, folks are writing to me, asking for the code again.

In general, any time you receive a message via WCF, the message itself will be processed on a MTA (multi-threaded apartment) thread. Normally, this is just fine. Sometimes, you might be calling out to a COM object. COM objects will not run if the threading model the COM object needs differs from the threading model of the calling thread. MTA COM objects work just fine. But, if you have a bunch of STA COM objects (typically produced by Visual Basic but sometimes coming from C++ applications or other utilities) that you use in your WCF service, you have a problem. To allow things to work, the method needs to be invoked on an STA thread.

In .NET, one creates an STA thread by setting the apartment state of the thread prior to Start()-ing the thread.

thread.SetApartmentState(ApartmentState.STA);

thread.Start();

Now, how do we make this happen in WCF? We need to wrap the invocation of the actual method! For this, we resort to an IOperationBehavior. One typically applies a behavior (IServiceBehavior, IContractBehavior, IOperationBehavior) via an attribute. The only exception to this is an IEndpointBehavior, which is applied via manipulation of the EndpointDescription or in the app|web.config file. The IOperationBehavior exposes four methods:

  1. AddBindingParameters(OperationDescription, BindingParameterCollection): In our case, we will just do nothing with this info.
  2. ApplyClientBehavior(OperationDescription, ClientOperation): Only called when the contract is used on a client. We will  do nothing here since we only care about the service implementation.
  3. ApplyDispatchBehavior(OperationDescription, DispatchOperation): Only called when the contract is used on the server. We will do our work here and override the IOperationInvoker on the DispatchOperation so that our operation is invoked on an STA thread.
  4. Validate(OperationDescription): Used to make sure that everything is OK before applying the behavior. Normally, one throws an exception from this method if the environment isn’t right in some way. Our implementation will throw if the method we are calling is being executed asynchronously. If you need an async version of the attribute, that work is left as an exercise for you, dear reader.

Our IOperationBehavior then looks like this:

public class STAOperationBehaviorAttribute: Attribute, IOperationBehavior

{

    public void AddBindingParameters(OperationDescription operationDescription,

      System.ServiceModel.Channels.BindingParameterCollection bindingParameters)

    {

    }

 

    public void ApplyClientBehavior(OperationDescription operationDescription,

      System.ServiceModel.Dispatcher.ClientOperation clientOperation)

    {

        // If this is applied on the client, well, it just doesn't make sense.

        // Don't throw in case this attribute was applied on the contract

        // instead of the implementation.

    }

 

    public void ApplyDispatchBehavior(OperationDescription operationDescription,

      System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation)

    {

        // Change the IOperationInvoker for this operation.

        dispatchOperation.Invoker = new STAOperationInvoker(dispatchOperation.Invoker);

    }

 

    public void Validate(OperationDescription operationDescription)

    {

        if (operationDescription.SyncMethod == null)

        {

            throw new InvalidOperationException("The STAOperationBehaviorAttribute " +

                "only works for synchronous method invocations.");

        }

    }

}

So far, so good. Now, we need to write that IOperationInvoker. You’ll note from the ApplyDispatchBehavior above that our STAOperationInvoker takes an IOperationInvoker in the constructor. This is done because, in general, the IOperationInvoker WCF gives us does everything right. It just needs to do its thing on an STA thread. Our implementation will delegate as much work as possible to the provided IOperationInvoker. This is a pattern you will follow in most WCF extensions as most extensions require a slight tweak to existing behavior. We do just that, except for our implementation of Invoke. In that, we will setup an STA thread and then call the contained IOperationInvoker’s Invoke method from the STA thread.

public class STAOperationInvoker : IOperationInvoker

{

    IOperationInvoker _innerInvoker;

    public STAOperationInvoker(IOperationInvoker invoker){

        _innerInvoker = invoker;

    }

 

    public object[] AllocateInputs()

    {

        return _innerInvoker.AllocateInputs();

    }

 

 

public object Invoke(object instance, object[] inputs, out object[] outputs)
{
    // Create a new, STA thread
    object[] staOutputs = null;
    object retval = null;
    STAException lastException = null;
    Thread thread = new Thread(
        delegate(){
            try
            {
                retval = _innerInvoker.Invoke(instance, inputs, out staOutputs);
            }
            catch (Exception ex)
            {
                lastException = new STAException(ex);
            }
        });
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
    try
    {
        thread.Join();
        if (lastException != null)
        {
            throw lastException;
        }
    }
    catch(Exception e)
    {
        Debug.WriteLine(e.ToString());
        throw;
    }
    outputs = staOutputs;
    return retval;
}

    public IAsyncResult InvokeBegin(object instance, object[] inputs,

      AsyncCallback callback, object state)

    {

        // We don't handle async...

        throw new NotImplementedException();

    }

 

    public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)

    {

        // We don't handle async...

        throw new NotImplementedException();

    }

 

    public bool IsSynchronous

    {

        get { return true; }

    }

}

To test, I wrote the following Service Contract:

[ServiceContract]

public interface ITestService

{

    [OperationContract]

    string GetApartmentTypeMTA();

 

    [OperationContract]

    string GetApartmentTypeSTA();

}

and applied the attribute to this implementation:

class TestService : ITestService

{

    public string GetApartmentTypeMTA()

    {

        return Thread.CurrentThread.GetApartmentState().ToString();

    }

 

    [STAOperationBehavior]

    public string GetApartmentTypeSTA()

    {

        return Thread.CurrentThread.GetApartmentState().ToString();

    }

}

When calling the service, GetApartmentTypeMTA always returns MTA and GetApartmentTypeSTA always returns STA.

If you want to save some typing time or see the sample application, go here.


Posted 07-17-2009 9:00 AM by Scott Seely
Filed under:

[Advertisement]

Comments

Sanjeev Agarwal wrote Daily tech links for .net and related technologies - July 18-21, 2009
on 07-20-2009 5:11 AM

Daily tech links for .net and related technologies - July 18-21, 2009 Web Development A (less) simple

audriscrow wrote re: Calling an STA COM Object from a WCF Operation
on 08-09-2009 8:39 PM

20th sulfate induce modeling adaptation dimming due

stanbenywe wrote re: Calling an STA COM Object from a WCF Operation
on 08-17-2009 5:32 PM

radiative number program cfcs projected mid sensitivity

lachdecas wrote re: Calling an STA COM Object from a WCF Operation
on 09-22-2009 8:43 PM

controls scenario app roughly until

mnottoli wrote re: Calling an STA COM Object from a WCF Operation
on 10-06-2009 6:08 AM

I found a little problem with this STAOperation implementation : If the invoked method throws an exception, the exception is seen as unhandled in a separate thread (the STA thread created for the invocation) and it does not propagate up to the WCF service model exception handling. This can kill a container application (e.g. a windows service ) because of the unhandled exception.

A first Solution can be the following : enclose the delegate() body in a try/catch block and pass the exception back to the synch invoker context. If an exception was thrown, rethrow it from the sync invoker scope. Did anyone else have this problem ? Do you have hints to complete/correct this solution?

thanks,

Scott Seely wrote re: Calling an STA COM Object from a WCF Operation
on 11-11-2009 8:29 AM

mnottoli-- I suspect that your solution is correct. I'll dig into this one ASAP and verify. Thanks for the catch. If I don't respond within 2 more days, feel free to send me email telling me to get it in motion (scott at scottseely dot com).

allysonkri wrote re: Calling an STA COM Object from a WCF Operation
on 12-08-2009 2:58 AM

research impact depends inc running attributable

Tony Bolton wrote re: Calling an STA COM Object from a WCF Operation
on 01-25-2010 10:07 AM

Hi Scott,

Just wondering if you managed to look at mnottoli's comment regarding exception handling?  Curious as this is something we're just getting into...

Cheers,

Tony

patanpatan wrote re: Calling an STA COM Object from a WCF Operation
on 02-25-2010 10:57 AM

Hi, can this be used with impersonation? I need  to use the COM component with windows integrated security

Scott Seely wrote re: Calling an STA COM Object from a WCF Operation
on 04-03-2010 9:23 PM

I've updated this post to include the fix for the exception problem.

Martin Staael wrote re: Calling an STA COM Object from a WCF Operation
on 09-20-2010 5:16 AM

Thanks for updating with the fix! Could you also update the download as  STAException doesn't exist?

I got it working by changing:

  STAException lastException = null;

 to

Exception lastException = null;

and then

lastException = new STAException(ex);

to

lastException = ex;

JOP wrote re: Calling an STA COM Object from a WCF Operation
on 10-27-2010 9:24 AM

Hi,

I found the current operation context is null (OperationContext.Current ) I need to access here in order to get callback channel. Is there any way to solve this? Your article has been really helpfull for us, thank you.

JOP wrote re: Calling an STA COM Object from a WCF Operation
on 10-27-2010 1:41 PM

Hi, We have already solved this last issue. The only thing we had to do was eclosing delegate code in

using (System.ServiceModel.OperationContextScope scope = new System.ServiceModel.OperationContextScope(OperationContext.Current)){}

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco <-- NEW Friend!
NServiceBus <-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)