none
COM Interop, thread marshal RRS feed

  • Question

  • I have a class that wraps a com object with C++/Interop following the example here http://msdn2.microsoft.com/en-us/library/f31k2c87.aspx

    The problem I have is when the COM object is created in a STA thread and methods are called from other threads.

    How do I marshal the call to the correct thread? If the object is created in an UI thread I can do Invoke but how do I do it in other threads?

    The documentation for Thread.Join says that the thread will do COM and SendMessage pumping while waiting.
    How can I do a SendMessage into a managed thread (or is there a special COM message)?

    Also, the finalizer in my wrapper class (like in the example) releases the COM object. Since the finalizer runs in a special thread I assume thread marshalling is needed. Can't find any examples on how to do this?

    I was thinking something like:

    - When creating the COM object:
    If the thread is STA, save the securitycontext.
    - In the finalizer:
    If a securitycontext is stored, use it to marshal the release

    This should work most of the time but are there any other pitfalls?



    Monday, March 24, 2008 4:33 PM

All replies

  •  

    digg.
    Tuesday, March 25, 2008 8:28 AM
  • So your C++ library will be a multi threaded wrapper around a STA COM object? Have you looked at using something like CoMashalInterThreadInterfaceInStream?

     

     

    Tuesday, March 25, 2008 1:39 PM
    Moderator
  • >So your C++ library will be a multi threaded wrapper around a STA COM object?

    Not really.

    The object I'm wrapping can use any threading model.

    It is the users of my wrapper that need it to work in a STA thread becuase other COM objects require it.

    There are two things I wan't to support:

    1. Finalizers in Windows Forms. My wrapper implements IDisposable but the .Net programmers I meet don't understand using and dispose patterns.

    2. Limited background worker thread support. The wrapped COM object got some services where the status need to be polled. I can hide most of the work in the wrapper but I need to use the polling functions from the MTA threadpool.

    >Have you looked at using something like CoMashalInterThreadInterfaceInStream?


    I was hoping I could avoid that. Also haven't found a confirmation that it really works with Thread.Join() etc. (or is this what COM-pumping means)


    Tuesday, March 25, 2008 8:22 PM
  • Could you please clarify the threading model of the COM component you want to wrap?
    Thursday, March 27, 2008 6:08 AM
  • >Could you please clarify the threading model of the COM component you want to wrap?

    It is not specified in the registry which I assume means both. Also checked with the developers and they said it works with both STA and MTA.

    I thought the CoMarshal.. stuff was complicated but it wasn't. Actually worked on  first try. Am a bit confused on how it works though.

    If I extract the proxy in an MTA apartment (and the object supports MTA), can the object be called from the MTA apartment then?

    Might have done some silly mistake but I wrote a test case looking like this (pseudo code):

    class MarshalTest {
      private bool mark;

      public void SecondThread() {
        Assert.That(Thread.CurrentThread.GetApartmentModel(), Is.EqualTo(ApartmentModel.MTA))
     

        try {
          comobject.Method();
          Assert.Fail()
        }
        catch (ComException err) {
          Assert.That(err.ErrorCode, Is.EqualTo(RPC_E_WRONGTHREAD))
        }

       // get proxy from stream
       proxy = CoGetAndReleaseStream...

       proxy.Method(); // Here I expect this thread to stall until the main thread is pumping

       mark = true;

       Sleep(50000);
      }

      public void MainThread() {
        Assert.That(Thread.CurrentThread.GetApartmentModel(), Is.EqualTo(ApartmentModel.STA))

         // Create Com object in this STA thread
         comobject = CreateObject...

        // Create stream with proxy
        CoMarshalInterThread...

        mark = false;

         // start a thread in MTA threadpool
        SecondThread.BeginInvoke(...) 

      
      // spinwait
      
      DateTime start = DateTime.Now;
        While ((DateTime.Now - now).Seconds < 5)
          Console.WriteLine("whatever"); // so the loop isn't optimized away

         // Here I expect the mark variable to still be false since this thread isn't pumping
        Assert.That(mark, Is.False);
       
        Thread.CurrentThread.Join(10000); // Here I expected the thread marshalling to happen
        Assert.That(mark, Is.True);
      } 
    }

    However, the mark variable was set to true while the main thread was spinning!

    -----

    Another question is about the Finalizer. At the moment I always create a stream when creating the com object in a STA thread.
    I read somewhere that the CLR RCW use lazy marshalling, i.e. it creates the stream only when it is needed. I assume this means that the Finalizer must switch to the STA thread to create the stream. Is this a CLR-only trick or can I use it as well?
    Thursday, March 27, 2008 10:08 PM