2
votes

When creating an out-of-process COM server in C# as documented in Microsoft's All-In-One code sample: CSExeCOMServer, it seems difficult to control the threading model of objects that are created in the server (by a client).

The object being created needs to be in an STA due to the fact that it uses WPF objects, and it's factory is being registered as demonstrated on line 95 of ExeCOMServer.cs and pasted below...

private void PreMessageLoop()
{
    //
    // Register the COM class factories.
    // 

    Guid clsidSimpleObj = new Guid(SimpleObject.ClassId);

    // Register the SimpleObject class object
    int hResult = COMNative.CoRegisterClassObject(
        ref clsidSimpleObj,                 // CLSID to be registered
        new SimpleObjectClassFactory(),     // Class factory
        CLSCTX.LOCAL_SERVER,                // Context to run
        REGCLS.MULTIPLEUSE | REGCLS.SUSPENDED,
        out _cookieSimpleObj);
    if (hResult != 0)
    {
        throw new ApplicationException(
        "CoRegisterClassObject failed w/err 0x" + hResult.ToString("X"));
    }

However, the CreateInstance function is Always called in a new thread which is in an MTA. It doesn't seem to matter that the main thread of the local server is marked (and verified) as an STA thread.

All material on the matter that has been found intimates that the apartment of the created objects should match the apartment of the thread in which the factory was registered. In fact, this seems to be the case when using an ATL COM server (mixed with Managed C++ to create the objects), but this method appears to be injecting a new thread in to the work flow who's intialization parameters, particularly the COM threading model, do not appear to be changeable.

Has anyone solved this issue without resorting to a COM server written largely in unmanaged code.

1
Ugh, I hate the crap they came up with. I was closely associated with the MSDN forums when they got hired in China. I was a community mod back then. The library is not peer-reviewed at all, it straddles the "55% accurate" angle. Especially the guy called "Ge" is very dangerous for his ignorance. The threading model of the server is affected by how the server initializes COM. Not the client.Hans Passant
Thanks for the insight... but the example seems to work well enough for what it does. It is not technically incorrect to have an MTA initialize our object. The confusing part is that the main thread of the SERVER belongs to an STA, yet the framework (COM, OLE, whatever) creates a new thread, in the server, which is in the server's MTA, to call our Factory's CreateInstance.Steven

1 Answers

2
votes

We have very similar situation recently when setting up a WinForm application to host some objects for 64bit interop. The UI thread enters the STA on startup (with the STAThread attribute). It wasn't long before we discovered all the constructors are run in the UI thread (STA) but all other methods are run in a worker thread (MTA) that .Net creates.

It wasn't really the MTA that's the problem, but we do have methods that accesses the UI and the fact that they are always run in a worker thread that's bugging us. Although we couldn't figure out how to force .Net to marshal the calls to the UI thread (STA), we did come up with some very dirty trick involving ContextBoundObject and method interception. I won't go into the details, but you can take a look at System.Runtime.Remoting.Messaging and System.Runtime.Remoting.Contexts namespaces. The idea is to trick the CCW into thinking a transparent proxy as the interop object, and we intercept every invocation to the proxy, pass the message to UI thread, then back-translate the message into method call. Dirty? Yes. Performance? Very bad. But it WORKS.

If you would like to see the method interception code you can drop me an email.