Hungry Mind , Blog about everything in IT - C#, Java, C++, .NET, Windows, WinAPI, ...

Marshall function call to another thread

Short tutorial on how to make cross apartment (cross thread) COM call without interfaces, proxies, stubs and tlbs.

Each apartment (thread) has its own accesible context object. It can be obtained via CoGetObjectContext API function:

CComPtr<IContext> pContext;   
CComPtr<IContextCallback> pContextCallback;
hr = CoGetObjectContext(IID_PPV_ARGS(&pContext));
hr = CoGetObjectContext(IID_PPV_ARGS(&pContextCallback));

One of interfaces you can get is IContextCallback with only one method - ContextCallback. As you might already guessed, we just need to get IContextCallback in target execution context and invoke ContextCallback method in any other context:

pContextCallback.p->AddRef();
_beginthread(&myThread, 65536, pContextCallback.p);
HANDLE h[] = { CreateEvent(NULL, TRUE, FALSE, NULL) };
DWORD dwIndex;
CoWaitForMultipleHandles(0, INFINITE, _countof(h), h, &dwIndex);
[...]
HRESULT _stdcall contextCall(ComCallData *pParam)
{
   OutputDebugString(_T("contextCall\n"));
   return S_OK;
}

void __cdecl myThread(LPVOID p)
{
   CoInitialize(NULL);

   CComPtr<IContextCallback> pContextCallback;
   pContextCallback.Attach(reinterpret_cast<IContextCallback *>(p));
   
   ComCallData cd = { 0, 0, NULL };
   pContextCallback->ContextCallback(&contextCall, &cd, IID_NULL, 0, NULL);

   CoUninitialize();
}

Here I'm starting a separate thread to make cross context call and using CoWaitForMultipleHandles OLE32 function to trigger message loop to accept cross thread COM calls (regular message loop is surely enough, I'm just reluctant to write one). Here are resulting call stacks:

Main thread:
> Win32ConsoleApplication.exe!contextCall(tagComCallData * pParam) Line 227 C++
  ole32.dll!CRemoteUnknown::DoCallback(struct tagXAptCallback *) Unknown
  rpcrt4.dll!_Invoke@12 () Unknown
  rpcrt4.dll!_NdrStubCall2@16 () Unknown
  ole32.dll!_CStdStubBuffer_Invoke@12 () Unknown
  ole32.dll!SyncStubInvoke(struct tagRPCOLEMESSAGE *,struct _GUID const &,class CIDObject *,void *,struct IRpcChannelBuffer *,struct IRpcStubBuffer *,unsigned long *) Unknown
  ole32.dll!StubInvoke(struct tagRPCOLEMESSAGE *,class CStdIdentity *,struct IRpcStubBuffer *,struct IRpcChannelBuffer *,struct tagIPIDEntry *,unsigned long *) Unknown
  ole32.dll!CCtxComChnl::ContextInvoke(struct tagRPCOLEMESSAGE *,struct IRpcStubBuffer *,struct tagIPIDEntry *,unsigned long *) Unknown
  ole32.dll!MTAInvoke(struct tagRPCOLEMESSAGE *,unsigned long,struct IRpcStubBuffer *,class IInternalChannelBuffer *,struct tagIPIDEntry *,unsigned long *) Unknown
  ole32.dll!STAInvoke(struct tagRPCOLEMESSAGE *,unsigned long,struct IRpcStubBuffer *,class IInternalChannelBuffer *,void *,struct tagIPIDEntry *,unsigned long *) Unknown
  ole32.dll!AppInvoke(class CMessageCall *,class CRpcChannelBuffer *,struct IRpcStubBuffer *,void *,void *,struct tagIPIDEntry *,struct LocalThis *) Unknown
  ole32.dll!ComInvokeWithLockAndIPID(class CMessageCall *,struct tagIPIDEntry *) Unknown
  ole32.dll!ComInvoke(class CMessageCall *) Unknown
  ole32.dll!ThreadDispatch(void *) Unknown
  ole32.dll!ThreadWndProc(struct HWND__ *,unsigned int,unsigned int,long) Unknown
  user32.dll!_InternalCallWinProc@20 () Unknown
  user32.dll!_UserCallWinProcCheckWow@32 () Unknown
  user32.dll!_DispatchMessageWorker@8 () Unknown
  user32.dll!_DispatchMessageW@4 () Unknown
  ole32.dll!CCliModalLoop::PeekRPCAndDDEMessage(void) Unknown
  ole32.dll!CCliModalLoop::FindMessage(unsigned long) Unknown
  ole32.dll!CCliModalLoop::HandleWakeForMsg(void) Unknown
  ole32.dll!CCliModalLoop::BlockFn(void * *,unsigned long,unsigned long *) Unknown
  ole32.dll!_CoWaitForMultipleHandles@20 () Unknown
  Win32ConsoleApplication.exe!wmain(int argc, wchar_t * * argv) Line 284 C++
  Win32ConsoleApplication.exe!__tmainCRTStartup() Line 533 C
  Win32ConsoleApplication.exe!wmainCRTStartup() Line 377 C
  kernel32.dll!@BaseThreadInitThunk@12 () Unknown
  ntdll.dll!___RtlUserThreadStart@8 () Unknown
  ntdll.dll!__RtlUserThreadStart@8 () Unknown
