I got to debug a COM threading issue where we see that when we are communicating between an application (say an MFC application) and a class library (developed using C# and implementing a COM component) the messages between the C++ thread and the C# thread are not being marshaled.

The thread that actually makes the COM call is an STA thread and the call stack shows that it is waiting (the reason for hang) and no cross apartment information is available to this thread. A typical call-stack is shown below.

 

05f2e758 7705c752 ntdll!NtWaitForMultipleObjects+0xc

05f2e8dc 766d9879 KERNELBASE!WaitForMultipleObjectsEx+0x10b

05f2e934 766d9778 USER32!RealMsgWaitForMultipleObjectsEx+0x16b

05f2e954 76c884d3 USER32!MsgWaitForMultipleObjectsEx+0x3e

05f2e9b4 76c8bbe6 combase!CCliModalLoop::BlockFn+0xff

05f2e9e8 76d3efc5 combase!ClassicSTAThreadDispatchCrossApartmentCall+0x12f

05f2eb04 76c8b7f3 combase!CRpcChannelBuffer::SendReceive2+0x555

05f2ebbc 76cc115d combase!ClassicSTAThreadSendReceive+0x1be

05f2ec18 76d3d39a combase!CCtxComChnl::SendReceive+0x281

05f2ec34 76b2e4a0 combase!NdrExtpProxySendReceive+0x43

05f2ec40 76bab313 RPCRT4!NdrpProxySendReceive+0xe

05f2f05c 76d3c779 RPCRT4!NdrClientCall2+0x20c

<SNIP>

05f2f358 73df8242 clr!RCW::CallQueryInterface+0x95

05f2f3b8 73df81ba clr!RCW::GetComIPForMethodTableFromCache+0xba

05f2f3c8 73df892a clr!RCW::GetComIPFromRCW+0x2d

05f2f474 73d9b51b clr!ComObject::SupportsInterface+0xd5

05f2f4b8 73ee1de1 clr!ObjIsInstanceOf+0x9b

05f2f554 02aa0bc0 clr!JITutil_ChkCastAny+0x77

05f2f5ac 72fc2407 mscorlib_ni!System.Threading.ThreadHelper.ThreadStart_Context(System.Object)+0x6f

<SNIP>

05f2fa64 73e3cf6a clr!ThreadNative::KickOffThread+0x1d2

05f2fb7c 76378543 clr!Thread::intermediateThreadProc+0x4d

05f2fb88 778abf39 KERNEL32!BaseThreadInitThunk+0xe

05f2fbcc 778abf0c ntdll!__RtlUserThreadStart+0x72

 

As per the COM apartment rules an STA apartment has only one thread. In this issue we are getting a hang as because we are not marshaling between the two different STA threads.  The reason behind this being the STA thread is not pumping messages, it just hangs doing nothing. COM STA threads are required to pump messages and the way to do it is by introducing a message pump (example code below).

 

BOOL bRet;

MSG msg;

HWND hWnd = NULL;

while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)

{

    if (bRet == -1)

    {

        // handle the error and possibly exit

    }

    else

    {

        TranslateMessage(&msg);

        DispatchMessage(&msg);

    }

}

 

Going back to the original issue - this means that the STA thread was not pumping messages, it just hangs, doing nothing. After introducing the above message loop everything started working.

Rules for single-threaded apartments are simple, but it is important to follow them carefully:

  • Every object should live on only one thread (within a single-threaded apartment).
  • Initialize the COM library for each thread.
  • Marshal all pointers to objects when passing them between apartments.
  • Each single-threaded apartment must have a message loop to handle calls from other processes and apartments within the same process. Single-threaded apartments without objects (client only) also need a message loop to dispatch the broadcast messages that some applications use.
  • If you are unmarshaling an interface pointer multiple times between apartments in a process, you might use the IGlobalInterfaceTable interface. With other techniques, you would have to remarshal each time.
  • If the interface pointer is unmarshaled only once, you may want to use the CoMarshalInterThreadInterfaceInStream function. It can also be used to pass an interface pointer from one thread to another thread in the same process.

If the creators apartment is multithreaded and the threading model is BOTH then the object lives in MTA. MTA objects won’t have to go about doing cross apartment calls for communicating with each other. Here is a table that lists where the COM object is supposed to live for an apartment and threading model.

 

Creator Apartment

ThreadingModel

Where Object live

STA

Apartment            

Creator's STA

STA

Free                 

MTA

STA

Both                 

Creator's STA

STA

None/Single          

Main STA

MTA

Apartment            

New STA

MTA

Free                 

MTA

MTA

Both                 

MTA

MTA

None/Single          

Main STA

Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/ms680112(v=vs.85).aspx