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?