Worker thread:
  ntdll.dll!_NtWaitForMultipleObjects@20 () Unknown
  ntdll.dll!_NtWaitForMultipleObjects@20 () Unknown
  KernelBase.dll!_WaitForMultipleObjectsEx@20 () Unknown
  kernel32.dll!_WaitForMultipleObjectsExImplementation@20 () Unknown
  user32.dll!_RealMsgWaitForMultipleObjectsEx@20 () Unknown
  ole32.dll!CCliModalLoop::BlockFn(void * *,unsigned long,unsigned long *) Unknown
  ole32.dll!ModalLoop(class CMessageCall *) Unknown
  ole32.dll!SwitchSTA(class OXIDEntry *,class CMessageCall * *) Unknown
  ole32.dll!CRpcChannelBuffer::SwitchAptAndDispatchCall(class CMessageCall * *) Unknown
  ole32.dll!CRpcChannelBuffer::SendReceive2(struct tagRPCOLEMESSAGE *,unsigned long *) Unknown
  ole32.dll!CCliModalLoop::SendReceive(struct tagRPCOLEMESSAGE *,unsigned long *,class IInternalChannelBuffer *) Unknown
  ole32.dll!CAptRpcChnl::SendReceive(struct tagRPCOLEMESSAGE *,unsigned long *) Unknown
  ole32.dll!CCtxComChnl::SendReceive(struct tagRPCOLEMESSAGE *,unsigned long *) Unknown
  ole32.dll!NdrExtpProxySendReceive(void *,struct _MIDL_STUB_MESSAGE *) Unknown
  rpcrt4.dll!@NdrpProxySendReceive@4 () Unknown
  rpcrt4.dll!_NdrClientCall2 () Unknown
  ole32.dll!_ObjectStublessClient@8 () Unknown
  ole32.dll!_ObjectStubless@0 () Unknown
  ole32.dll!CObjectContext::InternalContextCallback(long (*)(void *),void *,struct _GUID const &,int,struct IUnknown *) Unknown
  ole32.dll!CObjectContext::ContextCallback(long (*)(struct tagComCallData *),struct tagComCallData *,struct _GUID const &,int,struct IUnknown *) Unknown
> Win32ConsoleApplication.exe!myThread(void * p) Line 239 C++
  msvcr110d.dll!_callthreadstart() Line 255 C
  msvcr110d.dll!_threadstart(void * ptd) Line 239 C
  kernel32.dll!@BaseThreadInitThunk@12 () Unknown
  ntdll.dll!___RtlUserThreadStart@8 () Unknown
 ntdll.dll!__RtlUserThreadStart@8 () Unknown

Windows OS has many hidden features, used by Microsoft products. Are we any worse?

Copyright 2007-2011 Chabster