/*=================================================================== Microsoft Denali Microsoft Confidential. Copyright 1997 Microsoft Corporation. All Rights Reserved. Component: MTA Callback File: mtacb.cpp Owner: DmitryR This file contains the implementation of MTA callbacks ===================================================================*/ #include "denpre.h" #pragma hdrstop #include "MTAcb.h" #include "memchk.h" /*=================================================================== MTA Callback Thread Worker thread that implements the MTA callback functionality ===================================================================*/ class CMTACallbackThread { private: DWORD m_fInited : 1; // inited? DWORD m_fCSInited : 1; // critical section inited? DWORD m_fShutdown : 1; // shutdown? CRITICAL_SECTION m_csLock; // callback critical section HANDLE m_hDoItEvent; // callback requested event HANDLE m_hDoneEvent; // callback done event HANDLE m_hThread; // thread handle PMTACALLBACK m_pMTACallback; // callback function ptr void *m_pvContext; // arg1 void *m_pvContext2; // arg2 HRESULT m_hrResult; // return code // The call callback from MTA thread void DoCallback() { Assert(m_pMTACallback); m_hrResult = (*m_pMTACallback)(m_pvContext, m_pvContext2); } // The thread function static unsigned Thread(void *pvArg) { HRESULT hr; Assert(pvArg); CMTACallbackThread *pThread = (CMTACallbackThread *)pvArg; hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); // MTA if (FAILED(hr)) { // Bug 87857: Handle failure from CoInitialize if (hr == E_INVALIDARG) { CoUninitialize(); } // This shouldnt actually fail. Not entirely clear what to do if it does Assert(FALSE); return hr; } while (!pThread->m_fShutdown) { DWORD dwRet = MsgWaitForMultipleObjects ( 1, &(pThread->m_hDoItEvent), FALSE, INFINITE, QS_ALLINPUT ); if (pThread->m_fShutdown) break; if (dwRet == WAIT_OBJECT_0) { // Event -> do the callback pThread->DoCallback(); SetEvent(pThread->m_hDoneEvent); } else { // Do messages MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) DispatchMessage(&msg); } } CoUninitialize(); return 0; } public: // Constructor CMTACallbackThread() : m_fInited(FALSE), m_fCSInited(FALSE), m_fShutdown(FALSE), m_hDoItEvent(NULL), m_hDoneEvent(NULL), m_hThread(NULL), m_pMTACallback(NULL) { } // Destructor ~CMTACallbackThread() { // Real cleanup is in UnInit() // This is to cleanup after a bad Init() if (m_fCSInited) DeleteCriticalSection(&m_csLock); if (m_hDoItEvent) CloseHandle(m_hDoItEvent); if (m_hDoneEvent) CloseHandle(m_hDoneEvent); } // Init (real constructor) HRESULT Init() { HRESULT hr = S_OK; if (SUCCEEDED(hr)) { ErrInitCriticalSection(&m_csLock, hr); m_fCSInited = SUCCEEDED(hr); } if (SUCCEEDED(hr)) { m_hDoItEvent = IIS_CREATE_EVENT( "CMTACallbackThread::m_hDoItEvent", this, FALSE, FALSE ); if (!m_hDoItEvent) hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { m_hDoneEvent = IIS_CREATE_EVENT( "CMTACallbackThread::m_hDoneEvent", this, FALSE, FALSE ); if (!m_hDoneEvent) hr = E_OUTOFMEMORY; } // Launch the MTA thread unsigned threadId; uintptr_t ulThread = _beginthreadex(NULL, 0, CMTACallbackThread::Thread, this, 0, &threadId); if (ulThread == 0xffffffff || ulThread == 0) hr = E_OUTOFMEMORY; else m_hThread = (HANDLE)ulThread; if (SUCCEEDED(hr)) m_fInited = TRUE; return hr; } // UnInit (real destructor) HRESULT UnInit() { Assert(m_fInited); if (m_hThread) { // Kill the MTA thread m_fShutdown = TRUE; SetEvent(m_hDoItEvent); WaitForSingleObject(m_hThread, INFINITE); CloseHandle(m_hThread); m_hThread = NULL; } if (m_fCSInited) { DeleteCriticalSection(&m_csLock); m_fCSInited = FALSE; } if (m_hDoItEvent) { CloseHandle(m_hDoItEvent); m_hDoItEvent = NULL; } if (m_hDoneEvent) { CloseHandle(m_hDoneEvent); m_hDoneEvent = NULL; } m_fInited = FALSE; return S_OK; } // Execute callback HRESULT CallCallback ( PMTACALLBACK pMTACallback, void *pvContext, void *pvContext2 ) { if (m_fShutdown) return E_FAIL; Assert(m_fInited); Assert(pMTACallback); HRESULT hr = E_FAIL; DWORD eventSignaled; EnterCriticalSection(&m_csLock); Assert(m_pMTACallback == NULL); m_pMTACallback = pMTACallback; m_pvContext = pvContext; m_pvContext2 = pvContext2; m_hrResult = E_FAIL; // Tell MTA thread to call back SetEvent(m_hDoItEvent); // Wait till done CoWaitForMultipleHandles(0, INFINITE, 1, &m_hDoneEvent, &eventSignaled); // remember HRESULT hr = m_hrResult; // to make sure we never do it twice m_pMTACallback = NULL; LeaveCriticalSection(&m_csLock); return hr; } }; // Sole instance of the above static CMTACallbackThread g_MTACallbackThread; /*=================================================================== E x t e r n a l A P I ===================================================================*/ /*=================================================================== InitMTACallbacks To be called from DllInit() Inits the MTA callback processing Launches the MTA thread Parameters Returns: HRESULT ===================================================================*/ HRESULT InitMTACallbacks() { return g_MTACallbackThread.Init(); } /*=================================================================== UnInitMTACallbacks To be called from DllUnInit() Stops the MTA callback processing Kills the MTA thread Parameters Returns: HRESULT ===================================================================*/ HRESULT UnInitMTACallbacks() { return g_MTACallbackThread.UnInit(); } /*=================================================================== CallMTACallback Calls the hack. Parameters PMTACALLBACK pMTACallback call this function void *pvContext pass this to it void *pvContext2 extra arg Returns: HRESULT ===================================================================*/ HRESULT CallMTACallback ( PMTACALLBACK pMTACallback, void *pvContext, void *pvContext2 ) { return g_MTACallbackThread.CallCallback ( pMTACallback, pvContext, pvContext2 ); }