//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997. // // File: Msg.cpp // // Contents: Handles messages between threads // // Classes: CThreadMsgProxy // // Notes: // // History: 05-Nov-97 rogerg Created. // //-------------------------------------------------------------------------- #include "precomp.h" extern HINSTANCE g_hInst; extern void UnitApplication(); // globals for handling QueryEndSession HANDLE g_hEndSessionEvent = NULL; // created when an end session has occured. BOOL g_fShuttingDown = FALSE; // set when application begins to shutdown.(WM_QUIT) // global for keeping track of handler's threads. We create on Thread for each handler // CLSID STUBLIST *g_FirstStub = NULL; // pointer to first proxy in our list. CRITICAL_SECTION g_StubListCriticalSection; // Critical Section to use for adding proxy //+--------------------------------------------------------------------------- // // Function: TerminateStub, public // // Synopsis: Called by proxy to terminate a Stub of the given Id. // // Arguments: [pStubID] - Identifies the stub. // // Returns: S_OK - Stub was terminated // S_FALSE or Error - Stub either was already terminated // or couldn't be found. // // Modifies: // // History: 17-Nov-98 rogerg Created. // //---------------------------------------------------------------------------- HRESULT TerminateStub(STUBLIST *pStubID) { HRESULT hr = E_UNEXPECTED; STUBLIST *pStubList; CCriticalSection cCritSect(&g_StubListCriticalSection,GetCurrentThreadId()); cCritSect.Enter(); pStubList = g_FirstStub; while (pStubList) { if (pStubID == pStubList) { hr = pStubList->fStubTerminated ? S_FALSE : S_OK; pStubList->fStubTerminated = TRUE; break; } pStubList = pStubList->pNextStub; } cCritSect.Leave(); AssertSz(SUCCEEDED(hr),"Didn't find StubID on Terminate"); return hr; } //+--------------------------------------------------------------------------- // // Function: DoesStubExist, public // // Synopsis: Called by proxy see if Stub Exists // or has been terminated // // Arguments: [pStubID] - Identifies the stub. // // Returns: S_OK - Stubs exists // S_FALSE - Stub hasn't been terminated. // // Modifies: // // History: 17-Nov-98 rogerg Created. // //---------------------------------------------------------------------------- HRESULT DoesStubExist(STUBLIST *pStubID) { HRESULT hr = S_FALSE; STUBLIST *pStubList; CCriticalSection cCritSect(&g_StubListCriticalSection,GetCurrentThreadId()); cCritSect.Enter(); pStubList = g_FirstStub; while (pStubList) { if (pStubID == pStubList) { // if stub has already been terminated return S_FALSE hr = pStubList->fStubTerminated ? S_FALSE : S_OK; break; } pStubList = pStubList->pNextStub; } cCritSect.Leave(); return hr; } //+--------------------------------------------------------------------------- // // Function: CreateHandlerThread, public // // Synopsis: Called by client to create a new handler thread // // Arguments: [pThreadProxy] - on success returns a pointer to Proxy // [hwndDlg] = hwnd of Window to associate with this proxy // // Returns: Appropriate status code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- HRESULT CreateHandlerThread(CThreadMsgProxy **pThreadProxy, HWND hwndDlg, REFCLSID refClsid) { HRESULT hr = E_FAIL; HANDLE hThread = NULL; DWORD dwThreadId; HandlerThreadArgs ThreadArgs; STUBLIST *pStubList; BOOL fExistingStub = FALSE; CCriticalSection cCritSect(&g_StubListCriticalSection,GetCurrentThreadId()); *pThreadProxy = new CThreadMsgProxy(); if (NULL == *pThreadProxy) return E_OUTOFMEMORY; // lock the critical section and don't release it until the proxy // has been setup. cCritSect.Enter(); // Look to see if there is already a thread for this handler's // clsid and if there is, reuse it, else create a new one. pStubList = g_FirstStub; while (pStubList) { if ((pStubList->clsidStub == refClsid) && (FALSE == pStubList->fStubTerminated)) { fExistingStub = TRUE; break; } pStubList = pStubList->pNextStub; } // if found existing proxy then addref the cRefs and init the proxy // with the variables, else create a new one. if (fExistingStub) { ++(pStubList->cRefs); // bump up the cRefs hr = (*pThreadProxy)->InitProxy(pStubList->hwndStub, pStubList->ThreadIdStub, pStubList->hThreadStub, hwndDlg, pStubList->clsidStub, pStubList); } else { ThreadArgs.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (ThreadArgs.hEvent) { hThread = CreateThread(NULL, 0, HandlerThread, &ThreadArgs, 0, &dwThreadId); if (hThread) { WaitForSingleObject(ThreadArgs.hEvent,INFINITE); hr = ThreadArgs.hr; if (S_OK == hr) { STUBLIST *pNewStub; pNewStub = (STUBLIST*) ALLOC(sizeof(STUBLIST)); if (pNewStub) { pNewStub->pNextStub = NULL; pNewStub->cRefs = 1; pNewStub->hwndStub = ThreadArgs.hwndStub; pNewStub->ThreadIdStub = dwThreadId; pNewStub->hThreadStub = hThread; pNewStub->clsidStub = refClsid; pNewStub->fStubTerminated = FALSE; if (NULL == g_FirstStub) { g_FirstStub = pNewStub; } else { pStubList = g_FirstStub; while (pStubList->pNextStub) { pStubList = pStubList->pNextStub; } pStubList->pNextStub = pNewStub; } (*pThreadProxy)->InitProxy(ThreadArgs.hwndStub,dwThreadId,hThread,hwndDlg,refClsid,pNewStub); } else { hr = E_OUTOFMEMORY; } } // if failed to create thread, initproxy or add it to global list, then bail if (S_OK != hr) { CloseHandle(hThread); } } else { hr = HRESULT_FROM_WIN32(GetLastError()); } CloseHandle(ThreadArgs.hEvent); } } cCritSect.Leave(); // if got this far either found our created a handler thread, now need // to initialize the the stub side to create a hndlrMsg for this // instance of the handler. will return a hdnlrmsg that must be passed // along with everycall. if ( (S_OK == hr) && (*pThreadProxy)) { hr = (*pThreadProxy)->CreateNewHndlrMsg(); // Review - if fail to create hndlr message, then // free proxy and return an error. } if (S_OK != hr) { if ((*pThreadProxy)) { (*pThreadProxy)->Release(); *pThreadProxy = NULL; } } return hr; } //+--------------------------------------------------------------------------- // // Function: HandlerThread, public // // Synopsis: main proc for Handler thread // // Arguments: [lpArg] - Ptr to HandlerThreadArgs // // Returns: Appropriate status code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- DWORD WINAPI HandlerThread( LPVOID lpArg ) { MSG msg; HRESULT hr; CThreadMsgStub *pThreadMsgStub = NULL; CMsgServiceHwnd *pMsgDlg; HRESULT hrOleInitialize; BOOL fMsgDlgInitialized = FALSE; HandlerThreadArgs *pThreadArgs = (HandlerThreadArgs *) lpArg; DWORD dwThreadID = GetCurrentThreadId(); __try { pThreadArgs->hr = E_UNEXPECTED; // need to do a PeekMessage and then set an event to make sure // a message loop is created before the first PostMessage is sent. PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); hrOleInitialize = CoInitialize(NULL); // create our message hwnd pMsgDlg = new CMsgServiceHwnd; if (pMsgDlg) { if (pMsgDlg->Initialize(dwThreadID,MSGHWNDTYPE_HANDLERTHREAD)) { pThreadArgs->hwndStub = pMsgDlg->GetHwnd(); fMsgDlgInitialized = TRUE; } } // set the approriate error if (fMsgDlgInitialized && SUCCEEDED(hrOleInitialize)) hr = S_OK; else hr = E_UNEXPECTED; pThreadArgs->hr = hr; // let the caller know the thread is done initializing. if (pThreadArgs->hEvent) SetEvent(pThreadArgs->hEvent); if (S_OK == hr) { // sit in loop receiving messages. while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } } if (SUCCEEDED(hrOleInitialize)) { CoFreeUnusedLibraries(); CoUninitialize(); } } __except(QueryHandleException()) { AssertSz(0,"Exception in Handler Thread."); } return 0; } //+--------------------------------------------------------------------------- // // Member: CMsgServiceHwnd::HandleThreadMessage, public // // Synopsis: Responsible for determining the thread message that // was received and calling the proper handler routine // // Arguments: [pmsgInfo] - Ptr to MessagingInfo structure // [pgenMsg] - Ptr to Generic Message structure. // // Returns: Appropriate status code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- HRESULT CMsgServiceHwnd::HandleThreadMessage(MessagingInfo *pmsgInfo,GenericMsg *pgenMsg) { CHndlrMsg *pHndlrMsg; pgenMsg->hr = E_UNEXPECTED; // initialize the return result. // review, look up in linked list to validate. pHndlrMsg = pmsgInfo->pCHndlrMsg; m_fInOutCall = TRUE; switch (pgenMsg->ThreadMsg) { case ThreadMsg_Release: { MSGInitialize *pmsg = (MSGInitialize *) pgenMsg; ULONG cRefs; cRefs = pHndlrMsg->Release(); Assert(0 == cRefs); m_pHndlrMsg = NULL; // review, change when done. pgenMsg->hr = S_OK; m_fInOutCall = FALSE; } break; case ThreadMsg_Initialize: { MSGInitialize *pmsg = (MSGInitialize *) pgenMsg; pgenMsg->hr = pHndlrMsg->Initialize(pmsg->dwReserved,pmsg->dwSyncFlags, pmsg->cbCookie,pmsg->lpCookie); } break; case ThreadMsg_GetHandlerInfo: { MSGGetHandlerInfo *pmsg = (MSGGetHandlerInfo *) pgenMsg; pgenMsg->hr = pHndlrMsg->GetHandlerInfo(pmsg->ppSyncMgrHandlerInfo); } break; case ThreadMsg_GetItemObject: { MSGGetItemObject *pmsg = (MSGGetItemObject *) pgenMsg; pgenMsg->hr = pHndlrMsg->GetItemObject(pmsg->ItemID,pmsg->riid, pmsg->ppv); } break; case ThreadMsg_ShowProperties: { MSGShowProperties *pmsg = (MSGShowProperties *) pgenMsg; pgenMsg->hr = pHndlrMsg->ShowProperties(pmsg->hWndParent,pmsg->ItemID); } break; case ThreadMsg_PrepareForSync: { MSGPrepareForSync *pmsg = (MSGPrepareForSync *) pgenMsg; HANDLE hEvent = pmsgInfo->hMsgEvent; pgenMsg->hr = pHndlrMsg->PrepareForSync(pmsg->cbNumItems,pmsg->pItemIDs, pmsg->hWndParent,pmsg->dwReserved); } break; case ThreadMsg_Synchronize: { MSGSynchronize *pmsg = (MSGSynchronize *) pgenMsg; HANDLE hEvent = pmsgInfo->hMsgEvent; pgenMsg->hr = pHndlrMsg->Synchronize(pmsg->hWndParent); } break; case ThreadMsg_SetItemStatus: { MSGSetItemStatus *pmsg = (MSGSetItemStatus *) pgenMsg; pgenMsg->hr = pHndlrMsg->SetItemStatus(pmsg->ItemID,pmsg->dwSyncMgrStatus); } break; case ThreadMsg_ShowError: { MSGShowConflicts *pmsg = (MSGShowConflicts *) pgenMsg; pgenMsg->hr = pHndlrMsg->ShowError(pmsg->hWndParent,pmsg->ErrorID); } break; case ThreadMsg_AddHandlerItems: { MSGAddItemHandler *pmsg = (MSGAddItemHandler *) pgenMsg; pgenMsg->hr = pHndlrMsg->AddHandlerItems(pmsg->hwndList,pmsg->pcbNumItems); } break; case ThreadMsg_CreateServer: { MSGCreateServer *pmsg = (MSGCreateServer *) pgenMsg; pgenMsg->hr = pHndlrMsg->CreateServer(pmsg->pCLSIDServer,pmsg->pHndlrQueue,pmsg->pHandlerId,pmsg->dwProxyThreadId); } break; case ThreadMsg_SetHndlrQueue: { MSGSetHndlrQueue *pmsg = (MSGSetHndlrQueue *) pgenMsg; pgenMsg->hr = pHndlrMsg->SetHndlrQueue(pmsg->pHndlrQueue,pmsg->pHandlerId,pmsg->dwProxyThreadId); } break; case ThreadMsg_SetupCallback: { MSGSetupCallback *pmsg = (MSGSetupCallback *) pgenMsg; pgenMsg->hr = pHndlrMsg->SetupCallback(pmsg->fSet); } break; default: AssertSz(0,"Unknown Thread Message"); break; } m_fInOutCall = FALSE; return S_OK; } //+--------------------------------------------------------------------------- // // Function: DopModalLoop, public // // Synopsis: Sit in message loop until the specified object // becomes or thread becomes signalled. // // Arguments: [hEvent] - Event to wait on // [hThread] - Thread that if it becomes signalled indicates thread // that is being called has died. // [hwndDlg] - hwnd of Dialog on thread we should check message for, can be null. // [fAllowIncomingCalls] - incoming Messages can be dispatched during out call. // // Returns: Appropriate status code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- HRESULT DoModalLoopoLD(HANDLE hEvent,HANDLE hThread,HWND hwndDlg,BOOL fAllowIncomingCalls, DWORD dwTimeout) { HRESULT hr = S_OK; DWORD dwWakeup; DWORD dwHandleCount; DWORD dwStartTime = GetTickCount(); DWORD dwTimeoutValue; HANDLE handles[2]; handles[0] = hEvent; handles[1] = hThread; dwHandleCount = (NULL == hThread) ? 1 : 2; dwTimeoutValue = dwTimeout; // initial call to wait is just the passed in vaulue // just sit in a loop until the message has been processed or the thread // we are calling dies // if no event to wait on just fall through if (NULL == hEvent) { do { if (fAllowIncomingCalls) { dwWakeup = MsgWaitForMultipleObjects(dwHandleCount,&handles[0],FALSE,dwTimeoutValue,QS_ALLINPUT); } else { dwWakeup = WaitForMultipleObjects(dwHandleCount,&handles[0],FALSE,dwTimeoutValue); } if (WAIT_OBJECT_0 == dwWakeup) // call was completed. { hr = S_OK; break; } else if ((WAIT_OBJECT_0 +1 == dwWakeup) && (2== dwHandleCount) ) { // thread died within call. AssertSz(0,"Server Thread Terminated"); hr = E_UNEXPECTED; break; } else if (WAIT_ABANDONED_0 == dwWakeup) { AssertSz(0,"Abandoned"); // this shouldn't ever happen hr = E_UNEXPECTED; break; } else if (WAIT_TIMEOUT == dwWakeup) { hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); break; } else { MSG msg; // see if events are signalled themselves since can get into this // loop is items are in the queue even if events we are // waiting on are already set. if (WAIT_OBJECT_0 == WaitForSingleObject(hEvent,0)) { hr = S_OK; break; } else if (hThread && (WAIT_OBJECT_0 == WaitForSingleObject(hThread,0)) ) { AssertSz(0,"Server Thread Terminated"); hr = E_UNEXPECTED; break; } // only grab out one peek message since dispatch could // cause another message to get placed in the queue. if (PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_REMOVE)) { if ( (NULL == hwndDlg) || !IsDialogMessage(hwndDlg,&msg)) { TranslateMessage((LPMSG) &msg); DispatchMessage((LPMSG) &msg); } } } // adjust the timeout value if (INFINITE == dwTimeout) { dwTimeoutValue = INFINITE; } else { DWORD dwCurTime = GetTickCount(); // handle roll-over of GetTickCount. If this happens use has to wait // for the StartTime again. so user may have to wait twice as long // as originally anticipated. if (dwCurTime < dwStartTime) { dwStartTime = dwCurTime; } // if the elapsed time is greater than the timeout set the // timeout value to zero, else use the different/ if (dwTimeout <= (dwCurTime - dwStartTime)) { dwTimeoutValue = 0; } else { dwTimeoutValue = dwTimeout - (dwCurTime - dwStartTime); } } } while (1); } return hr; } //+--------------------------------------------------------------------------- // // Function: DopModalLoop, public // // Synopsis: Sit in message loop until the specified object // becomes or thread becomes signalled. // // Arguments: [hEvent] - Event to wait on // [hThread] - Thread that if it becomes signalled indicates thread // that is being called has died. // [hwndDlg] - hwnd of Dialog on thread we should check message for, can be null. // [fAllowIncomingCalls] - incoming Messages can be dispatched during out call. // // Returns: Appropriate status code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- HRESULT DoModalLoop(HANDLE hEvent,HANDLE hThread,HWND hwndDlg,BOOL fAllowIncomingCalls, DWORD dwTimeout) { HRESULT hr = S_OK; DWORD dwWakeup; DWORD dwHandleCount; DWORD dwStartTime = GetTickCount(); DWORD dwTimeoutValue; HANDLE handles[2]; handles[0] = hEvent; handles[1] = hThread; Assert(hEvent); dwHandleCount = (NULL == hThread) ? 1 : 2; dwTimeoutValue = dwTimeout; // initial call to wait is just the passed in vaulue // just sit in a loop until the message has been processed or the thread // we are calling dies do { DWORD dwWaitValue; MSG msg; // check if hEvents are signalled yet if (WAIT_OBJECT_0 == (dwWaitValue = WaitForSingleObject(hEvent,0)) ) { hr = S_OK; break; } else if ( (dwWaitValue != WAIT_ABANDONED) && hThread && (WAIT_OBJECT_0 == (dwWaitValue = WaitForSingleObject(hThread,0))) ) { // possible on Release message event was set between the // time we checked for it and our thread event check. if (WAIT_OBJECT_0 == (dwWaitValue = WaitForSingleObject(hEvent,0)) ) { hr = S_OK; } else { AssertSz(0,"Server Thread Terminated"); hr = E_UNEXPECTED; } break; } // if come out af any of these calls with abandoned then assert and break; if (WAIT_ABANDONED == dwWaitValue) { AssertSz(0,"Abandoned"); // this shouldn't ever happen hr = E_UNEXPECTED; break; } // if not then either grab next PeekMessage or wait for objects depending if (fAllowIncomingCalls) { // Leave any completion posts in the queue until the call has returned. if (PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_REMOVE) ) { dwWakeup = WAIT_OBJECT_0 + dwHandleCount; // set it to wait MsgWait would. Assert (msg.message != WM_QUIT); if ( (NULL == hwndDlg) || !IsDialogMessage(hwndDlg,&msg)) { TranslateMessage((LPMSG) &msg); DispatchMessage((LPMSG) &msg); } } else { dwWakeup = MsgWaitForMultipleObjects(dwHandleCount,&handles[0],FALSE,dwTimeoutValue,QS_ALLINPUT); } } else { dwWakeup = WaitForMultipleObjects(dwHandleCount,&handles[0],FALSE,dwTimeoutValue); } if (WAIT_TIMEOUT == dwWakeup) { hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); break; } // update the timeout value if (INFINITE == dwTimeout) { dwTimeoutValue = INFINITE; } else { DWORD dwCurTime = GetTickCount(); // handle roll-over of GetTickCount. If this happens use has to wait // for the StartTime again. so user may have to wait twice as long // as originally anticipated. if (dwCurTime < dwStartTime) { dwStartTime = dwCurTime; } // if the elapsed time is greater than the timeout set the // timeout value to zero, else use the different/ if (dwTimeout <= (dwCurTime - dwStartTime)) { dwTimeoutValue = 0; } else { dwTimeoutValue = dwTimeout - (dwCurTime - dwStartTime); } } } while (1); return hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::CThreadMsgProxy, public // // Synopsis: Constructor // Arguments: // // Returns: // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- CThreadMsgProxy::CThreadMsgProxy() { m_Clsid = GUID_NULL; m_cRef = 1; m_hwndDlg = NULL; m_pCHndlrMsg = NULL; m_fTerminatedHandler = FALSE; m_hThreadStub = NULL; m_ThreadIdStub = 0; m_hwndStub = NULL; m_hwndDlg = NULL; m_ThreadIdProxy = 0; m_fNewHndlrQueue = FALSE; m_pHandlerId = 0; m_pHndlrQueue = NULL; m_dwNestCount = 0; m_fHaveCompletionCall = FALSE; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::~CThreadMsgProxy, public // // Synopsis: destructor // Arguments: // // Returns: // // Modifies: // // History: 03-Jun-98 rogerg Created. // //---------------------------------------------------------------------------- CThreadMsgProxy::~CThreadMsgProxy() { Assert(0 == m_dwNestCount); Assert(NULL == m_pStubId); } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::InitProxy, public // // Synopsis: Initializes member vars of Thread Proxy // Arguments: [hwndStub] - hwnd of the Stub to send messages too. // [ThreadId] - ThreadId of the Stub // [hThread] - Handle of the Stub Thread. // // Returns: !!!!This function should be written so it never fails. // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::InitProxy(HWND hwndStub, DWORD ThreadId,HANDLE hThread, HWND hwndDlg,REFCLSID refClsid, STUBLIST *pStubId) { m_hwndStub = hwndStub; m_ThreadIdStub = ThreadId; m_hThreadStub = hThread; m_pStubId = pStubId; m_hwndDlg = hwndDlg; m_Clsid = refClsid; m_ThreadIdProxy = GetCurrentThreadId(); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::DispatchMsg, public // // Synopsis: Dispatches the specified messge // Arguments: [pgenMsg] - Ptr to Generic Message structure. // [fAllowIncomingCalls] - incoming Messages can be dispatched during out call. // // Returns: Appropriate status code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::DispatchMsg(GenericMsg *pgenMsg,BOOL fAllowIncomingCalls,BOOL fAsync) { HRESULT hr = E_UNEXPECTED; MessagingInfo msgInfo; // if the hndlrmsg information needs to be updated update // it before sending requested message AssertSz(!m_fTerminatedHandler,"Dispatching Message on Terminated Thread"); if (m_fTerminatedHandler) { return E_UNEXPECTED; } ++m_dwNestCount; msgInfo.hMsgEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (NULL == msgInfo.hMsgEvent) { --m_dwNestCount; return HRESULT_FROM_WIN32(GetLastError()); } msgInfo.dwSenderThreadID = m_ThreadIdProxy; msgInfo.pCHndlrMsg = m_pCHndlrMsg; // Post the message to the handler thread. Assert(m_hwndStub); Assert(m_pCHndlrMsg); Assert(m_hThreadStub); Assert(m_pStubId); if (m_hwndStub && m_pCHndlrMsg && m_hThreadStub && m_pStubId) { BOOL fPostMessage; fPostMessage = PostMessage(m_hwndStub,WM_THREADMESSAGE,(WPARAM) &msgInfo, (LPARAM) pgenMsg); Assert(fPostMessage || m_pStubId->fStubTerminated); if (fPostMessage) { hr = DoModalLoop(msgInfo.hMsgEvent,m_hThreadStub,m_hwndDlg,fAllowIncomingCalls,INFINITE); } } CloseHandle(msgInfo.hMsgEvent); --m_dwNestCount; // if have a callback message then post. Note don't have this code for stub messages // since it doesn;t have any callbacks if (m_fHaveCompletionCall) { PostMessage(m_msgCompletion.hwnd,m_msgCompletion.message,m_msgCompletion.wParam,m_msgCompletion.lParam); m_fHaveCompletionCall = FALSE; } return (S_OK != hr) ? hr : pgenMsg->hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::DispatchsStubMsg, public // // Synopsis: Dispatches the specified Stub messge // Arguments: [pgenMsg] - Ptr to Generic Message structure. // [fAllowIncomingCalls] - incoming Messages can be dispatched during out call. // // Returns: Appropriate status code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::DispatchsStubMsg(GenericMsg *pgenMsg,BOOL fAllowIncomingCalls) { HRESULT hr = E_UNEXPECTED; MessagingInfo msgInfo; BOOL fPostMessage; AssertSz(!m_fTerminatedHandler,"Dispatching Stub Message on Terminated Thread"); if (m_fTerminatedHandler) return E_UNEXPECTED; msgInfo.hMsgEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (NULL == msgInfo.hMsgEvent) return HRESULT_FROM_WIN32(GetLastError()); m_dwNestCount++; msgInfo.dwSenderThreadID = m_ThreadIdProxy; // Post the message to the handler thread. Assert(m_hwndStub); Assert(m_hThreadStub); Assert(m_pStubId); if (m_hwndStub && m_hThreadStub && m_pStubId) { fPostMessage = PostMessage(m_hwndStub,WM_THREADSTUBMESSAGE,(WPARAM) &msgInfo, (LPARAM) pgenMsg); Assert(fPostMessage || (m_pStubId->fStubTerminated)); if (fPostMessage) { hr = DoModalLoop(msgInfo.hMsgEvent,m_hThreadStub,m_hwndDlg,fAllowIncomingCalls,INFINITE); } } CloseHandle(msgInfo.hMsgEvent); m_dwNestCount--; Assert(FALSE == m_fHaveCompletionCall); // catch any stub calls that occur at same time as dispatch return (S_OK != hr) ? hr : pgenMsg->hr; } //+--------------------------------------------------------------------------- // // Function: TerminateHandlerThread, public // // Synopsis: terminate the non-responsive handler thread // // Returns: Appropriate status code // // Modifies: m_hThreadStub; // // History: 02-Nov-98 susia Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::TerminateHandlerThread(TCHAR *pszHandlerName,BOOL fPromptUser) { int iEndSession; TCHAR pszFormatString[MAX_PATH + 1], pszMessageText[MAX_PATH + 1], pszTitleString[MAX_STRING_RES + 1]; AssertSz(!m_fTerminatedHandler,"Terminate Handler called twice on same Proxy"); if (S_OK == DoesStubExist(m_pStubId)) { BOOL bResult; // let use know of cases don't want to prompt user but haven't killed stub yet. Assert(fPromptUser); if (fPromptUser) { if (!pszHandlerName) { LoadString(g_hInst,IDS_NULL_HANDLERNOTRESPONDING,pszMessageText, MAX_PATH); } else { LoadString(g_hInst,IDS_HANDLERNOTRESPONDING,pszFormatString, MAX_PATH); StringCchPrintf(pszMessageText, ARRAYSIZE(pszMessageText), pszFormatString, pszHandlerName); } LoadString(g_hInst,IDS_SYNCMGR_ERROR,pszTitleString, MAX_STRING_RES); iEndSession = MessageBox(m_hwndDlg,pszMessageText,pszTitleString, MB_YESNO | MB_ICONERROR ); if (IDYES != iEndSession) { return S_FALSE; //Yes will terminate the thread } } // make sure handler is still not responding. HRESULT fAllHandlerInstancesComplete = S_FALSE; if (m_pHndlrQueue) { fAllHandlerInstancesComplete = m_pHndlrQueue->IsAllHandlerInstancesCancelCompleted(m_Clsid); } // if no longer any instancesof this handler that aren't responding just ignore // the terminate. if (S_OK == fAllHandlerInstancesComplete) { return S_FALSE; } // mark the stubId as terminated TerminateStub(m_pStubId); // now terminate the thread. // CODE REVIEW: // NOTENOTE: // Danger Will Robinson - Danger Will Robinson - Danger Will Robinson // Maybe we can use something other than TerminateThread here ??? bResult = TerminateThread (m_hThreadStub, 0); AssertSz(bResult,"Error Terminating Thread"); } m_pStubId = NULL; // if get here means we should are terminating this thread m_fTerminatedHandler = TRUE; m_hThreadStub = 0; // set threadId of stub to zero. // set the proxy stubhwnd to NULL m_hwndStub = NULL; // if have a hndlrmsg tell it we are terminated and clear // out our member variable. if (m_pCHndlrMsg) { CHndlrMsg *pCHndlrMsg = m_pCHndlrMsg; m_pCHndlrMsg = NULL; pCHndlrMsg->ForceKillHandler(); } return S_OK; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::CreateNewHndlrMsg, public // // Synopsis: Make a request to the stub to create a new // Handler Message object // // Arguments: // Returns: Appropriate status code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::CreateNewHndlrMsg() { HRESULT hr = S_OK; MSGSTUBCreateStub msg; msg.MsgGen.ThreadMsg = StubMsg_CreateNewStub; hr = DispatchsStubMsg( (GenericMsg *) &msg,TRUE); m_pCHndlrMsg = msg.pCHndlrMsg; return hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::ReleaseStub, public // // Synopsis: Informst the Stub thread that it is no longer needed. // Arguments: // Returns: Appropriate status code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::ReleaseStub() { HRESULT hr = S_OK; GenericMsg msg; msg.ThreadMsg = StubMsg_Release; hr = DispatchsStubMsg( (GenericMsg *) &msg,TRUE); return hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::QueryInterface, public // // Synopsis: Standard QueryInterface // // Arguments: [iid] - Interface ID // [ppvObj] - Object return // // Returns: Always returns E_NOTIMPL; // // Modifies: [ppvObj] // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::QueryInterface(REFIID riid, LPVOID FAR *ppv) { AssertSz(0,"QI called on MsgProxy"); return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::AddRef, public // // Synopsis: Add reference // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CThreadMsgProxy::AddRef() { ULONG cRefs; cRefs = InterlockedIncrement((LONG *)& m_cRef); return cRefs; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::Release, public // // Synopsis: Release reference // Must properly handle the case Release is // called before the initialize method in case // createing the handler thread fails. // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CThreadMsgProxy::Release() { HRESULT hr = S_OK; GenericMsg msg; ULONG cRefs; cRefs = InterlockedDecrement( (LONG *) &m_cRef); if (cRefs) return cRefs; if (m_hThreadStub && !m_fTerminatedHandler) { CCriticalSection cCritSect(&g_StubListCriticalSection,GetCurrentThreadId()); BOOL fLastRelease = FALSE; BOOL fExistingStub = FALSE; STUBLIST *pStubList; // release the handler Thread. msg.ThreadMsg = ThreadMsg_Release; hr = DispatchMsg( (GenericMsg *) &msg,TRUE,FALSE); m_pCHndlrMsg = NULL; // If the cRefs in our proxy list is zero // then sit in loop until the ThreadStub Dies. cCritSect.Enter(); pStubList = g_FirstStub; while (pStubList) { if (pStubList->clsidStub == m_Clsid) { fExistingStub = TRUE; break; } pStubList= pStubList->pNextStub; } Assert(fExistingStub); // their should always be an existing proxy if (fExistingStub) { Assert(pStubList->cRefs > 0); (pStubList->cRefs)--; if (0 == pStubList->cRefs) { STUBLIST CurStub; STUBLIST *pCurStub = &CurStub; CurStub.pNextStub = g_FirstStub; while (pCurStub->pNextStub) { if (pCurStub->pNextStub == pStubList) { pCurStub->pNextStub = pStubList->pNextStub; g_FirstStub = CurStub.pNextStub; FREE(pStubList); break; } pCurStub = pCurStub->pNextStub; } fLastRelease = TRUE; } } cCritSect.Leave(); if (fLastRelease) { // send the quit command to the stub, if (S_OK == ReleaseStub()) { // Review, what if stubthread never dies. m_dwNestCount++; DoModalLoop(m_hThreadStub,NULL,NULL,TRUE,INFINITE); // sit in loop until the stub thread dies CloseHandle(m_hThreadStub); m_dwNestCount--; } } m_pStubId = NULL; // clear StubId } delete this; return cRefs; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::Initialize, public // // Synopsis: Sends Initialize command to the Handler thread // Arguments: [dwReserved] - reserved. // [dwSyncFlags] - syncflags // [cbCookie] - size of cookie data // [lpCookie] - ptr to any cookie data // // Returns: Appropriate error code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::Initialize(DWORD dwReserved, DWORD dwSyncFlags, DWORD cbCookie, const BYTE *lpCookie) { HRESULT hr = S_OK; MSGInitialize msg; msg.MsgGen.ThreadMsg = ThreadMsg_Initialize; // package up the parameters msg.dwReserved = dwReserved; msg.dwSyncFlags = dwSyncFlags; msg.cbCookie = cbCookie; msg.lpCookie = lpCookie; hr = DispatchMsg( (GenericMsg *) &msg,TRUE,FALSE); return hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::GetHandlerInfo, public // // Synopsis: Sends GetHandler command to the Handler thread // Arguments: [ppSyncMgrHandlerInfo] - pointer to SyncMgrHandlerInfo pointer // // Returns: Appropriate error code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::GetHandlerInfo(LPSYNCMGRHANDLERINFO *ppSyncMgrHandlerInfo) { HRESULT hr = S_OK; MSGGetHandlerInfo msg; msg.MsgGen.ThreadMsg = ThreadMsg_Initialize; // package up the parameters msg.ppSyncMgrHandlerInfo = ppSyncMgrHandlerInfo; hr = DispatchMsg( (GenericMsg *) &msg,TRUE,FALSE); return hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::EnumOfflineItems, public // // Synopsis: Sends Enum command to the Handler thread // Should not be called. AddItems method // should be called instead // // Arguments: [ppenumOfflineItems] - reserved. // // Returns: E_NOTIMPL; // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::EnumSyncMgrItems(ISyncMgrEnumItems **ppenumOfflineItems) { AssertSz(0,"EnumMethod Called on Proxy"); return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::GetItemObject, public // // Synopsis: Sends GetItemObject command to the Handler thread // Arguments: [ItemID] - identifies the item. // [riid] - requested interface // [ppv] - On success, pointer to object // // Returns: Appropriate error code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::GetItemObject(REFSYNCMGRITEMID ItemID,REFIID riid,void** ppv) { HRESULT hr = S_OK; MSGGetItemObject msg; msg.MsgGen.ThreadMsg = ThreadMsg_GetItemObject; // package up the parameters msg.ItemID = ItemID; msg.riid = riid; msg.ppv = ppv; hr = DispatchMsg( (GenericMsg *) &msg,TRUE,FALSE); return hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::ShowProperties, public // // Synopsis: Sends ShowProperties command to the Handler thread // Arguments: [hWndParent] - hwnd to use as parent of any dialogs // [ItemID] - Identifies the Item // // Returns: Appropriate error code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::ShowProperties(HWND hWndParent,REFSYNCMGRITEMID ItemID) { HRESULT hr = S_OK; MSGShowProperties msg; msg.MsgGen.ThreadMsg = ThreadMsg_ShowProperties; // package up the parameters msg.hWndParent = hWndParent; msg.ItemID = ItemID; hr = DispatchMsg( (GenericMsg *) &msg,TRUE,TRUE); return hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::SetProgressCallback, public // // Synopsis: Sends SetProgressCallback command to the Handler thread // This method should not be called, SetupCallback method // should be called instead // // Arguments: [lpCallBack] - Ptr to callback. // // Returns: Appropriate error code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::SetProgressCallback(ISyncMgrSynchronizeCallback *lpCallBack) { AssertSz(0,"SetProgressCallback called on Proxy"); return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::PrepareForSync, public // // Synopsis: Sends PrepareForSync command to the Handler thread // // Arguments: [cbNumItems] - Number of items in the pItemIDs array. // [pItemIDs] - array of item ids // [hWndParent] - hwnd to use as the parent for any dialogs // [dwReserved] - Reserved parameter. // // Returns: Appropriate error code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::PrepareForSync(ULONG cbNumItems,SYNCMGRITEMID *pItemIDs, HWND hWndParent,DWORD dwReserved) { HRESULT hr = S_OK; MSGPrepareForSync msg; msg.MsgGen.ThreadMsg = ThreadMsg_PrepareForSync; // package up the parameters msg.cbNumItems = cbNumItems; msg.pItemIDs = pItemIDs; msg.hWndParent = hWndParent; msg.dwReserved = dwReserved; hr = DispatchMsg( (GenericMsg *) &msg,TRUE,TRUE); return hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::Synchronize, public // // Synopsis: Sends Synchronize command to the Handler thread // // Arguments: [hWndParent] - hwnd to use as the parent for any dialogs // // Returns: Appropriate error code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::Synchronize(HWND hWndParent) { HRESULT hr = S_OK; MSGSynchronize msg; msg.MsgGen.ThreadMsg = ThreadMsg_Synchronize; // package up the parameters msg.hWndParent = hWndParent; hr = DispatchMsg( (GenericMsg *) &msg,TRUE,TRUE); return hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::SetItemStatus, public // // Synopsis: Sends SetItemStatus command to the Handler thread // // Arguments: [ItemID] - Identifies the item // [dwSyncMgrStatus] - Status to set the item too. // // Returns: Appropriate error code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::SetItemStatus(REFSYNCMGRITEMID ItemID,DWORD dwSyncMgrStatus) { HRESULT hr = S_OK; MSGSetItemStatus msg; msg.MsgGen.ThreadMsg = ThreadMsg_SetItemStatus; // package up the parameters msg.ItemID = ItemID; msg.dwSyncMgrStatus = dwSyncMgrStatus; hr = DispatchMsg( (GenericMsg *) &msg,TRUE,FALSE); return hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::ShowError, public // // Synopsis: Sends ShowError command to the Handler thread // // Arguments: [hWndParent] - hwnd to use as the parent for any dialogs // [dwErrorID] - Identifies the Error. // // Returns: Appropriate error code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::ShowError(HWND hWndParent,REFSYNCMGRERRORID ErrorID,ULONG *pcbNumItems,SYNCMGRITEMID **ppItemIDs) { HRESULT hr = S_OK; MSGShowConflicts msg; msg.MsgGen.ThreadMsg = ThreadMsg_ShowError; // package up the parameters msg.hWndParent = hWndParent; msg.ErrorID = ErrorID; msg.pcbNumItems = pcbNumItems; msg.ppItemIDs = ppItemIDs; hr = DispatchMsg( (GenericMsg *) &msg,TRUE,TRUE); return hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::CreateServer, public // // Synopsis: Sends CreateServer command to the Handler thread // // Arguments: [pCLSIDServer] - clsid of handler to create // [pHndlrQueue] - Queue the handler belongs too. // [wHandlerId] - ID assigned to this instance of the Handler // // Returns: Appropriate error code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::CreateServer(const CLSID *pCLSIDServer,CHndlrQueue *pHndlrQueue, HANDLERINFO *pHandlerId) { HRESULT hr = S_OK; MSGCreateServer msg; m_pHndlrQueue = pHndlrQueue; m_pHandlerId = pHandlerId; msg.MsgGen.ThreadMsg = ThreadMsg_CreateServer; // package up the parameters msg.pCLSIDServer = pCLSIDServer; msg.pHndlrQueue = pHndlrQueue; msg.pHandlerId = pHandlerId; msg.dwProxyThreadId = m_ThreadIdProxy; hr = DispatchMsg( (GenericMsg *) &msg,TRUE,FALSE); return hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::SetHndlrQueue, public // // Synopsis: Assigns a new queue to the Handler // // Arguments: [pHndlrQueue] - Queue the handler now belongs too. // [wHandlerId] - ID assigned to this instance of the Handler // [dwThreadIdProxy] - ThreadID the queue is in. // // Returns: Appropriate error code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::SetHndlrQueue(CHndlrQueue *pHndlrQueue, HANDLERINFO *pHandlerId, DWORD dwThreadIdProxy) { HRESULT hr = S_OK; MSGSetHndlrQueue msg; AssertSz(0,"this shouldn't be called"); m_ThreadIdProxy = dwThreadIdProxy; // update the threadId the Proxy is on. msg.MsgGen.ThreadMsg = ThreadMsg_SetHndlrQueue; // package up the parameters msg.pHndlrQueue = pHndlrQueue; msg.pHandlerId = pHandlerId; msg.dwProxyThreadId = dwThreadIdProxy; hr = DispatchMsg( (GenericMsg *) &msg,TRUE,FALSE); return hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::AddHandlerItems, public // // Synopsis: Request Handler adds its items to the queue. // // Arguments: [hwndList] - Currently not used.. // // Returns: Appropriate error code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::AddHandlerItems(HWND hwndList,DWORD *pcbNumItems) { HRESULT hr = S_OK; MSGAddItemHandler msg; msg.MsgGen.ThreadMsg = ThreadMsg_AddHandlerItems; // package up the parameters msg.hwndList = hwndList; msg.pcbNumItems = pcbNumItems; hr = DispatchMsg( (GenericMsg *) &msg,TRUE,FALSE); return hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::SetupCallback, public // // Synopsis: Request stub sets up the Callback. // // Arguments: [fSet] - TRUE == set the callback, FALSE == revoke it. // // Returns: Appropriate error code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::SetupCallback(BOOL fSet) { HRESULT hr = S_OK; MSGSetupCallback msg; AssertSz(0,"Shouldn't be called"); msg.MsgGen.ThreadMsg = ThreadMsg_SetupCallback; // package up the parameters msg.fSet = fSet; hr = DispatchMsg( (GenericMsg *) &msg,TRUE,FALSE); return hr; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::SetProxyParams, public // // Synopsis: informs server thread that the queue has been chagned // on it.. // // Arguments: // // Returns: Appropriate error code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::SetProxyParams(HWND hwndDlg, DWORD ThreadIdProxy, CHndlrQueue *pHndlrQueue,HANDLERINFO *pHandlerId ) { m_hwndDlg = hwndDlg; m_ThreadIdProxy = ThreadIdProxy; m_pHndlrQueue = pHndlrQueue; m_pHandlerId = pHandlerId; Assert(m_pCHndlrMsg); if (m_pCHndlrMsg) { m_pCHndlrMsg->SetHndlrQueue(pHndlrQueue,pHandlerId,m_ThreadIdProxy); } return S_OK; } //+--------------------------------------------------------------------------- // // Member: CThreadMsgProxy::SetProxyCompletion, public // // Synopsis: sets values for any completion notification to // post when returning from an out call // // Arguments: // // Returns: Appropriate error code // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CThreadMsgProxy::SetProxyCompletion(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam) { Assert(FALSE == m_fHaveCompletionCall); // should only ever have one. if (m_fHaveCompletionCall) // if already have a completion fail. return S_FALSE; m_fHaveCompletionCall = TRUE; m_msgCompletion.hwnd = hWnd; m_msgCompletion.message = Msg; m_msgCompletion.wParam = wParam; m_msgCompletion.lParam = lParam; return S_OK; } //+--------------------------------------------------------------------------- // // Member: CMsgServiceHwnd::CMsgServiceHwnd, public // // Synopsis: Constructor // // Arguments: // // Returns: // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- CMsgServiceHwnd::CMsgServiceHwnd() { m_hwnd = NULL; m_dwThreadID = -1; m_pHndlrMsg = NULL; m_fInOutCall = FALSE; m_pMsgServiceQueue = NULL; m_MsgHwndType = MSGHWNDTYPE_UNDEFINED; } //+--------------------------------------------------------------------------- // // Member: CMsgServiceHwnd::~CMsgServiceHwnd, public // // Synopsis: Destructor // // Arguments: // // Returns: // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- CMsgServiceHwnd::~CMsgServiceHwnd() { } //+--------------------------------------------------------------------------- // // Member: CMsgServiceHwnd::Initialize, public // // Synopsis: Initializes the service HWND // // Arguments: [dwThreadID] - id of thread hwnd belongs too. // [MsgHwndType] - type of MsgHwnd this is. // // Returns: TRUE on success, FALSE on failure // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- BOOL CMsgServiceHwnd::Initialize(DWORD dwThreadID,MSGHWNDTYPE MsgHwndType) { BOOL fInitialized = FALSE; TCHAR szWinTitle[MAX_STRING_RES]; m_MsgHwndType = MsgHwndType; LoadString(g_hInst, IDS_SYNCMGRNAME, szWinTitle, ARRAYSIZE(szWinTitle)); m_hwnd = CreateWindowEx(0, TEXT(MSGSERVICE_HWNDCLASSNAME), szWinTitle, // must use WS_POPUP so the window does not get // assigned a hot key by user. WS_DISABLED | WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, // REVIEW, can we give it a parent to not show up. NULL, g_hInst, this); Assert(m_hwnd); if (m_hwnd) { m_dwThreadID = dwThreadID; fInitialized = TRUE; } if (!fInitialized) { Assert(NULL == m_pHndlrMsg); } // caller still needs to call Destroy if initialize returns false. return fInitialized; } //+--------------------------------------------------------------------------- // // Member: CMsgServiceHwnd::Destroy, public // // Synopsis: Destroys the ServiceHwnd // // Arguments: // Returns: // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- void CMsgServiceHwnd::Destroy() { BOOL fDestroy; // HANDLER m_pHndlrMsg will be destroyed by the Release HandleThreadMessage call // only case that it shouldn't is if for some reason CreateThreadHndlr failed Assert(NULL == m_pHndlrMsg); if (m_pHndlrMsg) { m_pHndlrMsg->Release(); m_pHndlrMsg = NULL; } if (m_hwnd) { fDestroy = DestroyWindow(m_hwnd); Assert(fDestroy); } delete this; } //+--------------------------------------------------------------------------- // // Member: CMsgServiceHwnd::MsgThreadWndProc, public // // Synopsis: Servicer Side message handling window. // // Arguments: // Returns: // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- LRESULT CALLBACK MsgThreadWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) { CMsgServiceHwnd *pThis = (CMsgServiceHwnd *) GetWindowLongPtr(hWnd, DWL_THREADWNDPROCCLASS); if (pThis || (msg == WM_CREATE)) { switch (msg) { case WM_CREATE : { CREATESTRUCT *pCreateStruct = (CREATESTRUCT *) lParam; SetWindowLongPtr(hWnd, DWL_THREADWNDPROCCLASS,(LONG_PTR) pCreateStruct->lpCreateParams ); pThis = (CMsgServiceHwnd *) pCreateStruct->lpCreateParams ; } break; case WM_DESTROY: SetWindowLongPtr(hWnd, DWL_THREADWNDPROCCLASS,(LONG_PTR) NULL); PostQuitMessage(0); // no longer need this thread break; case WM_THREADSTUBMESSAGE: // message send only to stub. { MessagingInfo *pmsgInfo = (MessagingInfo *) wParam; GenericMsg *pgenMsg = (GenericMsg *) lParam; Assert(MSGHWNDTYPE_HANDLERTHREAD == pThis->m_MsgHwndType); pgenMsg->hr = E_UNEXPECTED; switch(pgenMsg->ThreadMsg) { case StubMsg_Release: // proxy is telling us there is no need to stick around // any longer so post a quit message. Assert(NULL == pThis->m_pHndlrMsg); pThis->Destroy(); // no longer need this pgenMsg->hr = S_OK; break; case StubMsg_CreateNewStub: // proxy is telling us there is no need to stick around // any longer so post a quit message. pThis->m_pHndlrMsg = new CHndlrMsg; ((MSGSTUBCreateStub *) pgenMsg)->pCHndlrMsg = pThis->m_pHndlrMsg; pThis->m_pHndlrMsg = NULL; pgenMsg->hr = S_OK; break; default: AssertSz(0,"Unknown StubMessage"); break; }; if (pmsgInfo->hMsgEvent) { SetEvent(pmsgInfo->hMsgEvent); } } break; case WM_THREADMESSAGE: { MessagingInfo *pmsgInfo = (MessagingInfo *) wParam; GenericMsg *pgenMsg = (GenericMsg *) lParam; Assert(MSGHWNDTYPE_HANDLERTHREAD == pThis->m_MsgHwndType); pThis->HandleThreadMessage(pmsgInfo,pgenMsg); // won't be an hEvent on an async call if (pmsgInfo->hMsgEvent) { SetEvent(pmsgInfo->hMsgEvent); } // on an async call we free } break; case WM_CFACTTHREAD_REVOKE: { Assert(MSGHWNDTYPE_MAINTHREAD == pThis->m_MsgHwndType); HRESULT hr = CoRevokeClassObject((DWORD)wParam); Assert(S_OK == hr); } break; case WM_MAINTHREAD_QUIT: // handles shutdown of main thread. { HANDLE hThread = (HANDLE) lParam; Assert(MSGHWNDTYPE_MAINTHREAD == pThis->m_MsgHwndType); // set ShuttingDown Flag for race conditions with QueryEnd, // before yielding. g_fShuttingDown = TRUE; // if there is an hThread that was passed wait until it goes away Assert(0 == hThread); // we currently don't support this. if (hThread) { WaitForSingleObject(hThread,INFINITE); CloseHandle(hThread); } // if have a queryEndSession object state its okay to return now // no need to cleanup window. if (g_hEndSessionEvent) { HANDLE hEvent = g_hEndSessionEvent; // g_hEndSessionEvent = NULL; // leave EndSession NON-Null since only need to handle one. SetEvent(hEvent); } else { pThis->Destroy(); // Clean up this window. } } break; case WM_QUERYENDSESSION: { HWND hwndQueryParent; UINT uiMessageID; BOOL fLetUserDecide; BOOL fReturn = TRUE; // only handle this message if it is the main thread window if (MSGHWNDTYPE_MAINTHREAD != pThis->m_MsgHwndType) { break; } if (!g_fShuttingDown && (S_FALSE == ObjMgr_HandleQueryEndSession(&hwndQueryParent,&uiMessageID,&fLetUserDecide))) { TCHAR pszTitle[MAX_PATH]; TCHAR pszMessageText[MAX_PATH]; UINT uType; // style of messagebox. int iEndSession; LoadString(g_hInst,IDS_SYNCMGRNAME,pszTitle,sizeof(pszTitle)/sizeof(TCHAR)); LoadString(g_hInst,uiMessageID,pszMessageText,sizeof(pszMessageText)/sizeof(TCHAR)); if (fLetUserDecide) { uType = MB_YESNO | MB_ICONEXCLAMATION | MB_SETFOREGROUND; } else { uType = MB_OK | MB_ICONSTOP | MB_SETFOREGROUND; } iEndSession = MessageBox(hwndQueryParent, pszMessageText, pszTitle, uType); if (!fLetUserDecide || IDYES != iEndSession) { fReturn = FALSE; // FALSE causes system to stop the shutdown. } } // if we are going to allow shutdown cleanup our threads // before returning since on Win9x its too late afterwards. if (fReturn) { HANDLE hEndSessionEvent = NULL; // its possible that another QUERYENDSESSION comes // in while we are still shutting down. If already // handling an end sessios or in WM_MAINTHREAD_QUIT just fall through if (NULL == g_hEndSessionEvent && !g_fShuttingDown) { g_hEndSessionEvent = CreateEvent(NULL, TRUE, FALSE, NULL); hEndSessionEvent = g_hEndSessionEvent; ObjMgr_CloseAll(); // start the process of closing down the dialogs. Assert(hEndSessionEvent); // wait until other threads have cleaned up so we know its safe to terminate. if (hEndSessionEvent) { DoModalLoop(hEndSessionEvent ,NULL,NULL,TRUE,INFINITE); CloseHandle(hEndSessionEvent); } } } return fReturn; } break; default: break; } } return DefWindowProc(hWnd, msg, wParam, lParam); } //+--------------------------------------------------------------------------- // // Function: InitMessageService, public // // Synopsis: Initializes our internal thread messaging service. // Must be called before any Messagin is done. // // Arguments: // Returns: S_OK if Service was successfully initialized. // // Modifies: // // History: 05-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDAPI InitMessageService() { ATOM aWndClass = 0; WNDCLASS xClass; DWORD dwErr; // initialize the proxy critical section if (InitializeCriticalSectionAndSpinCount(&g_StubListCriticalSection, 0)) { // Register windows class.we need for handling thread communication xClass.style = 0; xClass.lpfnWndProc = MsgThreadWndProc; xClass.cbClsExtra = 0; xClass.cbWndExtra = sizeof(PVOID); // room for class this ptr xClass.hInstance = g_hInst; xClass.hIcon = NULL; xClass.hCursor = NULL; xClass.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1); xClass.lpszMenuName = NULL; xClass.lpszClassName = TEXT(MSGSERVICE_HWNDCLASSNAME); aWndClass = RegisterClass( &xClass ); dwErr = GetLastError(); Assert(aWndClass); } return (0 == aWndClass) ? S_FALSE : S_OK; }