//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997. // // File: Hndlrq.cpp // // Contents: Implements class for keeping track of handlers // and the UI associated with them // // Classes: CHndlrQueue // // Notes: // // History: 05-Nov-97 rogerg Created. // //-------------------------------------------------------------------------- #include "precomp.h" #define HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE 10 // called to set up the JobInfo on a choice queue. STDMETHODIMP CHndlrQueue::AddQueueJobInfo(DWORD dwSyncFlags,DWORD cbNumConnectionNames, TCHAR **ppConnectionNames, TCHAR *pszScheduleName,BOOL fCanMakeConnection ,JOBINFO **pJobInfo) { HRESULT hr = E_UNEXPECTED; TCHAR *pszConnectionName; TCHAR **pszConnectionNameArray; CLock clockqueue(this); *pJobInfo = NULL; Assert(m_QueueType == QUEUETYPE_CHOICE); if (m_QueueType != QUEUETYPE_CHOICE) { return E_UNEXPECTED; } clockqueue.Enter(); Assert(NULL == m_pFirstJobInfo); // fix up connections so have at least one connection/job. // this currently happens on an UpdateItems. if (NULL == ppConnectionNames || 0 == cbNumConnectionNames ) { cbNumConnectionNames = 1; pszConnectionName = TEXT(""); pszConnectionNameArray = &pszConnectionName; } else { pszConnectionName = *ppConnectionNames; pszConnectionNameArray = ppConnectionNames; } // create a job requesting size for the number of connections passed in. hr = CreateJobInfo(&m_pFirstJobInfo,cbNumConnectionNames); if (S_OK == hr) { DWORD dwConnectionIndex; Assert(cbNumConnectionNames >= 1); // Review assert for debugging to test when have multiple connections for first time. m_pFirstJobInfo->cbNumConnectionObjs = 0; // add a connectionObject for each connection for (dwConnectionIndex = 0; dwConnectionIndex < cbNumConnectionNames; ++dwConnectionIndex) { hr = ConnectObj_FindConnectionObj(pszConnectionNameArray[dwConnectionIndex], TRUE,&(m_pFirstJobInfo->pConnectionObj[dwConnectionIndex])); if (S_OK != hr) { break; } else { ++m_pFirstJobInfo->cbNumConnectionObjs; } } if (S_OK == hr) { m_pFirstJobInfo->dwSyncFlags = dwSyncFlags; if ((SYNCMGRFLAG_SCHEDULED == (dwSyncFlags & SYNCMGRFLAG_EVENTMASK))) { StringCchCopy(m_pFirstJobInfo->szScheduleName, ARRAYSIZE(m_pFirstJobInfo->szScheduleName), pszScheduleName); m_pFirstJobInfo->fCanMakeConnection = fCanMakeConnection; m_pFirstJobInfo->fTriedConnection = FALSE; } } else { // couldn't create the connectionObj so release our jobID m_pFirstJobInfo = NULL; } } *pJobInfo = m_pFirstJobInfo; clockqueue.Leave(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::CHndlrQueue, public // // Synopsis: Constructor used to create a progress queue // // Arguments: [QueueType] - Type of Queue that should be created. // [hwndDlg] - Hwnd who owns this queue. // // Returns: // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- CHndlrQueue::CHndlrQueue(QUEUETYPE QueueType,CBaseDlg *pDlg) { Assert( (QueueType == QUEUETYPE_PROGRESS) || (QueueType == QUEUETYPE_CHOICE) ); m_pFirstHandler = NULL; m_wHandlerCount = 0; m_dwShowErrororOutCallCount = 0; m_cRefs = 1; m_QueueType = QueueType; m_fItemsMissing = FALSE; m_dwQueueThreadId = GetCurrentThreadId(); m_iNormalizedMax = 0; m_fNumItemsCompleteNeedsARecalc = TRUE; m_pDlg = pDlg; if (m_pDlg) { m_hwndDlg = m_pDlg->GetHwnd(); Assert(m_hwndDlg); } m_fInCancelCall = FALSE; m_pFirstJobInfo = NULL; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::~CHndlrQueue, public // // Synopsis: Destructor // // Arguments: // // Returns: // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- CHndlrQueue::~CHndlrQueue() { CLock clockqueue(this); // for a progress queue all the jobInfos should be released // for the choice queue there should be one JobInfo that has to // be released that was addref'd in the constructor. Assert(0 == m_cRefs); Assert(NULL == m_pFirstJobInfo); // review - this should never fire anymore. Assert(NULL == m_pFirstJobInfo || m_QueueType == QUEUETYPE_CHOICE); // there shouldn't be any unreleased JobInfo Assert(m_pFirstHandler == NULL); // All Handlers should have been released by now. } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::AddRef, public // // Synopsis: // // Arguments: // // Returns: // // Modifies: // // History: 01-June-98 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CHndlrQueue::AddRef() { DWORD cRefs; Assert(m_cRefs >= 1); // should never zero bounce. cRefs = InterlockedIncrement((LONG *)& m_cRefs); return cRefs; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::Release, public // // Synopsis: // // Arguments: // // Returns: // // Modifies: // // History: 01-June-98 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CHndlrQueue::Release() { DWORD cRefs; cRefs = InterlockedDecrement( (LONG *) &m_cRefs); Assert( ((LONG) cRefs) >= 0); // should never go negative. if (0 == cRefs) { delete this; } return cRefs; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::AddHandler, public // // Synopsis: Adds a new empty handler to the queue and returns it ID // // Arguments: [pwHandlerID] - on success contains the assigned Handler ID // [pJobInfo] - Job this item is associated with // [dwRegistrationFlags] - Flags the Handler has registered for. // // Returns: Appropriate return codes // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::AddHandler(HANDLERINFO **ppHandlerId,JOBINFO *pJobInfo,DWORD dwRegistrationFlags) { HRESULT hr = E_OUTOFMEMORY; LPHANDLERINFO pnewHandlerInfo; CLock clockqueue(this); *ppHandlerId = 0; pnewHandlerInfo = (LPHANDLERINFO) ALLOC(sizeof(HANDLERINFO)); if (pnewHandlerInfo) { clockqueue.Enter(); m_fNumItemsCompleteNeedsARecalc = TRUE; // need to recalc next GetProgress. // initialize the new Handler Entry memset(pnewHandlerInfo, 0, sizeof(HANDLERINFO)); pnewHandlerInfo->HandlerState = HANDLERSTATE_CREATE; pnewHandlerInfo->pHandlerId = pnewHandlerInfo; pnewHandlerInfo->dwRegistrationFlags = dwRegistrationFlags; // queue should be a choice queue and // there should already be a jobinfo. Assert(m_QueueType == QUEUETYPE_CHOICE); Assert(m_pFirstJobInfo); Assert(pJobInfo == m_pFirstJobInfo); // for now job info should always be the first one. if (m_QueueType == QUEUETYPE_CHOICE && pJobInfo) { AddRefJobInfo(pJobInfo); pnewHandlerInfo->pJobInfo = pJobInfo; } // add to end of list and set pHandlerId. End of list since in choice dialog want // first writer wins so don't have to continue searches when setting item state. if (NULL == m_pFirstHandler) { m_pFirstHandler = pnewHandlerInfo; } else { LPHANDLERINFO pCurHandlerInfo; pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo->pNextHandler) { pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } pCurHandlerInfo->pNextHandler = pnewHandlerInfo; } *ppHandlerId = pnewHandlerInfo->pHandlerId; clockqueue.Leave(); hr = S_OK; } return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::ForceKillHandlers, public // // Synopsis: Kills unresponsive handlers after timeout // // Returns: Appropriate return codes // // History: 20-Nov-98 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::ForceCompleteOutCalls(LPHANDLERINFO pCurHandler) { // need to have lock for argument to be valid. ASSERT_LOCKHELD(this); //prepare for sync out call if (pCurHandler->dwOutCallMessages & ThreadMsg_PrepareForSync) { CallCompletionRoutine(pCurHandler,ThreadMsg_PrepareForSync, HRESULT_FROM_WIN32(ERROR_CANCELLED),0,NULL); } //Synchronize out call if (pCurHandler->dwOutCallMessages & ThreadMsg_Synchronize) { CallCompletionRoutine(pCurHandler,ThreadMsg_Synchronize, HRESULT_FROM_WIN32(ERROR_CANCELLED),0,NULL); } //ShowProperties out call if (pCurHandler->dwOutCallMessages & ThreadMsg_ShowProperties) { CallCompletionRoutine(pCurHandler,ThreadMsg_ShowProperties, HRESULT_FROM_WIN32(ERROR_CANCELLED),0,NULL); } //Show Errors out call if (pCurHandler->dwOutCallMessages & ThreadMsg_ShowError) { CallCompletionRoutine(pCurHandler,ThreadMsg_ShowError, HRESULT_FROM_WIN32(ERROR_CANCELLED),0,NULL); } // force handler state to release. pCurHandler->HandlerState = HANDLERSTATE_RELEASE; return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::ForceKillHandlers, public // // Synopsis: Kills unresponsive handlers after timeout // // Returns: Appropriate return codes // // History: 30-Oct-98 susia Created. // 19-Nov-98 rogerg Change to only kill first unresponsive handler // //---------------------------------------------------------------------------- #define BAD_HANDLERSTATE(pHandlerId) \ ( (HANDLERSTATE_INSYNCHRONIZE >= pHandlerId->HandlerState) \ || (pHandlerId->dwOutCallMessages & ThreadMsg_SetItemStatus) \ ) STDMETHODIMP CHndlrQueue::ForceKillHandlers(BOOL *pfItemToKill) { HRESULT hr = S_OK; LPHANDLERINFO pCurHandler; CLock clockqueue(this); *pfItemToKill = TRUE; // if something strange happens make sure timer gets reset. clockqueue.Enter(); pCurHandler = m_pFirstHandler; while (pCurHandler) { // if handler is cancelled but still in a noncancelled state or // is cancelled but stuck in the outcall then terminate. // need to check both because some handlers may call the callback // to set the state done but still be stuck in an out call. if ( pCurHandler->fCancelled && BAD_HANDLERSTATE(pCurHandler) ) { TCHAR pszHandlerName[MAX_SYNCMGRHANDLERNAME + 1]; ConvertString(pszHandlerName, (pCurHandler->SyncMgrHandlerInfo).wszHandlerName, MAX_SYNCMGRHANDLERNAME); // yield because of message box in Terminate Handler call. Assert(!pCurHandler->fInTerminateCall); pCurHandler->fInTerminateCall = TRUE; clockqueue.Leave(); hr = pCurHandler->pThreadProxy->TerminateHandlerThread(pszHandlerName,TRUE); clockqueue.Enter(); pCurHandler->fInTerminateCall = FALSE; if (hr == S_OK) { LPHANDLERINFO pKilledHandler = pCurHandler; ForceCompleteOutCalls(pCurHandler); // now need to loop through remaining instances handlers off the same clsid // we just killed // CODE REVIEW: NOTENOTE: // pCurHandler is being assigned, not compared - its a '=', not a '==' while(pCurHandler = pCurHandler->pNextHandler) { if (pCurHandler->clsidHandler == pKilledHandler->clsidHandler) { // must meet original kil criteria if ( pCurHandler->fCancelled && BAD_HANDLERSTATE(pCurHandler) ) { HRESULT hrProxyTerminate; pCurHandler->fInTerminateCall = TRUE; clockqueue.Leave(); hrProxyTerminate = pCurHandler->pThreadProxy->TerminateHandlerThread(pszHandlerName,FALSE); clockqueue.Enter(); Assert(S_OK == hrProxyTerminate);// this should never fail. ForceCompleteOutCalls(pCurHandler); pCurHandler->fInTerminateCall = FALSE; } } } } // if handled one , break out and reqiure to be called again. break; } pCurHandler = pCurHandler->pNextHandler; } // finally loop through the queue and see if there are any more items to kill *pfItemToKill = FALSE; pCurHandler = m_pFirstHandler; while (pCurHandler) { // if handler is cancelled but still in a noncancelled state or // is cancelled but stuck in the outcall then terminate. // need to check both because some handlers may call the callback // to set the state done but still be stuck in an out call. if ( pCurHandler->fCancelled && BAD_HANDLERSTATE(pCurHandler) ) { *pfItemToKill = TRUE; break; } pCurHandler = pCurHandler->pNextHandler; } clockqueue.Leave(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::Cancel, public // // Synopsis: Set the current Handler Items in the queue // into cancel mode. // // The Different States Are. // If the item is waiting for <= PrepareForSync place in Release // If InPrepareForSync Skip Items and then Synchrnoize // will check the complete value before calling through // and if set will just release the Handler. // If Waiting to Synchronize Skip Items then let Synchronize // Check for complete value and just set release // If Item is currently In the Synchronize Skip all items // and then just let synchronize return // // Algorithm. If <= PrepareForSync then place in Release, Else if <= InSynchronize // then SkipTheItems // // Note: Relies on Synchronize setting handler state before calling // through to Handlers Synchronize Method. PrepareForSync should // also check this in case new PrepareforSync request comes // in during an out call in this routine. // // Arguments: // // Returns: Appropriate return codes // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::Cancel(void) { HRESULT hr = E_UNEXPECTED; LPHANDLERINFO pCurHandler; CLock clockqueue(this); clockqueue.Enter(); // don't do anything is still processing the last cancel request if (!m_fInCancelCall) { m_fInCancelCall = TRUE; // first thing set cancel to true on all handler items // so if sync,PrepareForSyncRequest comes in during SetItemStatus // out call it will be cancelled immediately. pCurHandler = m_pFirstHandler; while (pCurHandler) { pCurHandler->fCancelled = TRUE; pCurHandler = pCurHandler->pNextHandler; } // now loop through looking for any items that need to have // their item status set. // !!!remember new requests can come in so only make out call // if fCancelled is set. // !!! items can be removed from queue in between our cancel call // and when we return. pCurHandler = m_pFirstHandler; while (pCurHandler) { CThreadMsgProxy *pThreadProxy = pCurHandler->pThreadProxy; if (pCurHandler->fCancelled && pThreadProxy && (pCurHandler->HandlerState >= HANDLERSTATE_INPREPAREFORSYNC) && (pCurHandler->HandlerState <= HANDLERSTATE_INSYNCHRONIZE) ) { // could be in a setitemstatus call, if so then don't do another. // review - dup of SkipCode. should have a general purpose function // to call after setting what items should be cancelled. if (!(pCurHandler->dwOutCallMessages & ThreadMsg_SetItemStatus)) { pCurHandler->dwOutCallMessages |= ThreadMsg_SetItemStatus; clockqueue.Leave(); // send a reset to the hwnd we belong to if there is one if (m_hwndDlg) { SendMessage(m_hwndDlg,WM_PROGRESS_RESETKILLHANDLERSTIMER,0,0); } hr = pThreadProxy->SetItemStatus(GUID_NULL, SYNCMGRSTATUS_STOPPED); clockqueue.Enter(); pCurHandler->dwOutCallMessages &= ~ThreadMsg_SetItemStatus; } } else if (pCurHandler->HandlerState < HANDLERSTATE_INPREPAREFORSYNC) { LPITEMLIST pCurItem; pCurHandler->HandlerState = HANDLERSTATE_RELEASE; // need to setup HwndCallback so progres gets updated. // review, after ship why can't setup HwndCallback on transferqueueu pCurHandler->hWndCallback = m_hwndDlg; // if handler hansn't been kicked off yet, just reset the items ourselves pCurItem = pCurHandler->pFirstItem; while (pCurItem) { if (pCurItem->fIncludeInProgressBar) { SYNCMGRPROGRESSITEM SyncProgressItem; SyncProgressItem.cbSize = sizeof(SYNCMGRPROGRESSITEM); SyncProgressItem.mask = SYNCMGRPROGRESSITEM_PROGVALUE | SYNCMGRPROGRESSITEM_MAXVALUE | SYNCMGRPROGRESSITEM_STATUSTYPE; SyncProgressItem.iProgValue = HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE; SyncProgressItem.iMaxValue = HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE; SyncProgressItem.dwStatusType = SYNCMGRSTATUS_STOPPED; // set progress faking we are in an out call so any releasecompleted // handler that comes through doesn't release us. // if already in outCall then progress just won't get updated // until next time. if (!(pCurHandler->dwOutCallMessages & ThreadMsg_SetItemStatus)) { pCurHandler->dwOutCallMessages |= ThreadMsg_SetItemStatus; clockqueue.Leave(); Progress(pCurHandler->pHandlerId, pCurItem->offlineItem.ItemID,&SyncProgressItem); clockqueue.Enter(); pCurHandler->dwOutCallMessages &= ~ThreadMsg_SetItemStatus; } } pCurItem = pCurItem->pnextItem; } } pCurHandler = pCurHandler->pNextHandler; } m_fInCancelCall = FALSE; } clockqueue.Leave(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::MoveHandler, public // // Synopsis: Moves the Handler from a queue into this queue. // // Arguments: [pQueueMoveFrom] - Queue the handler is being moved from. // [pHandlerInfoMoveFrom] - Handler that is being moved // [ppHandlerId] - On Success contains the new HandlerID // // Returns: Appropriate return codes // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::MoveHandler(CHndlrQueue *pQueueMoveFrom, LPHANDLERINFO pHandlerInfoMoveFrom, HANDLERINFO **ppHandlerId, CLock *pclockQueue) { LPITEMLIST pCurItem = NULL; JOBINFO *pJobInfo = NULL; BOOL fHasItemsToSync = FALSE; ASSERT_LOCKHELD(this); // items should already be locked when this function is called. ASSERT_LOCKHELD(pQueueMoveFrom); if ( (QUEUETYPE_PROGRESS != m_QueueType) && (QUEUETYPE_CHOICE != m_QueueType) ) { Assert(QUEUETYPE_CHOICE == m_QueueType); Assert(QUEUETYPE_PROGRESS == m_QueueType); return E_UNEXPECTED; // review error code. } *ppHandlerId = 0; ++m_wHandlerCount; // pHandlerInfoMoveFrom->pHandlerId = m_wHandlerCount; pHandlerInfoMoveFrom->pNextHandler = NULL; *ppHandlerId = pHandlerInfoMoveFrom->pHandlerId; // now fix up the items duplicate flag information. pCurItem = pHandlerInfoMoveFrom->pFirstItem; while (pCurItem) { LPHANDLERINFO pHandlerMatched; LPITEMLIST pItemListMatch; // setup the information for the UI depending on if this item is check and // the state it is in. // if item is now within a valid range then uncheck it. if (SYNCMGRITEMSTATE_CHECKED == pCurItem->offlineItem.dwItemState && ( (pHandlerInfoMoveFrom->HandlerState < HANDLERSTATE_PREPAREFORSYNC) || (pHandlerInfoMoveFrom->HandlerState >= HANDLERSTATE_RELEASE) ) ) { Assert(pHandlerInfoMoveFrom->HandlerState >= HANDLERSTATE_PREPAREFORSYNC); // this should never happen. pCurItem->offlineItem.dwItemState = SYNCMGRITEMSTATE_UNCHECKED; } // setup the UI information based on if the item is checked. // or if its a hidden item. if ( (SYNCMGRITEMSTATE_UNCHECKED == pCurItem->offlineItem.dwItemState) || pCurItem->fHiddenItem) { SetItemProgressValues(pCurItem,HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE,HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE); pCurItem->fIncludeInProgressBar = FALSE; } else { fHasItemsToSync = TRUE; SetItemProgressValues(pCurItem,0,HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE); pCurItem->fIncludeInProgressBar = TRUE; } if (IsItemAlreadyInList(pHandlerInfoMoveFrom->clsidHandler, (pCurItem->offlineItem.ItemID), pHandlerInfoMoveFrom->pHandlerId, &pHandlerMatched,&pItemListMatch) ) { pCurItem->fDuplicateItem = TRUE; } else { Assert(FALSE == pCurItem->fDuplicateItem); // catch case of duplicate getting lost pCurItem->fDuplicateItem = FALSE; } pCurItem = pCurItem->pnextItem; } // if the item we are moving has a Proxy then update the proxy to the new queue. // We update this when the item is not attached to either queue. if (pHandlerInfoMoveFrom->pThreadProxy) { HANDLERINFO *pHandlerInfoArg = pHandlerInfoMoveFrom->pHandlerId; // set the proxy to point to the new information pHandlerInfoMoveFrom->pThreadProxy->SetProxyParams(m_hwndDlg ,m_dwQueueThreadId ,this ,pHandlerInfoArg); } // Add the handler to this list. if (NULL == m_pFirstHandler) { m_pFirstHandler = pHandlerInfoMoveFrom; // Assert(1 == m_wHandlerCount); // Review = HandlerCount doesn't have to be 1 if ReleaseCompltedHandlers has been called. } else { LPHANDLERINFO pCurHandlerInfo; pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo->pNextHandler) { pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } pCurHandlerInfo->pNextHandler = pHandlerInfoMoveFrom; } // if this is a progress queue and there are not items to sync for the // handler or the HandlerState isn't in PrepareForSync then set // the state to TransferRelease since it can be freed. if ((QUEUETYPE_PROGRESS == m_QueueType && !fHasItemsToSync ) || (pHandlerInfoMoveFrom->HandlerState != HANDLERSTATE_PREPAREFORSYNC)) { pHandlerInfoMoveFrom->HandlerState = HANDLERSTATE_TRANSFERRELEASE; } return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::TransferQueueData, public // // Synopsis: Moves the Items from one queue to another. Currently we only // support transferrring items from a choice queueu to a choice or // progress queue. Only handlers in the PREPAREFORSYNC state are moved // when transferring to a Progress queue. When transferring to a choice // queue only items in the ADDHANDLERITEMS state are moved. // // !!Warning - Cannot release lock during this process // // Arguments: [pQueueMoveFrom] - Queue to move items from. // [dwSyncFlags] - flags that started the sync // [pszConnectionName] - Connection the sync should be performed on, can be NULL // [szSchedulName] - Name of Schedule that started this Job. Can be NULL. // [hRasPendingEvent] - Event to signal when job is complete. Can be NULL. // // Returns: Appropriate return codes // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::TransferQueueData(CHndlrQueue *pQueueMoveFrom /* ,DWORD dwSyncFlags,TCHAR *pzConnectionName,TCHAR *szScheduleName */) { HRESULT hr = E_UNEXPECTED; HANDLERINFO HandlerInfoMoveFrom; LPHANDLERINFO pHandlerInfoMoveFrom = &HandlerInfoMoveFrom; CLock clockqueue(this); CLock clockqueueMoveFrom(pQueueMoveFrom); clockqueue.Enter(); clockqueueMoveFrom.Enter(); m_fNumItemsCompleteNeedsARecalc = TRUE; // need to recalc NumItems next time if ((QUEUETYPE_PROGRESS != m_QueueType && QUEUETYPE_CHOICE != m_QueueType) || QUEUETYPE_CHOICE != pQueueMoveFrom->m_QueueType) { Assert(QUEUETYPE_PROGRESS == m_QueueType || QUEUETYPE_CHOICE == m_QueueType); Assert(QUEUETYPE_CHOICE == pQueueMoveFrom->m_QueueType); } else if (NULL == pQueueMoveFrom->m_pFirstHandler) { // if no job info then there aren't any items to move. } else { JOBINFO *pMoveFromJobInfo = NULL; // transfer everything over and then release after done call freecompletedhandlers // to clean anything up. // transfer over all jobs Assert(pQueueMoveFrom->m_pFirstJobInfo); Assert(pQueueMoveFrom->m_pFirstJobInfo->pConnectionObj); pMoveFromJobInfo = pQueueMoveFrom->m_pFirstJobInfo; pQueueMoveFrom->m_pFirstJobInfo = NULL; if (NULL == m_pFirstJobInfo) { m_pFirstJobInfo = pMoveFromJobInfo; } else { JOBINFO *pCurLastJob = NULL; pCurLastJob = m_pFirstJobInfo; while (pCurLastJob->pNextJobInfo) { pCurLastJob = pCurLastJob->pNextJobInfo; } pCurLastJob->pNextJobInfo = pMoveFromJobInfo; } // loop through moving items, have to reassign the Handler ID and // !!Warning - This function does nothing with ListViewData it is up to the // caller to make sure this is set up properly // review - should just loop through fixing up necessary items and then // add entire list onto end. inneficient to do one at a time. pHandlerInfoMoveFrom->pNextHandler = pQueueMoveFrom->m_pFirstHandler; while (pHandlerInfoMoveFrom->pNextHandler) { LPHANDLERINFO pHandlerToMove; HANDLERINFO *pNewHandlerId; // Asserts for making sure the UI has been cleared from the queue Assert(FALSE == pHandlerInfoMoveFrom->pNextHandler->fHasErrorJumps); Assert(pHandlerInfoMoveFrom->pNextHandler->pJobInfo); // !!! Warning get next handler before transfer or next ptr will be invalid. pHandlerToMove = pHandlerInfoMoveFrom->pNextHandler; pHandlerInfoMoveFrom->pNextHandler = pHandlerToMove->pNextHandler; MoveHandler(pQueueMoveFrom,pHandlerToMove,&pNewHandlerId,&clockqueue); // now set the original queues head pQueueMoveFrom->m_pFirstHandler = HandlerInfoMoveFrom.pNextHandler; hr = S_OK; } } clockqueue.Leave(); clockqueueMoveFrom.Leave(); // now free any handlers that came into the queue that we // don't want to do anything with . ReleaseHandlers(HANDLERSTATE_TRANSFERRELEASE); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::SetQueueHwnd, public // // Synopsis: informs the queue os the new dialog owner if any // queue must also loop through existing proxies // and reset their hwnd. // // Arguments: // // Returns: Appropriate return codes // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::SetQueueHwnd(CBaseDlg *pDlg) { LPHANDLERINFO pCurHandlerInfo; CLock clockqueue(this); clockqueue.Enter(); m_pDlg = pDlg; if (m_pDlg) { m_hwndDlg = m_pDlg->GetHwnd(); } else { m_hwndDlg = NULL; } m_dwQueueThreadId = GetCurrentThreadId(); // make sure queu threadId is updated. pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo) { if (pCurHandlerInfo->pThreadProxy) { pCurHandlerInfo->pThreadProxy->SetProxyParams(m_hwndDlg ,m_dwQueueThreadId ,this ,pCurHandlerInfo->pHandlerId); } pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } clockqueue.Leave(); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::ReleaseCompletedHandlers, public // // Synopsis: Releases any Handlers that are in the Release or free // dead state from the queue. // // Arguments: // // Returns: Appropriate return codes // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::ReleaseCompletedHandlers() { return ReleaseHandlers(HANDLERSTATE_RELEASE); } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::FreeAllHandlers, public // // Synopsis: Releases all handlers from the queue. // // Arguments: // // Returns: Appropriate return codes // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::FreeAllHandlers(void) { return ReleaseHandlers(HANDLERSTATE_NEW); // release handlers in all states. } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::ReleaseHandlers, public // // Synopsis: Releases any Handlers are in a state >= the requested state // // Arguments: HandlerState - Frees all handlers that have a state >= the requested state. // // !!Warning: This should be the only place the proxy if freed and // the handler is removed from the list. // // Returns: Appropriate return codes // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::ReleaseHandlers(HANDLERSTATE HandlerState) { HANDLERINFO HandlerInfoStart; LPHANDLERINFO pPrevHandlerInfo = &HandlerInfoStart; LPHANDLERINFO pCurHandlerInfo = NULL; LPHANDLERINFO pHandlerFreeList = NULL; LPITEMLIST pCurItem = NULL; LPITEMLIST pNextItem = NULL; CLock clockqueue(this); ASSERT_LOCKNOTHELD(this); // shouldn't be any out calls in progress when this is called. clockqueue.Enter(); m_fNumItemsCompleteNeedsARecalc = TRUE; // need to recalc next GetProgress. // loop through the handlers finding the one that match the criteria // removing them from list and adding them to the free list // we do this so don't have to worry about someone else accessing // handlers we are freeing during an out call. if (HANDLERSTATE_NEW == HandlerState) { // Release should only be called on this state if caller is sure no out // calls are in progress or else handler may not exist when // they come back pHandlerFreeList = m_pFirstHandler; m_pFirstHandler = NULL; } else { Assert(HandlerState >= HANDLERSTATE_RELEASE); // if in release no out calls are in progress. pPrevHandlerInfo->pNextHandler = m_pFirstHandler; while (pPrevHandlerInfo->pNextHandler) { pCurHandlerInfo = pPrevHandlerInfo->pNextHandler; // if meet handler state criteria and not in any out calls then can // remove from list. // if request for HANDLERSTATE_NEW then assert than there shouldn't be // any out calls in progress or terminating. Assert(!(HandlerState == HANDLERSTATE_NEW) || (0 == pCurHandlerInfo->dwOutCallMessages && !pCurHandlerInfo->fInTerminateCall)); if ( (HandlerState <= pCurHandlerInfo->HandlerState) && (0 == pCurHandlerInfo->dwOutCallMessages) && !(pCurHandlerInfo->fInTerminateCall)) { Assert (HANDLERSTATE_RELEASE == pCurHandlerInfo->HandlerState || HANDLERSTATE_TRANSFERRELEASE == pCurHandlerInfo->HandlerState || HANDLERSTATE_HASERRORJUMPS == pCurHandlerInfo->HandlerState || HANDLERSTATE_DEAD == pCurHandlerInfo->HandlerState); // remove from queue list and add to free. pPrevHandlerInfo->pNextHandler = pCurHandlerInfo->pNextHandler; pCurHandlerInfo->pNextHandler = pHandlerFreeList; pHandlerFreeList = pCurHandlerInfo; } else { // if no match then just continue. pPrevHandlerInfo = pCurHandlerInfo; } } // update the queue head. m_pFirstHandler = HandlerInfoStart.pNextHandler; } // now loop through the free list freeing the items. while (pHandlerFreeList) { pCurHandlerInfo = pHandlerFreeList; pHandlerFreeList = pHandlerFreeList->pNextHandler; // if the item has a job info release the reference on it. if (pCurHandlerInfo->pJobInfo) { ReleaseJobInfo(pCurHandlerInfo->pJobInfo); pCurHandlerInfo->pJobInfo = NULL; } if (pCurHandlerInfo->pThreadProxy) { CThreadMsgProxy *pThreadProxy = pCurHandlerInfo->pThreadProxy; HWND hwndCallback; Assert(HANDLERSTATE_DEAD != pCurHandlerInfo->HandlerState); pCurHandlerInfo->HandlerState = HANDLERSTATE_DEAD; pThreadProxy = pCurHandlerInfo->pThreadProxy; pCurHandlerInfo->pThreadProxy = NULL; hwndCallback = pCurHandlerInfo->hWndCallback; pCurHandlerInfo->hWndCallback = NULL; clockqueue.Leave(); // release lock when making the OutCall. pThreadProxy->Release(); // review, don't release proxy to try to catch race condition. clockqueue.Enter(); } pCurItem = pCurHandlerInfo->pFirstItem; while (pCurItem) { pNextItem = pCurItem->pnextItem; FREE(pCurItem); pCurItem = pNextItem; } FREE(pCurHandlerInfo); } clockqueue.Leave(); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::GetHandlerInfo, public // // Synopsis: Gets Data associated with the HandlerID and ItemID // // Arguments: [clsidHandler] - ClsiId Of Handler the Item belongs too // // Returns: Appropriate return codes // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::GetHandlerInfo(REFCLSID clsidHandler, LPSYNCMGRHANDLERINFO pSyncMgrHandlerInfo) { HRESULT hr = S_FALSE; LPHANDLERINFO pCurHandlerInfo = NULL; CLock clockqueue(this); clockqueue.Enter(); // find first handler that matches the request CLSID pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo ) { if (clsidHandler == pCurHandlerInfo->clsidHandler) { *pSyncMgrHandlerInfo = pCurHandlerInfo->SyncMgrHandlerInfo; hr = S_OK; break; } pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } clockqueue.Leave(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::GetHandlerInfo, public // // Synopsis: Gets Data associated with the HandlerID and ItemID // // Arguments: [wHandlerId] - Id Of Handler the Item belongs too // // Returns: Appropriate return codes // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::GetHandlerInfo(HANDLERINFO *pHandlerId, LPSYNCMGRHANDLERINFO pSyncMgrHandlerInfo) { HRESULT hr = S_FALSE; LPHANDLERINFO pHandlerInfo = NULL; CLock clockqueue(this); clockqueue.Enter(); if (S_OK == LookupHandlerFromId(pHandlerId,&pHandlerInfo)) { *pSyncMgrHandlerInfo = pHandlerInfo->SyncMgrHandlerInfo; hr = S_OK; } clockqueue.Leave(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::GetItemDataAtIndex, public // // Synopsis: Gets Data associated with the HandlerID and ItemID // // Arguments: [wHandlerId] - Id Of Handler the Item belongs too // [wItemID] - Identifies the Item in the Handler // [pclsidHandler] - on return contains a pointer to the clsid of the Handler // [offlineItem] - on returns contains a pointer to the OfflineItem for the item. // [pfHiddenItem] - On return is a bool indicating if this item is hidden. // // Returns: Appropriate return codes // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::GetItemDataAtIndex(HANDLERINFO *pHandlerId,WORD wItemID, CLSID *pclsidHandler,SYNCMGRITEM *offlineItem,BOOL *pfHiddenItem) { BOOL fFoundMatch = FALSE; LPHANDLERINFO pCurHandlerInfo = NULL; LPITEMLIST pCurItem = NULL; CLock clockqueue(this); clockqueue.Enter(); pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo && !fFoundMatch) { // only valid if Hanlder is in the PrepareForSync state. if (pHandlerId == pCurHandlerInfo->pHandlerId) // see if CLSID matches { // see if handler info has a matching item pCurItem = pCurHandlerInfo->pFirstItem; while (pCurItem) { if (wItemID == pCurItem->wItemId) { fFoundMatch = TRUE; break; } pCurItem = pCurItem->pnextItem; } } if (!fFoundMatch) pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } if (fFoundMatch) { if (pclsidHandler) { *pclsidHandler = pCurHandlerInfo->clsidHandler; } if (offlineItem) { *offlineItem = pCurItem->offlineItem; } if (pfHiddenItem) { *pfHiddenItem = pCurItem->fHiddenItem; } } clockqueue.Leave(); return fFoundMatch ? S_OK : S_FALSE; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::GetItemDataAtIndex, public // // Synopsis: Gets Data associated with the HandlerID and OfflineItemID // // Arguments: [wHandlerId] - Id Of Handler the Item belongs too // [ItemID] - identifies the Item by its OfflineItemID // [pclsidHandler] - on return contains a pointer to the clsid of the Handler // [offlineItem] - on returns contains a pointer to the OfflineItem for the item. // [pfHiddenItem] - On return is a bool indicating if this item is a hidden item. // // Returns: Appropriate return codes // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::GetItemDataAtIndex(HANDLERINFO *pHandlerId,REFSYNCMGRITEMID ItemID,CLSID *pclsidHandler, SYNCMGRITEM *offlineItem,BOOL *pfHiddenItem) { BOOL fFoundMatch = FALSE; LPHANDLERINFO pCurHandlerInfo = NULL; LPITEMLIST pCurItem = NULL; CLock clockqueue(this); clockqueue.Enter(); pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo && !fFoundMatch) { // only valid if handler is in the PrepareForSync state. if (pHandlerId == pCurHandlerInfo->pHandlerId) // see if CLSID matches { // see if handler info has a matching item pCurItem = pCurHandlerInfo->pFirstItem; while (pCurItem) { if (ItemID == pCurItem->offlineItem.ItemID) { fFoundMatch = TRUE; break; } pCurItem = pCurItem->pnextItem; } } if (!fFoundMatch) pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } if (fFoundMatch) { *pclsidHandler = pCurHandlerInfo->clsidHandler; *offlineItem = pCurItem->offlineItem; *pfHiddenItem = pCurItem->fHiddenItem; } clockqueue.Leave(); return fFoundMatch ? S_OK : S_FALSE; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::FindFirstItemInState, public // // Synopsis: Finds the first Item in the queue that matches the given state. // // Arguments: // [hndlrState] - specifies matching state we are looking for. // [pwHandlerId] - on return contains the HandlerID of the Item // [pwItemID] - on returns contains the ItemID of the item in the queue. // // Returns: S_OK if an Item was found with an unassigned ListView. // S_FALSE - if no Item was found. // Appropriate error return codes // // Modifies: // // History: 30-Jul-98 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::FindFirstItemInState(HANDLERSTATE hndlrState, HANDLERINFO **ppHandlerId,WORD *pwItemID) { return FindNextItemInState(hndlrState,0,0,ppHandlerId,pwItemID); } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::FindNextItemInState, public // // Synopsis: Finds the first Item in the queue that matches the given state. // after the specified item. // // Arguments: // [hndlrState] - specifies matching state we are looking for. // [pOfflineItemID] - on returns contains a pointer to the OfflineItem for the item. // [pwHandlerId] - on return contains the HandlerID of the Item // [pwItemID] - on returns contains the ItemID of the item in the queue. // // Returns: S_OK if an Item was found with an unassigned ListView. // S_FALSE - if no Item was found. // Appropriate error return codes // // Modifies: // // History: 30-Jul-98 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::FindNextItemInState(HANDLERSTATE hndlrState, HANDLERINFO *pLastHandlerId,WORD wLastItemID, HANDLERINFO **ppHandlerId,WORD *pwItemID) { BOOL fFoundMatch = FALSE; LPHANDLERINFO pCurHandlerInfo = NULL; LPITEMLIST pCurItem = NULL; CLock clockqueue(this); clockqueue.Enter(); pCurHandlerInfo = m_pFirstHandler; if (pLastHandlerId) { // loop until find the specified handler or hit end of list. while(pCurHandlerInfo && pLastHandlerId != pCurHandlerInfo->pHandlerId) { pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } if (NULL == pCurHandlerInfo) // reached end of list without finding the Handler { Assert(0); // user must have passed an invalid start HandlerID. clockqueue.Leave(); return S_FALSE; } // loop until find item or end of item list pCurItem = pCurHandlerInfo->pFirstItem; while (pCurItem && pCurItem->wItemId != wLastItemID) { pCurItem = pCurItem->pnextItem; } if (NULL == pCurItem) // reached end of item list without finding the specified item { Assert(0); // user must have passed an invalid start ItemID. clockqueue.Leave(); return S_FALSE; } // now we found the Handler and item. loop through remaining items for this handler // if it still has another item then just return that. pCurItem = pCurItem->pnextItem; if (pCurItem) { Assert(hndlrState == pCurHandlerInfo->HandlerState); // should only be called in PrepareForSyncState fFoundMatch = TRUE; } if (!fFoundMatch) pCurHandlerInfo = pCurHandlerInfo->pNextHandler; // increment to next handler if no match } if (!fFoundMatch) { // in didn't find a match in the while (pCurHandlerInfo) { if ((hndlrState == pCurHandlerInfo->HandlerState) && (pCurHandlerInfo->pFirstItem) ) { pCurItem = pCurHandlerInfo->pFirstItem; fFoundMatch = TRUE; break; } pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } } if (fFoundMatch) { *ppHandlerId = pCurHandlerInfo->pHandlerId; *pwItemID = pCurItem->wItemId; } clockqueue.Leave(); return fFoundMatch ? S_OK : S_FALSE; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::SetItemState, public // // Synopsis: Set the Item state for the first item finds that it // matches in the Queue. Sets all other matches to unchecked. // // Arguments: // // Returns: Appropriate error return codes // // Modifies: // // History: 30-Jul-98 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::SetItemState(REFCLSID clsidHandler,REFSYNCMGRITEMID ItemID,DWORD dwState) { LPHANDLERINFO pCurHandlerInfo = NULL; LPITEMLIST pCurItem = NULL; BOOL fFoundMatch = FALSE; CLock clockqueue(this); if (QUEUETYPE_CHOICE != m_QueueType) { Assert(QUEUETYPE_CHOICE == m_QueueType); return E_UNEXPECTED; } clockqueue.Enter(); pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo) { if (clsidHandler == pCurHandlerInfo->clsidHandler) { pCurItem = pCurHandlerInfo->pFirstItem; while (pCurItem) { if (ItemID == pCurItem->offlineItem.ItemID) { // if the handlerstate is not prepareforsync or not first match then uncheck if ((HANDLERSTATE_PREPAREFORSYNC != pCurHandlerInfo->HandlerState) || fFoundMatch) { pCurItem->offlineItem.dwItemState = SYNCMGRITEMSTATE_UNCHECKED; } else { pCurItem->offlineItem.dwItemState = dwState; fFoundMatch = TRUE; } } pCurItem = pCurItem->pnextItem; } } pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } clockqueue.Leave(); Assert(fFoundMatch); // we should have found at least one match. return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::SkipItem, public // // Synopsis: loop through handler and mark the items appropriately that match.. // // Arguments: [iItem] - List View Item to skip. // // Returns: Appropriate return codes. // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::SkipItem(REFCLSID clsidHandler,REFSYNCMGRITEMID ItemID) { LPHANDLERINFO pCurHandlerInfo = NULL; LPITEMLIST pCurItem = NULL; CLock clockqueue(this); HRESULT hr = S_OK; if (QUEUETYPE_PROGRESS != m_QueueType) { Assert(QUEUETYPE_PROGRESS == m_QueueType); return E_UNEXPECTED; } clockqueue.Enter(); pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo ) { if (clsidHandler == pCurHandlerInfo->clsidHandler) { pCurItem = pCurHandlerInfo->pFirstItem; while (pCurItem) { // if item is cancelled then also treat as a match to // handle case cancel came in while in an out call. if ( ItemID == pCurItem->offlineItem.ItemID) { // found an item, now if it hasn't started the sync or // is not already complete set the value. if ((pCurHandlerInfo->HandlerState < HANDLERSTATE_RELEASE) ) { pCurItem->fItemCancelled = TRUE; // If haven't called PrepareforSync yet then // set uncheck the item so it isn't passed to PrepareForSync // If PrepareForSync has already been called, call the items // SkipMethod. if already in a setItemstatus call for this handler don't // do anything. // if not in another setitemstatus call loop through freeing all // the items that have the cancel set. // essentially a dup of cance and also handle case cancel // comes win while this handler is in an out call. if (!(pCurHandlerInfo->dwOutCallMessages & ThreadMsg_SetItemStatus)) { pCurHandlerInfo->dwOutCallMessages |= ThreadMsg_SetItemStatus; if (pCurHandlerInfo->HandlerState >= HANDLERSTATE_INPREPAREFORSYNC && pCurItem->fSynchronizingItem ) { CThreadMsgProxy *pThreadProxy; SYNCMGRITEMID ItemId; pThreadProxy = pCurHandlerInfo->pThreadProxy; ItemId = pCurItem->offlineItem.ItemID; clockqueue.Leave(); if (pThreadProxy) { hr = pThreadProxy->SetItemStatus(ItemId, SYNCMGRSTATUS_SKIPPED); } clockqueue.Enter(); } else { // once done skipping handler if state is <= preparefor sync we set the state accordingly. // if were syncing up to handler. if ( (pCurHandlerInfo->HandlerState <= HANDLERSTATE_PREPAREFORSYNC) && (pCurItem->fIncludeInProgressBar) ) { SYNCMGRPROGRESSITEM SyncProgressItem; // unheck the state so PrepareForsync doesn't include // this item. pCurItem->offlineItem.dwItemState = SYNCMGRITEMSTATE_UNCHECKED; SyncProgressItem.cbSize = sizeof(SYNCMGRPROGRESSITEM); SyncProgressItem.mask = SYNCMGRPROGRESSITEM_PROGVALUE | SYNCMGRPROGRESSITEM_MAXVALUE | SYNCMGRPROGRESSITEM_STATUSTYPE; SyncProgressItem.iProgValue = HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE; SyncProgressItem.iMaxValue = HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE; SyncProgressItem.dwStatusType = SYNCMGRSTATUS_SKIPPED; // need to setup HwndCallback so progres gets updated. // review, after ship why can't setup HwndCallback on transferqueueu pCurHandlerInfo->hWndCallback = m_hwndDlg; clockqueue.Leave(); Progress(pCurHandlerInfo->pHandlerId, pCurItem->offlineItem.ItemID,&SyncProgressItem); clockqueue.Enter(); } } pCurHandlerInfo->dwOutCallMessages &= ~ThreadMsg_SetItemStatus; } } } pCurItem = pCurItem->pnextItem; } } pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } clockqueue.Leave(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::ItemHasProperties, public // // Synopsis: determines if the item in the queue has properties. // Uses the first item match it finds. // // Arguments: // // Returns: Appropriate error return codes // // Modifies: // // History: 30-Jul-98 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::ItemHasProperties(REFCLSID clsidHandler,REFSYNCMGRITEMID ItemID) { LPHANDLERINFO pHandlerInfo; LPITEMLIST pItem; HRESULT hr = S_FALSE; CLock clockqueue(this); Assert(QUEUETYPE_CHOICE == m_QueueType); ASSERT_LOCKNOTHELD(this); clockqueue.Enter(); // item is guidNULL this is toplevel so use the getHandlerInfo, else see // if the item supports showProperties. if (S_OK == FindItemData(clsidHandler,ItemID, HANDLERSTATE_PREPAREFORSYNC,HANDLERSTATE_PREPAREFORSYNC,&pHandlerInfo,&pItem)) { if (GUID_NULL == ItemID) { Assert(NULL == pItem); hr = ((pHandlerInfo->SyncMgrHandlerInfo).SyncMgrHandlerFlags & SYNCMGRHANDLER_HASPROPERTIES) ? S_OK : S_FALSE; } else { Assert(pItem); if (pItem) { hr = (pItem->offlineItem).dwFlags & SYNCMGRITEM_HASPROPERTIES ? S_OK : S_FALSE; } else { hr = S_FALSE; } } } clockqueue.Leave(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::ShowProperties, public // // Synopsis: Calls the ShowProperties Method on the first items it finds. // Uses the first item match it finds. // // Arguments: // // Returns: Appropriate error return codes // // Modifies: // // History: 30-Jul-98 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::ShowProperties(REFCLSID clsidHandler,REFSYNCMGRITEMID ItemID,HWND hwndParent) { LPHANDLERINFO pHandlerInfo; LPHANDLERINFO pHandlerId = NULL; LPITEMLIST pItem; HRESULT hr = E_UNEXPECTED; BOOL fHandlerCalled = FALSE; BOOL fHasProperties = FALSE; CThreadMsgProxy *pThreadProxy; CLock clockqueue(this); Assert(QUEUETYPE_CHOICE == m_QueueType); ASSERT_LOCKNOTHELD(this); clockqueue.Enter(); if (S_OK == FindItemData(clsidHandler,ItemID, HANDLERSTATE_PREPAREFORSYNC,HANDLERSTATE_PREPAREFORSYNC,&pHandlerInfo,&pItem)) { Assert(HANDLERSTATE_PREPAREFORSYNC == pHandlerInfo->HandlerState); pThreadProxy = pHandlerInfo->pThreadProxy; pHandlerId = pHandlerInfo->pHandlerId; Assert(!(ThreadMsg_ShowProperties & pHandlerInfo->dwOutCallMessages)); pHandlerInfo->dwOutCallMessages |= ThreadMsg_ShowProperties; if (GUID_NULL == ItemID && pHandlerInfo) { Assert(NULL == pItem); fHasProperties = (pHandlerInfo->SyncMgrHandlerInfo).SyncMgrHandlerFlags & SYNCMGRHANDLER_HASPROPERTIES ? TRUE : FALSE; } else if (pItem) { Assert(SYNCMGRITEM_HASPROPERTIES & pItem->offlineItem.dwFlags); fHasProperties = (pItem->offlineItem).dwFlags & SYNCMGRITEM_HASPROPERTIES ? TRUE : FALSE; } else { fHasProperties = FALSE; } clockqueue.Leave(); // make sure properties flag isn't set. if (fHasProperties && pThreadProxy ) { fHandlerCalled = TRUE; hr = pThreadProxy->ShowProperties(hwndParent,ItemID); } else { AssertSz(0,"ShowProperties called on an Item without properties"); hr = S_FALSE; } Assert(pHandlerId); if ( (fHandlerCalled && (FAILED(hr))) || (!fHandlerCalled) ) { GUID guidCompletion = ItemID; CallCompletionRoutine(pHandlerId,ThreadMsg_ShowProperties,hr,1,&guidCompletion); // since called completion routine map anything but S_OK to S_FALSE; // so caller doesn't wait for the callback. if (S_OK != hr) { hr = S_FALSE; } } } else { Assert(FAILED(hr)); // should return some failure so caller knows callback isn't coming. clockqueue.Leave(); } return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::ReEnumHandlerItems, public // // Synopsis: Deletes any Items associated with any handlers that // match the clsid of the handler and then // call the first handlers in the list enumeration method // again. // // Arguments: // // Returns: Appropriate error return codes // // Modifies: // // History: 30-Jul-98 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::ReEnumHandlerItems(REFCLSID clsidHandler,REFSYNCMGRITEMID ItemID) { LPHANDLERINFO pCurHandlerInfo = NULL; HANDLERINFO *pHandlerId = NULL; LPITEMLIST pCurItem = NULL; CLock clockqueue(this); if (QUEUETYPE_CHOICE != m_QueueType) { Assert(QUEUETYPE_CHOICE == m_QueueType); return E_UNEXPECTED; } clockqueue.Enter(); pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo) { if (clsidHandler == pCurHandlerInfo->clsidHandler) { LPITEMLIST pNextItem; // if first handler we found update the handlerID if (pHandlerId) { pHandlerId = pCurHandlerInfo->pHandlerId; pCurHandlerInfo->HandlerState = HANDLERSTATE_ADDHANDLERTEMS; // put back to addhandlerItems statest } pCurHandlerInfo->wItemCount = 0; pCurItem = pCurHandlerInfo->pFirstItem; while (pCurItem) { pNextItem = pCurItem->pnextItem; FREE(pCurItem); pCurItem = pNextItem; } pCurHandlerInfo->pFirstItem = NULL; } pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } clockqueue.Leave(); // if have a handler id add them back to the queue if (pHandlerId) { DWORD cbNumItemsAdded; AddHandlerItemsToQueue(pHandlerId,&cbNumItemsAdded); } return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::IsItemCompleted, private // // Synopsis: Given an handler item determines if its // synchronization is completed // // !!!This is not efficient. n! solution. If get // a lot of items may need to have to rewrite // and cache some information concerning dup // items. // // Arguments: [wHandlerId] - Handler the item belongs too. // [wItemID] - Identifies the Item // // Returns: // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- BOOL CHndlrQueue::IsItemCompleted(LPHANDLERINFO pHandler,LPITEMLIST pItem) { CLSID clsidHandler; SYNCMGRITEMID ItemId; int iItemNotComplete = 0; Assert(pHandler); Assert(pItem); ASSERT_LOCKHELD(this); // caller of this function should have already locked the queue. clsidHandler = pHandler->clsidHandler; ItemId = pItem->offlineItem.ItemID; // back up to beginning of handler to simplify logic // items must be pCurItem->fIncludeInProgressBar && !pCurItem->fProgressBarHandled // to count toward not being a completion; while (pHandler) { if (pHandler->clsidHandler == clsidHandler) { // see if handler info has a matching item pItem = pHandler->pFirstItem; while (pItem) { if (pItem->offlineItem.ItemID == ItemId) { if (pItem->fIncludeInProgressBar && !pItem->fProgressBarHandled) { if (pItem->iProgValue < pItem->iProgMaxValue) { ++iItemNotComplete; } pItem->fProgressBarHandled = TRUE; } } pItem = pItem->pnextItem; } } pHandler = pHandler->pNextHandler; } return iItemNotComplete ? FALSE : TRUE; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::SetItemProgressInfo, public // // Synopsis: Updates the stored progress information for the // Associated Items // // Arguments: [wHandlerId] - Handler the item belongs too. // [wItemID] - Identifies the Item // [pSyncProgressItem] - Pointer to Progress Information. // [pfProgressChanged] - returns true if any progress values were changed // for the item // // Returns: S_OK - at least one item with the iItem assigned was found // S_FALSE - Item does not have properties. // Appropriate error return codes // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::SetItemProgressInfo(HANDLERINFO *pHandlerId,WORD wItemID, LPSYNCMGRPROGRESSITEM pSyncProgressItem, BOOL *pfProgressChanged) { HRESULT hr = E_UNEXPECTED; LPHANDLERINFO pHandlerInfo = NULL; BOOL fFoundMatch = FALSE; LPITEMLIST pCurItem = NULL; CLock clockqueue(this); Assert(pfProgressChanged); *pfProgressChanged = FALSE; if (QUEUETYPE_PROGRESS != m_QueueType) { Assert(QUEUETYPE_PROGRESS == m_QueueType); return E_UNEXPECTED; // review error code. } clockqueue.Enter(); if (S_OK == LookupHandlerFromId(pHandlerId,&pHandlerInfo)) { // try to find the matching item. pCurItem = pHandlerInfo->pFirstItem; while (pCurItem) { if (wItemID == pCurItem->wItemId) { fFoundMatch = TRUE; break; } pCurItem = pCurItem->pnextItem; } } if (fFoundMatch) { SetItemProgressInfo(pCurItem,pSyncProgressItem,pfProgressChanged); } clockqueue.Leave(); return fFoundMatch ? S_OK : S_FALSE; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::SetItemProgressInfo, private // // Synopsis: Updates the stored progress information for the // Associated iTEM // // Arguments: [pItem] - Identifies the Item // [pSyncProgressItem] - Pointer to Progress Information. // [pfProgressChanged] - returns true if any progress values were changed // for the item // // Returns: S_OK - at least one item with the iItem assigned was found // Appropriate error return codes // // !!Caller must have already taken a lock. // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::SetItemProgressInfo(LPITEMLIST pItem,LPSYNCMGRPROGRESSITEM pSyncProgressItem, BOOL *pfProgressChanged) { BOOL fProgressAlreadyCompleted; ASSERT_LOCKHELD(this); // caller of this function should have already locked the queue. // progress is considered complete if Values is >= Maxa fProgressAlreadyCompleted = (pItem->iProgMaxValue <= pItem->iProgValue); if (SYNCMGRPROGRESSITEM_MAXVALUE & pSyncProgressItem->mask) { // if Progress Max Value is negative then don't set. if (pSyncProgressItem->iMaxValue >= 0) { if (pItem->iProgMaxValue != pSyncProgressItem->iMaxValue) { *pfProgressChanged = TRUE; pItem->fProgValueDirty = TRUE; pItem->iProgMaxValue = pSyncProgressItem->iMaxValue; } } } if (SYNCMGRPROGRESSITEM_PROGVALUE & pSyncProgressItem->mask) { // if progress value is negative, don't change it if (pSyncProgressItem->iProgValue > 0) { if (pItem->iProgValue != pSyncProgressItem->iProgValue) { *pfProgressChanged = TRUE; pItem->fProgValueDirty = TRUE; pItem->iProgValue = pSyncProgressItem->iProgValue; } } } if (SYNCMGRPROGRESSITEM_STATUSTYPE & pSyncProgressItem->mask) { if (pItem->dwStatusType != pSyncProgressItem->dwStatusType) { *pfProgressChanged = TRUE; pItem->dwStatusType = pSyncProgressItem->dwStatusType; // if status is complete set the progvalue == to the max // on behalf of the handler so the Items completed and progress bar // gets updated. if (pItem->dwStatusType == SYNCMGRSTATUS_SKIPPED || pItem->dwStatusType == SYNCMGRSTATUS_SUCCEEDED || pItem->dwStatusType == SYNCMGRSTATUS_FAILED ) { pItem->fProgValueDirty = TRUE; pItem->iProgValue = pItem->iProgMaxValue; } } } // if progressValue is > max then set it to max if (pItem->iProgValue > pItem->iProgMaxValue) { // AssertSz(0,"Progress Value is > Max"); pItem->iProgValue = pItem->iProgMaxValue; } // see if need to recalc numItems completed next time // GetProgressInfo is Called. BOOL fProgressCompletedNow = (pItem->iProgMaxValue <= pItem->iProgValue); if (fProgressAlreadyCompleted != fProgressCompletedNow) { m_fNumItemsCompleteNeedsARecalc = TRUE; } return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::SetItemProgressValues, private // // Synopsis: Private helper function for updating/initializing // an items progress bar values. // // Arguments: [pItem] - Identifies the Item // [pSyncProgressItem] - Pointer to Progress Information. // [pfProgressChanged] - returns true if any progress values were changed // for the item // // Returns: S_OK - at least one item with the iItem assigned was found // Appropriate error return codes // // !!Caller must have already taken a lock. // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::SetItemProgressValues(LPITEMLIST pItem,INT iProgValue,INT iProgMaxValue) { SYNCMGRPROGRESSITEM SyncProgressItem; BOOL fProgressChanged; ASSERT_LOCKHELD(this); // caller of this function should have already locked the queue. SyncProgressItem.cbSize = sizeof(SYNCMGRPROGRESSITEM); SyncProgressItem.mask = SYNCMGRPROGRESSITEM_PROGVALUE | SYNCMGRPROGRESSITEM_MAXVALUE; SyncProgressItem.iProgValue = iProgValue; SyncProgressItem.iMaxValue = iProgMaxValue; return SetItemProgressInfo(pItem,&SyncProgressItem,&fProgressChanged); } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::GetProgressInfo, public // // Synopsis: calculates current progress bar values and number of items complete. // // Arguments: [piProgValue] - on return contains the new Progress Bar Value. // [piMaxValue] - on return contains the Progress Bar Max Value // [piNumItemsComplete] - on returns contains number of items complete. // [iNumItemsTotal] - on returns contains number of total items. // // Returns: Appropriate error codes // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::GetProgressInfo(INT *piProgValue,INT *piMaxValue,INT *piNumItemsComplete, INT *piNumItemsTotal) { LPHANDLERINFO pCurHandlerInfo = NULL; LPITEMLIST pCurItem = NULL; INT iCurValue; BOOL fNormalizedValueChanged = FALSE; CLock clockqueue(this); if (QUEUETYPE_PROGRESS != m_QueueType) { Assert(QUEUETYPE_PROGRESS == m_QueueType); return E_UNEXPECTED; // review error code. } clockqueue.Enter(); // if m_fNumItemsCompleteNeedsARecalc is set need // to recalc normalized and numItems Comlete and Total Items. if (m_fNumItemsCompleteNeedsARecalc) { INT iNormalizedMax; m_ulProgressItemCount = 0; // get the number of selected items in the queue. pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo) { // see if handler info has a matching item pCurItem = pCurHandlerInfo->pFirstItem; while (pCurItem) { if (pCurItem->fIncludeInProgressBar) { //if this item should be included in the progress, increment the progress bar count. ++m_ulProgressItemCount; pCurItem->fProgressBarHandled = FALSE; // reset handled } pCurItem = pCurItem->pnextItem; } pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } if (0 == m_ulProgressItemCount) { iNormalizedMax = 0; } else { iNormalizedMax = MAXPROGRESSVALUE/m_ulProgressItemCount; if (0 == iNormalizedMax) { iNormalizedMax = 1; } } if (m_iNormalizedMax != iNormalizedMax) { fNormalizedValueChanged = TRUE; m_iNormalizedMax = iNormalizedMax; } } // now loop thruogh again getting total CurValue and finished items // we say an item is finished if it is out of the synchronize method or the min==max. pCurHandlerInfo = m_pFirstHandler; iCurValue = 0; // if numitemcount needs updated reset the member vars if (m_fNumItemsCompleteNeedsARecalc) { m_iCompletedItems = 0; m_iItemCount = 0; } while (pCurHandlerInfo) { // see if handler info has a matching item pCurItem = pCurHandlerInfo->pFirstItem; while (pCurItem) { if (pCurItem->fIncludeInProgressBar) { // if Progress is dirty or normalized value changed // need to recalc this items progress value if (pCurItem->fProgValueDirty || fNormalizedValueChanged) { int iProgValue = pCurItem->iProgValue; int iProgMaxValue = pCurItem->iProgMaxValue; if (iProgValue && iProgMaxValue) { pCurItem->iProgValueNormalized = (int) ((1.0*iProgValue*m_iNormalizedMax)/iProgMaxValue); } else { pCurItem->iProgValueNormalized = 0; } pCurItem->fProgValueDirty = FALSE; } iCurValue += pCurItem->iProgValueNormalized; // Handle NumItems needing to be recalc'd if (m_fNumItemsCompleteNeedsARecalc && !pCurItem->fProgressBarHandled) { ++m_iItemCount; // now loop through this item and remaining items and if any match // mark as handled and if complete then incrment the compleated count; if (IsItemCompleted(pCurHandlerInfo,pCurItem)) { ++m_iCompletedItems; } } } pCurItem = pCurItem->pnextItem; } pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } m_fNumItemsCompleteNeedsARecalc = FALSE; *piProgValue = iCurValue; *piMaxValue = m_iNormalizedMax*m_ulProgressItemCount; *piNumItemsComplete = m_iCompletedItems; *piNumItemsTotal = m_iItemCount; Assert(*piProgValue <= *piMaxValue); clockqueue.Leave(); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::RemoveFinishedProgressItems, public // // Synopsis: Loops through handler setting any finished items // fIncludeInProgressBar value to false // // Arguments: // // Returns: Appropriate return codes. // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::RemoveFinishedProgressItems() { LPHANDLERINFO pCurHandlerInfo = NULL; LPITEMLIST pCurItem = NULL; CLock clockqueue(this); clockqueue.Enter(); m_fNumItemsCompleteNeedsARecalc = TRUE; pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo) { // mark any items that have completed their synchronization. if (HANDLERSTATE_INSYNCHRONIZE < pCurHandlerInfo->HandlerState) { // see if handler info has a matching item pCurItem = pCurHandlerInfo->pFirstItem; while (pCurItem) { pCurItem->fIncludeInProgressBar = FALSE; pCurItem = pCurItem->pnextItem; } } pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } clockqueue.Leave(); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::AreAnyItemsSelectedInQueue, public // // Synopsis: Determines if there are any items selected in the queue. // can be called by choice dialog for example before creating // progress and doing a transfer since there is no need to // if nothing to sync anyways. // // Arguments: // // Returns: TRUE - At least one item is selected inthe queue // FALSE - No Items are slected in the queue. // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- BOOL CHndlrQueue::AreAnyItemsSelectedInQueue() { LPHANDLERINFO pCurHandlerInfo = NULL; LPITEMLIST pCurItem = NULL; BOOL fFoundSelectedItem = FALSE; CLock clockqueue(this); clockqueue.Enter(); // invalidate UI that applies to entire queue pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo && !fFoundSelectedItem) { // if handler state is less than a completion go ahead and // check the items. if (HANDLERSTATE_HASERRORJUMPS > pCurHandlerInfo->HandlerState) { pCurItem = pCurHandlerInfo->pFirstItem; while (pCurItem) { // clear Item UI information if (pCurItem->offlineItem.dwItemState == SYNCMGRITEMSTATE_CHECKED) { fFoundSelectedItem = TRUE; break; } pCurItem = pCurItem->pnextItem; } } pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } clockqueue.Leave(); return fFoundSelectedItem; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::PersistChoices, public // // Synopsis: Saves Selected Users choices for the next time // the choice dialog is brought up. Only should // be called from a choice queue. // // Arguments: // // Returns: Appropriate return codes. // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::PersistChoices(void) { LPHANDLERINFO pCurHandlerInfo = NULL; LPITEMLIST pCurItem = NULL; CLock clockqueue(this); if (QUEUETYPE_CHOICE != m_QueueType) { Assert(QUEUETYPE_CHOICE == m_QueueType); return S_FALSE; } ASSERT_LOCKNOTHELD(this); clockqueue.Enter(); // currently only persist on a manual invoke since user // has to go to settings to change other invoke types and // that is persisted // since this is the choice queue we know all handlers have the // same JobID. if this ever changes, have to set on a case by // case basis. if (m_pFirstJobInfo && m_pFirstJobInfo->pConnectionObj && (SYNCMGRFLAG_MANUAL == (m_pFirstJobInfo->dwSyncFlags & SYNCMGRFLAG_EVENTMASK)) ) { TCHAR *pszConnectionName = m_pFirstJobInfo->pConnectionObj[0]->pwszConnectionName; DWORD dwSyncFlags = m_pFirstJobInfo->dwSyncFlags; Assert(1 == m_pFirstJobInfo->cbNumConnectionObjs); // assert manual only ever has one connectionObj // delete all previously stored preferences. // this is valid because only called from choice queue that all ConnectionNames are the same. if (!m_fItemsMissing) { RegRemoveManualSyncSettings(pszConnectionName); } pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo) { // only save if Handler is in the PrepareForSync state. // bug, need to make sure return code from enum wasn't missing items if (HANDLERSTATE_PREPAREFORSYNC == pCurHandlerInfo->HandlerState ) { // save out these items. pCurItem = pCurHandlerInfo->pFirstItem; while (pCurItem) { if (!pCurItem->fDuplicateItem) { switch(dwSyncFlags & SYNCMGRFLAG_EVENTMASK) { case SYNCMGRFLAG_MANUAL: RegSetSyncItemSettings(SYNCTYPE_MANUAL, pCurHandlerInfo->clsidHandler, pCurItem->offlineItem.ItemID, pszConnectionName, pCurItem->offlineItem.dwItemState, NULL); break; default: AssertSz(0,"UnknownSyncFlag Event"); break; }; } pCurItem = pCurItem->pnextItem; } } pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } } clockqueue.Leave(); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::FindFirstHandlerInState, public // // Synopsis: Finds the first Handler that matches the specified // state in the queue. // // Arguments: [hndlrState] - Requested handler state. // [pwHandlerID] - on success filled with HandlerID that was found // // Returns: Appropriate return codes. // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::FindFirstHandlerInState(HANDLERSTATE hndlrState, REFCLSID clsidHandler, HANDLERINFO **ppHandlerId,CLSID *pMatchHandlerClsid) { return FindNextHandlerInState(0, clsidHandler,hndlrState, ppHandlerId, pMatchHandlerClsid); } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::FindNextHandlerInState, public // // Synopsis: Finds next handler after LastHandlerID in the queue that matches // the requested state. Passing in 0 for the LastHandlerID is the same // as calling FindFirstHandlerInState // // if GUID_NULL is passed in for the clsidHandler the first handler that // matches the specified state is returned. // // Arguments: [wLastHandlerID] - Id of last handler found. // [clsidHandler] - specific handler classid is requested, only find matches with this clsid // [hndlrState] - Requested handler state. // [pwHandlerID] - on success filled with HandlerID that was found // [pMatchHandlerClsid] - on sucdess clsid of handler found. // // Returns: Appropriate return codes. // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::FindNextHandlerInState(HANDLERINFO *pLastHandlerID,REFCLSID clsidHandler, HANDLERSTATE hndlrState,HANDLERINFO **ppHandlerID,CLSID *pMatchHandlerClsid) { HRESULT hr = S_FALSE; LPHANDLERINFO pCurHandler; CLock clockqueue(this); *ppHandlerID = 0; clockqueue.Enter(); pCurHandler = m_pFirstHandler; if (pLastHandlerID) { // loop foward until find the last handlerID we checked or hit the end while (pCurHandler) { if (pLastHandlerID == pCurHandler->pHandlerId) { break; } pCurHandler = pCurHandler->pNextHandler; } if (pCurHandler) { pCurHandler = pCurHandler->pNextHandler; // increment to next handler. } } while (pCurHandler) { if ( (hndlrState == pCurHandler->HandlerState) && ((GUID_NULL == clsidHandler) || (clsidHandler == pCurHandler->clsidHandler)) ) { *ppHandlerID = pCurHandler->pHandlerId; *pMatchHandlerClsid = pCurHandler->clsidHandler; hr = S_OK; break; } pCurHandler = pCurHandler->pNextHandler; } clockqueue.Leave(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::CreateServer, public // // Synopsis: Creates a new proxy then calls proxy to create and instance of the // specified handler. // // Arguments: [wHandlerId] - Id of handler to call. // [pCLSIDServer] - CLSID of Handler to Create. // // Returns: Appropriate return codes. // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::CreateServer(HANDLERINFO *pHandlerId, const CLSID *pCLSIDServer) { HRESULT hr = E_UNEXPECTED; LPHANDLERINFO pHandlerInfo = NULL; CLock clockqueue(this); ASSERT_LOCKNOTHELD(this); clockqueue.Enter(); if (S_OK == LookupHandlerFromId(pHandlerId,&pHandlerInfo)) { Assert(HANDLERSTATE_CREATE == pHandlerInfo->HandlerState); Assert(0 == pHandlerInfo->dwCallNestCount); pHandlerInfo->dwCallNestCount++; if (HANDLERSTATE_CREATE == pHandlerInfo->HandlerState) { pHandlerInfo->HandlerState = HANDLERSTATE_INCREATE; Assert(NULL == pHandlerInfo->pThreadProxy); // see if there is already a thread for this handler's // CLSID. hr = CreateHandlerThread(&(pHandlerInfo->pThreadProxy), m_hwndDlg, *pCLSIDServer); if (S_OK == hr) { HANDLERINFO *pHandlerIdArg; CThreadMsgProxy *pThreadProxy; pHandlerIdArg = pHandlerInfo->pHandlerId; pThreadProxy = pHandlerInfo->pThreadProxy; clockqueue.Leave(); hr = pThreadProxy->CreateServer(pCLSIDServer,this,pHandlerIdArg); clockqueue.Enter(); pHandlerInfo->pThreadProxy = pThreadProxy; } if (S_OK == hr) { pHandlerInfo->clsidHandler = *pCLSIDServer; pHandlerInfo->HandlerState = HANDLERSTATE_INITIALIZE; } else { pHandlerInfo->HandlerState = HANDLERSTATE_RELEASE; } } pHandlerInfo->dwCallNestCount--; } clockqueue.Leave(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::Initialize, public // // Synopsis: Calls Hanlder's Initialize method // // Arguments: [wHandlerId] - Id of handler to call. // [dwReserved] - Initialize reserved parameter // [dwSyncFlags] - Sync flags // [cbCookie] - size of cookie data // [lpCookie] - ptr to cookie data // // Returns: Appropriate return codes. // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::Initialize(HANDLERINFO *pHandlerId,DWORD dwReserved,DWORD dwSyncFlags, DWORD cbCookie,const BYTE *lpCooke) { HRESULT hr = E_UNEXPECTED; LPHANDLERINFO pHandlerInfo = NULL; CLock clockqueue(this); ASSERT_LOCKNOTHELD(this); clockqueue.Enter(); if (S_OK == LookupHandlerFromId(pHandlerId, &pHandlerInfo)) { Assert(HANDLERSTATE_INITIALIZE == pHandlerInfo->HandlerState); Assert(pHandlerInfo->pThreadProxy); Assert(0 == pHandlerInfo->dwCallNestCount); pHandlerInfo->dwCallNestCount++; if ( (HANDLERSTATE_INITIALIZE == pHandlerInfo->HandlerState) && (pHandlerInfo->pThreadProxy) ) { CThreadMsgProxy *pThreadProxy; Assert(dwSyncFlags & SYNCMGRFLAG_EVENTMASK); // an event should be set pHandlerInfo->HandlerState = HANDLERSTATE_ININITIALIZE; pThreadProxy = pHandlerInfo->pThreadProxy; clockqueue.Leave(); hr = pThreadProxy->Initialize(dwReserved,dwSyncFlags,cbCookie,lpCooke); clockqueue.Enter(); if (S_OK == hr) { pHandlerInfo->HandlerState = HANDLERSTATE_ADDHANDLERTEMS; } else { pHandlerInfo->HandlerState = HANDLERSTATE_RELEASE; } } pHandlerInfo->dwCallNestCount--; } clockqueue.Leave(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::AddHandlerItemsToQueue, public // // Synopsis: Calls through to proxy to add items to the queue // // Arguments: [wHandlerId] - Id of handler to call. // // Returns: Appropriate return codes. // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::AddHandlerItemsToQueue(HANDLERINFO *pHandlerId,DWORD *pcbNumItems) { HRESULT hr = E_UNEXPECTED; LPHANDLERINFO pHandlerInfo = NULL; CLock clockqueue(this); ASSERT_LOCKNOTHELD(this); clockqueue.Enter(); Assert(pcbNumItems); Assert(QUEUETYPE_CHOICE == m_QueueType); // items should only be added in a choice queue. *pcbNumItems = 0; if (S_OK == LookupHandlerFromId(pHandlerId,&pHandlerInfo)) { Assert(0 == pHandlerInfo->dwCallNestCount); pHandlerInfo->dwCallNestCount++; Assert(HANDLERSTATE_ADDHANDLERTEMS == pHandlerInfo->HandlerState); Assert(pHandlerInfo->pThreadProxy); if ( (HANDLERSTATE_ADDHANDLERTEMS == pHandlerInfo->HandlerState) && (pHandlerInfo->pThreadProxy) ) { CThreadMsgProxy *pThreadProxy; pHandlerInfo->HandlerState = HANDLERSTATE_INADDHANDLERITEMS; pThreadProxy = pHandlerInfo->pThreadProxy; clockqueue.Leave(); // on return all items should be filled in. hr = pThreadProxy->AddHandlerItems(NULL /* HWND */,pcbNumItems); clockqueue.Enter(); if ( (S_OK == hr) || (S_SYNCMGR_MISSINGITEMS == hr) ) { if (S_SYNCMGR_MISSINGITEMS == hr) m_fItemsMissing = TRUE; hr = S_OK; // review, need to handler missing items in registry. pHandlerInfo->HandlerState = HANDLERSTATE_PREPAREFORSYNC; } else { // on an error, go ahead and release the proxy if server can't enum pHandlerInfo->HandlerState = HANDLERSTATE_RELEASE; *pcbNumItems = 0; } } pHandlerInfo->dwCallNestCount--; } clockqueue.Leave(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::GetItemObject, public // // Synopsis: Calls through to proxy to get an items object pointer // // Arguments: [wHandlerId] - Id of handler to call. // [wItemID] - ID of item to get the object of. // [riid] - interface requested of the object // [ppv] - on success is a pointer to the newly created object // // Returns: Currently all handlers should return E_NOTIMPL. // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::GetItemObject(HANDLERINFO *pHandlerId,WORD wItemID,REFIID riid,void** ppv) { HRESULT hr = E_UNEXPECTED; LPHANDLERINFO pHandlerInfo = NULL; CLock clockqueue(this); Assert(QUEUETYPE_PROGRESS == m_QueueType); ASSERT_LOCKNOTHELD(this); clockqueue.Enter(); if (S_OK == LookupHandlerFromId(pHandlerId,&pHandlerInfo)) { LPITEMLIST pCurItem; Assert(HANDLERSTATE_PREPAREFORSYNC == pHandlerInfo->HandlerState); Assert(pHandlerInfo->pThreadProxy); Assert(0 == pHandlerInfo->dwCallNestCount); pHandlerInfo->dwCallNestCount++; if ( (HANDLERSTATE_PREPAREFORSYNC == pHandlerInfo->HandlerState) && (pHandlerInfo->pThreadProxy)) { // now try to find the item. pCurItem = pHandlerInfo->pFirstItem; while (pCurItem) { if (wItemID == pCurItem->wItemId) { CThreadMsgProxy *pThreadProxy; SYNCMGRITEMID ItemID; pThreadProxy = pHandlerInfo->pThreadProxy; ItemID = pCurItem->offlineItem.ItemID; clockqueue.Leave(); hr = pThreadProxy->GetItemObject(ItemID,riid,ppv); return hr; } pCurItem = pCurItem->pnextItem; } } pHandlerInfo->dwCallNestCount--; } clockqueue.Leave(); AssertSz(0, "Specified object wasn't found"); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::SetUpProgressCallback, public // // Synopsis: Calls through to proxy to set up the progress callback // // Arguments: [wHandlerId] - Id of handler to call. // [fSet] - TRUE == create, FALSE == destroy. // [hwnd] - Callback info should be sent to specified window. // // Returns: Appropriate Error code // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::SetUpProgressCallback(HANDLERINFO *pHandlerId,BOOL fSet,HWND hwnd) { HRESULT hr = E_UNEXPECTED; LPHANDLERINFO pHandlerInfo = NULL; CLock clockqueue(this); AssertSz(0,"this function no longer used"); return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::PrepareForSync, public // // Synopsis: Calls through to Handlers PrepareForSync method. // // Arguments: [wHandlerId] - Id of handler to call. // [hWndParent] - Hwnd to use for any displayed dialogs. // // Returns: Appropriate Error code // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::PrepareForSync(HANDLERINFO *pHandlerId,HWND hWndParent) { HRESULT hr = E_UNEXPECTED; LPHANDLERINFO pHandlerInfo = NULL; ULONG cbNumItems; SYNCMGRITEMID *pItemIDs; BOOL fHandlerCalled = FALSE; CLock clockqueue(this); Assert(QUEUETYPE_PROGRESS == m_QueueType); ASSERT_LOCKNOTHELD(this); clockqueue.Enter(); if (S_OK == LookupHandlerFromId(pHandlerId,&pHandlerInfo)) { Assert(HANDLERSTATE_PREPAREFORSYNC == pHandlerInfo->HandlerState); Assert(pHandlerInfo->pThreadProxy); Assert(0 == pHandlerInfo->dwCallNestCount); pHandlerInfo->dwCallNestCount++; Assert(!(ThreadMsg_PrepareForSync & pHandlerInfo->dwOutCallMessages)); pHandlerInfo->dwOutCallMessages |= ThreadMsg_PrepareForSync; if (HANDLERSTATE_PREPAREFORSYNC == pHandlerInfo->HandlerState) { // if item doesn't have a ThreadProxy or it has been cancelled, // put in the Release State if ( (NULL == pHandlerInfo->pThreadProxy) || (pHandlerInfo->fCancelled) ) { pHandlerInfo->HandlerState = HANDLERSTATE_RELEASE; } else { // create a list of the selected items and pass to PrepareForSync cbNumItems = GetSelectedItemsInHandler(pHandlerInfo,0,NULL); if (0 == cbNumItems) { // if no items selected don't call prepareforsync // and set the HandlerState so it can be released pHandlerInfo->HandlerState = HANDLERSTATE_RELEASE; hr = S_FALSE; } else { pItemIDs = (SYNCMGRITEMID *) ALLOC(sizeof(SYNCMGRITEMID)*cbNumItems); if (pItemIDs) { // loop through items filling in the proper data GetSelectedItemsInHandler(pHandlerInfo,&cbNumItems,pItemIDs); if (0 == cbNumItems) { hr = S_FALSE; // There are no selected items. } else { CThreadMsgProxy *pThreadProxy; JOBINFO *pJobInfo = NULL; pHandlerInfo->HandlerState = HANDLERSTATE_INPREPAREFORSYNC; pThreadProxy = pHandlerInfo->pThreadProxy; pHandlerInfo->hWndCallback = hWndParent; pJobInfo = pHandlerInfo->pJobInfo; // if we need to dial to make the connection do // so now. clockqueue.Leave(); DWORD dwSyncFlags = pJobInfo->dwSyncFlags & SYNCMGRFLAG_EVENTMASK; BOOL fAutoDialDisable = TRUE; if ( dwSyncFlags == SYNCMGRFLAG_MANUAL || dwSyncFlags == SYNCMGRFLAG_INVOKE ) fAutoDialDisable = FALSE; // // Ignore failure return from ApplySyncItemDialState // ApplySyncItemDialState( fAutoDialDisable ); hr = OpenConnection(pJobInfo); if (S_OK == hr) { // if this is on an idle write out the last // handler id // review - if don't wait to call PrepareForSync // on idle the setlastIdlehandler should be called on sync. if (pJobInfo && (SYNCMGRFLAG_IDLE == (pJobInfo->dwSyncFlags & SYNCMGRFLAG_EVENTMASK)) ) { SetLastIdleHandler(pHandlerInfo->clsidHandler); } fHandlerCalled = TRUE; hr = pThreadProxy->PrepareForSync(cbNumItems, pItemIDs, hWndParent, 0 /* dwReserved */ ); } else { clockqueue.Enter(); pHandlerInfo->hWndCallback = NULL; clockqueue.Leave(); } } // on return from PrepareFroSync or error need to free items clockqueue.Enter(); FREE(pItemIDs); } else { hr = E_OUTOFMEMORY; } } } } } clockqueue.Leave(); // if the handler returns an errorfrom PrepareForSync we need // to call the completion routine ourselves and/or we never got to the point // of making the outcall. if ( (fHandlerCalled && (S_OK != hr)) || (!fHandlerCalled)) { CallCompletionRoutine(pHandlerId,ThreadMsg_PrepareForSync,hr,0,NULL); } return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::PrepareForSyncCompleted, public // // Synopsis: Called by completion routine on a PrepareForSyncCompleted // // Warning: Assume queue is locked and pHandlerInfo has // already been verified. // // Returns: Appropriate Error code // // Modifies: // // History: 02-Jun-98 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::PrepareForSyncCompleted(LPHANDLERINFO pHandlerInfo,HRESULT hCallResult) { ASSERT_LOCKHELD(this); // caller of this function should have already locked the queue. if (S_OK == hCallResult) { pHandlerInfo->HandlerState = HANDLERSTATE_SYNCHRONIZE; } else { pHandlerInfo->HandlerState = HANDLERSTATE_RELEASE; } if ( (pHandlerInfo->HandlerState != HANDLERSTATE_SYNCHRONIZE)) { // if handler didn't make it to the synchronize state then fix up the items LPITEMLIST pCurItem = NULL; // prepare for sync either doesn't want to handle the // items or an error occured, // need to go ahead and mark the items as completed. // same routine that is after synchronize. pCurItem = pHandlerInfo->pFirstItem; while (pCurItem) { SetItemProgressValues(pCurItem, HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE, HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE); pCurItem->fSynchronizingItem = FALSE; pCurItem = pCurItem->pnextItem; } } pHandlerInfo->dwCallNestCount--; // decrement nestcount put on by PrepareForSync call. // if the handler state has been released but it has some jumptext, which it can if // the PrepareForsync was caused by a retry then set the results to HANDLERSTATE_HASERRORJUMPS if ((HANDLERSTATE_RELEASE == pHandlerInfo->HandlerState) && (pHandlerInfo->fHasErrorJumps)) { pHandlerInfo->HandlerState = HANDLERSTATE_HASERRORJUMPS; } return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::Synchronize, public // // Synopsis: Calls through to Handlers Synchronize method. // // Arguments: [wHandlerId] - Id of handler to call. // [hWndParent] - Hwnd to use for any displayed dialogs. // // Returns: Appropriate Error code // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::Synchronize(HANDLERINFO *pHandlerId,HWND hWndParent) { HRESULT hr = E_UNEXPECTED; LPHANDLERINFO pHandlerInfo = NULL; BOOL fHandlerCalled = FALSE; CLock clockqueue(this); Assert(QUEUETYPE_PROGRESS == m_QueueType); ASSERT_LOCKNOTHELD(this); clockqueue.Enter(); if (S_OK == LookupHandlerFromId(pHandlerId,&pHandlerInfo)) { Assert(HANDLERSTATE_SYNCHRONIZE == pHandlerInfo->HandlerState); Assert(pHandlerInfo->pThreadProxy); Assert(0 == pHandlerInfo->dwCallNestCount); pHandlerInfo->dwCallNestCount++; Assert(!(ThreadMsg_Synchronize & pHandlerInfo->dwOutCallMessages)); pHandlerInfo->dwOutCallMessages |= ThreadMsg_Synchronize; if ( (HANDLERSTATE_SYNCHRONIZE == pHandlerInfo->HandlerState) && (pHandlerInfo->pThreadProxy) ) { // make sure the handler has a proxy and the item // wasn't cancelled. if ( (NULL == pHandlerInfo->pThreadProxy) || (pHandlerInfo->fCancelled)) { pHandlerInfo->HandlerState = HANDLERSTATE_RELEASE; } else { CThreadMsgProxy *pThreadProxy; pHandlerInfo->HandlerState = HANDLERSTATE_INSYNCHRONIZE; pThreadProxy= pHandlerInfo->pThreadProxy; clockqueue.Leave(); fHandlerCalled = TRUE; hr = pThreadProxy->Synchronize(hWndParent); clockqueue.Enter(); } } } clockqueue.Leave(); // if the handler returns an error from Synchronize we need // to call the completion routine ourselves and/or we never got to the point // of making the outcall. if ( (fHandlerCalled && (S_OK != hr)) || (!fHandlerCalled) ) { CallCompletionRoutine(pHandlerId,ThreadMsg_Synchronize,hr,0,NULL); } return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::SynchronizeCompleted, public // // Synopsis: Called by completion routine on a SynchronizeCompleted // // Warning: Assume queue is locked and pHandlerInfo has // already been verified. // // Returns: Appropriate Error code // // Modifies: // // History: 02-Jun-98 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::SynchronizeCompleted(LPHANDLERINFO pHandlerInfo,HRESULT hCallResult) { LPITEMLIST pCurItem = NULL; BOOL fRetrySync = FALSE; ASSERT_LOCKHELD(this); // caller of this function should have already locked the queue. if (pHandlerInfo->fRetrySync) { // if a retry request came in during the sync, retry it. pHandlerInfo->HandlerState = HANDLERSTATE_PREPAREFORSYNC; pHandlerInfo->fRetrySync = FALSE; // reset the retry sync flag. fRetrySync = TRUE; } else if (pHandlerInfo->fHasErrorJumps) { pHandlerInfo->HandlerState = HANDLERSTATE_HASERRORJUMPS; } else { pHandlerInfo->HandlerState = HANDLERSTATE_RELEASE; } // when come out of synchronize we set the items values for them. // in case they were negligent. pCurItem = pHandlerInfo->pFirstItem; while (pCurItem) { SetItemProgressValues(pCurItem, HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE, HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE); pCurItem->fSynchronizingItem = FALSE; pCurItem = pCurItem->pnextItem; } pHandlerInfo->dwCallNestCount--; // remove nest count // if the handler state has been released but it has some jumptext, which it can if // the sycnrhonize was caused by a retry then set the results to HANDLERSTATE_HASERRORJUMPS if ((HANDLERSTATE_RELEASE == pHandlerInfo->HandlerState) && (pHandlerInfo->fHasErrorJumps)) { pHandlerInfo->HandlerState = HANDLERSTATE_HASERRORJUMPS; } return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::ShowError, public // // Synopsis: Calls through to Handlers ShowError method. // // Arguments: [wHandlerId] - Id of handler to call. // [hWndParent] - Hwnd to use for any displayed dialogs. // [dwErrorID] - Identifies the error to show // // Returns: Appropriate Error code // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::ShowError(HANDLERINFO *pHandlerId,HWND hWndParent,REFSYNCMGRERRORID ErrorID) { HRESULT hr = E_UNEXPECTED; LPHANDLERINFO pHandlerInfo = NULL; BOOL fHandlerCalled = FALSE; BOOL fAlreadyInShowErrors = TRUE; CLock clockqueue(this); Assert(QUEUETYPE_PROGRESS == m_QueueType); clockqueue.Enter(); if (S_OK == LookupHandlerFromId(pHandlerId,&pHandlerInfo)) { Assert(pHandlerInfo->fHasErrorJumps); Assert(pHandlerInfo->pThreadProxy); // if we are already handling a ShowError for this handler then don't // start another one if (!(pHandlerInfo->fInShowErrorCall)) { fAlreadyInShowErrors = FALSE; m_dwShowErrororOutCallCount++; // increment handlers ShowError OutCall Count. Assert(!(ThreadMsg_ShowError & pHandlerInfo->dwOutCallMessages)); pHandlerInfo->dwOutCallMessages |= ThreadMsg_ShowError; if (pHandlerInfo->pThreadProxy ) { CThreadMsgProxy *pThreadProxy; ULONG cbNumItems = 0; // review, these are not longer necessary. SYNCMGRITEMID *pItemIDs = NULL; pThreadProxy = pHandlerInfo->pThreadProxy; fHandlerCalled = TRUE; pHandlerInfo->fInShowErrorCall = TRUE; clockqueue.Leave(); hr = pThreadProxy->ShowError(hWndParent,ErrorID,&cbNumItems,&pItemIDs); clockqueue.Enter(); } } } clockqueue.Leave(); // if the handler returns an error from ShowError we need // to call the completion routine ourselves and/or we never got to the point // of making the outcall and there wasn't already an outcall in progress. if ( (fHandlerCalled && (S_OK != hr)) || (!fHandlerCalled && !fAlreadyInShowErrors) ) { CallCompletionRoutine(pHandlerId,ThreadMsg_ShowError,hr,0,NULL); } return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::ShowErrorCompleted, public // // Synopsis: Called by completion routine on a ShowErrorCompleted // // Warning: Assume queue is locked and pHandlerInfo has // already been verified. // // Returns: // // Modifies: // // History: 02-Jun-98 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::ShowErrorCompleted(LPHANDLERINFO pHandlerInfo,HRESULT hCallResult, ULONG cbNumItems,SYNCMGRITEMID *pItemIDs) { ASSERT_LOCKHELD(this); // caller of this function should have already locked the queue. if (S_SYNCMGR_RETRYSYNC == hCallResult) { // validate we got something back for cbNumItems and pItemIDs or // don't do anything if ( (0 == cbNumItems) || (NULL == pItemIDs)) { Assert(cbNumItems); // assert in debug so can catch handlers. Assert(pItemIDs); } else { SYNCMGRITEMID *pCurItemItemId; ULONG cbNumItemsIndex; // if the handler is in the release state then change to prepareForSync // if it is still in a synchronize just set the fRetrySync flag in the // handler for it to check when done. // Cases // Handlers PrepareForSync Method hasn't been called. Just add items to request // Handlers is between InPrepareForSync and InSynchronize. Set RetrySyncFlag // Handler has is done with it synchronize. reset state to PrepareForSync // when prepareforsync is called on an item it state gets set back to unchecked // so just need to worry about setting appropriate items to checked. pCurItemItemId = pItemIDs; for (cbNumItemsIndex = 0 ; cbNumItemsIndex < cbNumItems; cbNumItemsIndex++) { BOOL fFoundMatch = FALSE; LPITEMLIST pHandlerItem; pHandlerItem = pHandlerInfo->pFirstItem; while (pHandlerItem) { if (*pCurItemItemId == pHandlerItem->offlineItem.ItemID) { pHandlerItem->offlineItem.dwItemState = SYNCMGRITEMSTATE_CHECKED; SetItemProgressValues(pHandlerItem,0, HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE); pHandlerItem->fIncludeInProgressBar = TRUE; fFoundMatch = TRUE; break; } pHandlerItem = pHandlerItem->pnextItem; } if (!fFoundMatch) { LPITEMLIST pNewItem; SYNCMGRITEM syncItem; // if didn't find a match this must be a new item, add it to the list // and set up the appropriate states. // Note: items added like this should not be included in the progress bar. // first time progress is called on an item it will get included // in the progress bar. syncItem.cbSize = sizeof(SYNCMGRITEM); syncItem.dwFlags = SYNCMGRITEM_TEMPORARY; syncItem.ItemID = *pCurItemItemId; syncItem.dwItemState = SYNCMGRITEMSTATE_CHECKED; syncItem.hIcon = NULL; *syncItem.wszItemName = L'\0'; pNewItem = AllocNewHandlerItem(pHandlerInfo,&syncItem); if (pNewItem) { pNewItem->offlineItem.dwItemState = SYNCMGRITEMSTATE_CHECKED; SetItemProgressValues(pNewItem, HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE, HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE); pNewItem->fHiddenItem = TRUE; // set to indicate not part of UI. pNewItem->fIncludeInProgressBar = FALSE; } } ++pCurItemItemId; } if (pHandlerInfo->HandlerState < HANDLERSTATE_INPREPAREFORSYNC) { // don't reset anything. just make sure requested items are added // to the request. } else if (pHandlerInfo->HandlerState > HANDLERSTATE_INSYNCHRONIZE) { // if synchronize is complete reset the state to PrepareForSync. pHandlerInfo->HandlerState = HANDLERSTATE_PREPAREFORSYNC; } else { // retry request came in between the PrepareForSync call and Synchronize // being complete. Assert(pHandlerInfo->HandlerState >= HANDLERSTATE_INPREPAREFORSYNC); Assert(pHandlerInfo->HandlerState < HANDLERSTATE_DEAD); pHandlerInfo->fRetrySync = TRUE; } // // If the handler has been canceled, uncancel it to enable the retry // pHandlerInfo->fCancelled = FALSE; } } --m_dwShowErrororOutCallCount; // decrement handlers ShowError OutCall Count. // should never happen but in case out call goes negative fixup to zero. Assert( ((LONG) m_dwShowErrororOutCallCount) >= 0); if ( ((LONG) m_dwShowErrororOutCallCount) < 0) { m_dwShowErrororOutCallCount = 0; } pHandlerInfo->fInShowErrorCall = FALSE; // handler is no longer in a ShowError Call return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::FindItemData, private // // Synopsis: finds associated handler and item info. caller must be // holding the lock and access the returned info before // releasing the lock // // !! Only matches items that have a state between or equal // to the handler state ranges. // // !!! If ItemID of GUID_NULL is passed it it returns a match // of the first handler found and sets pItem out param to NULL // // Arguments: // // Returns: Appropriate return codes // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::FindItemData(CLSID clsidHandler,REFSYNCMGRITEMID OfflineItemID, HANDLERSTATE hndlrStateFirst,HANDLERSTATE hndlrStateLast, LPHANDLERINFO *ppHandlerInfo,LPITEMLIST *ppItem) { LPHANDLERINFO pCurHandlerInfo = NULL; LPITEMLIST pCurItem = NULL; BOOL fNoMatchFound = TRUE; HRESULT hr = S_FALSE; ASSERT_LOCKHELD(this); Assert(ppHandlerInfo); Assert(ppItem); *ppHandlerInfo = NULL; *ppItem = NULL; pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo && fNoMatchFound) { if ( (clsidHandler == pCurHandlerInfo->clsidHandler) && (hndlrStateLast >= pCurHandlerInfo->HandlerState) && (hndlrStateFirst <= pCurHandlerInfo->HandlerState) ) { *ppHandlerInfo = pCurHandlerInfo; // if top level item tem ppItem to NULL and return okay if (GUID_NULL == OfflineItemID) { *ppItem = NULL; hr = S_OK; fNoMatchFound = FALSE; } else { pCurItem = pCurHandlerInfo->pFirstItem; while (pCurItem) { if (OfflineItemID == pCurItem->offlineItem.ItemID) { *ppItem = pCurItem; hr = S_OK; fNoMatchFound = FALSE; break; } pCurItem = pCurItem->pnextItem; } } } pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::LookupHandlerFromId, private // // Synopsis: Finds associate handler info from the given Id // // Arguments: [wHandlerId] - Id of handler to call. // [pHandlerInfo] - on S_OK pointer to handler info // // Returns: Appropriate Error code // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::LookupHandlerFromId(HANDLERINFO *pHandlerId,LPHANDLERINFO *pHandlerInfo) { HRESULT hr = E_UNEXPECTED; LPHANDLERINFO pCurItem; ASSERT_LOCKHELD(this); // caller of this function should have already locked the queue. *pHandlerInfo = NULL; pCurItem = m_pFirstHandler; while (pCurItem) { if (pHandlerId == pCurItem->pHandlerId ) { *pHandlerInfo = pCurItem; Assert(pCurItem == pHandlerId); hr = S_OK; break; } pCurItem = pCurItem->pNextHandler; } Assert(S_OK == hr); // test assert to see if ever fires. return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::AllocNewHandlerItem, public // // Synopsis: Adds new item to the specified handler. // // Arguments: [wHandlerId] - Id of handler. // [pOfflineItem] - Points to Items information to add. // // Returns: Appropriate Error code // // Modifies: // // History: 13-May-98 rogerg Created. // //---------------------------------------------------------------------------- LPITEMLIST CHndlrQueue::AllocNewHandlerItem(LPHANDLERINFO pHandlerInfo,SYNCMGRITEM *pOfflineItem) { LPITEMLIST pNewItem = NULL; ASSERT_LOCKHELD(this); // caller of this function should have already locked the queue. Assert(pHandlerInfo); Assert(pOfflineItem); // Allocate the item. pNewItem = (LPITEMLIST) ALLOC(sizeof(ITEMLIST)); if (pNewItem) { // set up defaults. memset(pNewItem, 0, sizeof(ITEMLIST)); pNewItem->wItemId = ++pHandlerInfo->wItemCount; pNewItem->pHandlerInfo = pHandlerInfo; pNewItem->fDuplicateItem = FALSE; pNewItem->fIncludeInProgressBar = FALSE; SetItemProgressValues(pNewItem, 0, HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE); pNewItem->dwStatusType = SYNCMGRSTATUS_PENDING; pNewItem->fHiddenItem = FALSE; pNewItem->fSynchronizingItem = FALSE; pNewItem->offlineItem = *pOfflineItem; // stick the item on the end of the list if (NULL == pHandlerInfo->pFirstItem) { pHandlerInfo->pFirstItem = pNewItem; Assert(1 == pHandlerInfo->wItemCount); } else { LPITEMLIST pCurItem; pCurItem = pHandlerInfo->pFirstItem; while (pCurItem->pnextItem) pCurItem = pCurItem->pnextItem; pCurItem->pnextItem = pNewItem; Assert ((pCurItem->wItemId + 1) == pNewItem->wItemId); } } return pNewItem; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::SetHandlerInfo, public // // Synopsis: Adds item to the specified handler. // Called in context of the handlers thread. // // Arguments: [pHandlerId] - Id of handler. // [pSyncMgrHandlerInfo] - Points to SyncMgrHandlerInfo to be filled in. // // Returns: Appropriate Error code // // Modifies: // // History: 28-Jul-98 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::SetHandlerInfo(HANDLERINFO *pHandlerId,LPSYNCMGRHANDLERINFO pSyncMgrHandlerInfo) { HRESULT hr = E_UNEXPECTED; LPHANDLERINFO pHandlerInfo = NULL; CLock clockqueue(this); if (!pSyncMgrHandlerInfo) { Assert(pSyncMgrHandlerInfo); return E_INVALIDARG; } clockqueue.Enter(); Assert(m_QueueType == QUEUETYPE_CHOICE); if (S_OK == LookupHandlerFromId(pHandlerId,&pHandlerInfo)) { if (HANDLERSTATE_INADDHANDLERITEMS != pHandlerInfo->HandlerState) { Assert(HANDLERSTATE_INADDHANDLERITEMS == pHandlerInfo->HandlerState); hr = E_UNEXPECTED; } else { // Quick Check of Size here. other paramters should already // be validated by hndlrmsg if (pSyncMgrHandlerInfo->cbSize != sizeof(SYNCMGRHANDLERINFO) ) { Assert(pSyncMgrHandlerInfo->cbSize == sizeof(SYNCMGRHANDLERINFO)); hr = E_INVALIDARG; } else { pHandlerInfo->SyncMgrHandlerInfo = *pSyncMgrHandlerInfo; hr = S_OK; } } } clockqueue.Leave(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::IsAllHandlerInstancesCancelCompleted, private // // Synopsis: Asks queue if all interintances of a Handler CLSID // are completed, Called in proxy terminate to see // if after requesting user input there are still items to // kill. // // Note: Only checks instances for this queue. // // Arguments: // // Returns: S_OK; if all handler instances are done. // S_FALSE - if still items going that should be killed. // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::IsAllHandlerInstancesCancelCompleted(REFCLSID clsidHandler) { HRESULT hr = S_OK; LPHANDLERINFO pCurHandlerInfo; CLock clockqueue(this); // just loop through handlers matching clsid and if any are <= SynchronizeCompleted // and the cancelled flag set then an instance of the Handler is still // stuck in a Cancel. Assert(m_QueueType == QUEUETYPE_PROGRESS); clockqueue.Enter(); pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo) { if ( (clsidHandler == pCurHandlerInfo->clsidHandler) && (BAD_HANDLERSTATE(pCurHandlerInfo) ) && (pCurHandlerInfo->fCancelled) ) { hr = S_FALSE; break; } pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } clockqueue.Leave(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::AddItemToHandler, public // // Synopsis: Adds item to the specified handler. // Called in context of the handlers thread. // // Arguments: [wHandlerId] - Id of handler. // [pOfflineItem] - Points to Items information to add. // // Returns: Appropriate Error code // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::AddItemToHandler(HANDLERINFO *pHandlerId,SYNCMGRITEM *pOfflineItem) { HRESULT hr = E_UNEXPECTED; // review for Lookup failures LPHANDLERINFO pHandlerInfo = NULL; LPITEMLIST pNewItem = NULL; LPHANDLERINFO pHandlerMatched; LPITEMLIST pItemListMatch; CLock clockqueue(this); clockqueue.Enter(); Assert(m_QueueType == QUEUETYPE_CHOICE); if (S_OK == LookupHandlerFromId(pHandlerId,&pHandlerInfo)) { if (HANDLERSTATE_INADDHANDLERITEMS != pHandlerInfo->HandlerState) { Assert(HANDLERSTATE_INADDHANDLERITEMS == pHandlerInfo->HandlerState); hr = E_UNEXPECTED; } else { // make sure the handler has a jobID and ConnectionObj // associated with it. Assert(pHandlerInfo->pJobInfo); Assert(pHandlerInfo->pJobInfo->pConnectionObj); if (pHandlerInfo->pJobInfo && pHandlerInfo->pJobInfo->pConnectionObj) { DWORD dwSyncFlags = pHandlerInfo->pJobInfo->dwSyncFlags; // Allocate the item. pNewItem = AllocNewHandlerItem(pHandlerInfo,pOfflineItem); if (NULL == pNewItem) { hr = E_OUTOFMEMORY; } else { DWORD dwCheckState; DWORD dwDefaultCheck; // what default for the item should be. DWORD ConnectionIndex; DWORD dwSyncEvent = dwSyncFlags & SYNCMGRFLAG_EVENTMASK; // if SyncType is SYNCMGRFLAG_CONNECT, SYNCMGRFLAG_PENDINGDISCONNECT // or Idle, set the defaults based on registration flags // If change this logic need to also change logic in dll hndlrq. dwDefaultCheck = pOfflineItem->dwItemState; if ( ( (dwSyncEvent == SYNCMGRFLAG_IDLE) && !(pHandlerInfo->dwRegistrationFlags & SYNCMGRREGISTERFLAG_IDLE) ) || ( (dwSyncEvent == SYNCMGRFLAG_CONNECT) && !(pHandlerInfo->dwRegistrationFlags & SYNCMGRREGISTERFLAG_CONNECT) ) || ( (dwSyncEvent == SYNCMGRFLAG_PENDINGDISCONNECT) && !(pHandlerInfo->dwRegistrationFlags & SYNCMGRREGISTERFLAG_PENDINGDISCONNECT) ) ) { dwDefaultCheck = SYNCMGRITEMSTATE_UNCHECKED; } // get appropriate stored setting based on the sync flags // invoke we just use whatever the handler tells us it should be. if (SYNCMGRFLAG_INVOKE != dwSyncEvent) { for (ConnectionIndex = 0; ConnectionIndex < pHandlerInfo->pJobInfo->cbNumConnectionObjs; ++ConnectionIndex) { TCHAR *pszConnectionName = pHandlerInfo->pJobInfo->pConnectionObj[ConnectionIndex]->pwszConnectionName; switch(dwSyncEvent) { case SYNCMGRFLAG_MANUAL: // only support one connection for manual Assert(pHandlerInfo->pJobInfo->cbNumConnectionObjs == 1); if (RegGetSyncItemSettings(SYNCTYPE_MANUAL, pHandlerInfo->clsidHandler, pOfflineItem->ItemID, pszConnectionName,&dwCheckState, dwDefaultCheck, NULL)) { pNewItem->offlineItem.dwItemState = dwCheckState; } break; case SYNCMGRFLAG_CONNECT: case SYNCMGRFLAG_PENDINGDISCONNECT: if (RegGetSyncItemSettings(SYNCTYPE_AUTOSYNC, pHandlerInfo->clsidHandler, pOfflineItem->ItemID, pszConnectionName,&dwCheckState, dwDefaultCheck, NULL)) { // for logon/logoff a checkstate of set wins and // as soon as it is set break out of the loop if ( (0 == ConnectionIndex) || (SYNCMGRITEMSTATE_CHECKED == dwCheckState) ) { pNewItem->offlineItem.dwItemState = dwCheckState; } if (SYNCMGRITEMSTATE_CHECKED == pNewItem->offlineItem.dwItemState) { break; } } break; case SYNCMGRFLAG_IDLE: if (RegGetSyncItemSettings(SYNCTYPE_IDLE, pHandlerInfo->clsidHandler, pOfflineItem->ItemID, pszConnectionName,&dwCheckState, dwDefaultCheck, NULL)) { // for Idle a checkstate of set wins and // as soon as it is set break out of the loop if ( (0 == ConnectionIndex) || (SYNCMGRITEMSTATE_CHECKED == dwCheckState)) { pNewItem->offlineItem.dwItemState = dwCheckState; } if (SYNCMGRITEMSTATE_CHECKED ==pNewItem->offlineItem.dwItemState) { break; } } break; case SYNCMGRFLAG_SCHEDULED: // if caused by an invoke, use whatever handler tells us. // only support one connection for schedule Assert(pHandlerInfo->pJobInfo->cbNumConnectionObjs == 1); if (pHandlerInfo->pJobInfo) { if (RegGetSyncItemSettings(SYNCTYPE_SCHEDULED, pHandlerInfo->clsidHandler, pOfflineItem->ItemID, pszConnectionName, &dwCheckState, SYNCMGRITEMSTATE_UNCHECKED, // if don't find item, don't check pHandlerInfo->pJobInfo->szScheduleName)) { pNewItem->offlineItem.dwItemState = dwCheckState; } else { // If don't find then default is to be unchecked. pNewItem->offlineItem.dwItemState = SYNCMGRITEMSTATE_UNCHECKED; } } break; case SYNCMGRFLAG_INVOKE: // if caused by an invoke, use whatever handler tells us. break; default: AssertSz(0, "UnknownSyncFlag Event"); break; }; } } // Search and mark duplicate entries. if (IsItemAlreadyInList(pHandlerInfo->clsidHandler, (pOfflineItem->ItemID),pHandlerInfo->pHandlerId, &pHandlerMatched,&pItemListMatch) ) { Assert(QUEUETYPE_CHOICE == m_QueueType || QUEUETYPE_PROGRESS == m_QueueType); pNewItem->fDuplicateItem = TRUE; // duplicate handling // if a manual sync then first writer to the queue wins, if (QUEUETYPE_CHOICE == m_QueueType) { pNewItem->offlineItem.dwItemState = SYNCMGRITEMSTATE_UNCHECKED; } } hr = S_OK; } } } } clockqueue.Leave(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::Progress, public // // Synopsis: Updates items progress information // Called in the context of the Handlers thread // // Arguments: [wHandlerId] - Id of handler. // [ItemID] - OfflineItemID of the specified item. // [lpSyncProgressItem] - Pointer to SyncProgressItem. // // Returns: Appropriate Error code // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::Progress(HANDLERINFO *pHandlerId,REFSYNCMGRITEMID ItemID, LPSYNCMGRPROGRESSITEM lpSyncProgressItem) { HRESULT hr = E_UNEXPECTED; LPHANDLERINFO pHandlerInfo = NULL; LPITEMLIST pCurItem = NULL; BOOL fFoundMatch = FALSE; PROGRESSUPDATEDATA progressData; HWND hwndCallback; CLock clockqueue(this); progressData.pHandlerID = pHandlerId; progressData.ItemID = ItemID; Assert(QUEUETYPE_PROGRESS == m_QueueType); clockqueue.Enter(); if (S_OK == LookupHandlerFromId(pHandlerId,&pHandlerInfo)) { pCurItem = pHandlerInfo->pFirstItem; while (pCurItem) { if (ItemID == pCurItem->offlineItem.ItemID) { fFoundMatch = TRUE; break; } pCurItem = pCurItem->pnextItem; } if (pHandlerInfo->fCancelled) hr = S_SYNCMGR_CANCELALL; else if (!fFoundMatch || pCurItem->fItemCancelled) { Assert(fFoundMatch); hr = S_SYNCMGR_CANCELITEM; } else hr = S_OK; } if (fFoundMatch) // store everyting in local vars. { // if found match but shouldn't include in progress bar // fix it up. if ( (pCurItem->fHiddenItem) || (FALSE == pCurItem->fIncludeInProgressBar)) { // if found a match it should be included in the progress bar // Assert(TRUE == pCurItem->fIncludeInProgressBar); // Review if test app hits this. Assert(FALSE == pCurItem->fHiddenItem); // shouldn't get progress on hidden items. fFoundMatch = FALSE; if (S_OK == hr) { hr = S_SYNCMGR_CANCELITEM; // return cancel item just as if item wasn't cancelled. } } else { progressData.clsidHandler = pHandlerInfo->clsidHandler; progressData.wItemId = pCurItem->wItemId; hwndCallback = pHandlerInfo->hWndCallback; } } clockqueue.Leave(); if (fFoundMatch) { // send off data to the callback window. // it is responsible for updating the items progress values. if (hwndCallback) { // validate the ProgressItem structure before passing it on. if (IsValidSyncProgressItem(lpSyncProgressItem)) { SendMessage(hwndCallback,WM_PROGRESS_UPDATE, (WPARAM) &progressData,(LPARAM) lpSyncProgressItem); } else { if (S_OK == hr) // CANCEL RESULTS OVERRIDE ARG PROBLEMS { hr = E_INVALIDARG; } } } } return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::LogError, public // // Synopsis: Logs and error for the specified item // Called in the context of the Handlers thread // // Arguments: [wHandlerId] - Id of handler. // [dwErrorLevel] - ErrorLevel of the Error // [lpcErrorText] - Text of the Error. // [lpSyncLogError] - Pointer to SyncLogError structure // // Returns: Appropriate Error code // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::LogError(HANDLERINFO *pHandlerId,DWORD dwErrorLevel, const WCHAR *lpcErrorText, LPSYNCMGRLOGERRORINFO lpSyncLogError) { HRESULT hr = E_UNEXPECTED; LPHANDLERINFO pHandlerInfo = NULL; LPITEMLIST pCurItem = NULL; BOOL fFoundMatch = FALSE; MSGLogErrors msgLogErrors; HWND hWndCallback = NULL; CLock clockqueue(this); msgLogErrors.dwErrorLevel = dwErrorLevel; msgLogErrors.lpcErrorText = lpcErrorText; msgLogErrors.ErrorID = GUID_NULL; msgLogErrors.fHasErrorJumps = FALSE; msgLogErrors.mask = 0; Assert(QUEUETYPE_PROGRESS == m_QueueType); clockqueue.Enter(); if (S_OK == LookupHandlerFromId(pHandlerId,&pHandlerInfo)) { hWndCallback = pHandlerInfo->hWndCallback; // validate the paramaters. if (NULL == hWndCallback) { hr = E_UNEXPECTED; } else if (!IsValidSyncLogErrorInfo(dwErrorLevel,lpcErrorText,lpSyncLogError)) { hr = E_INVALIDARG; } else { if (lpSyncLogError && (lpSyncLogError->mask & SYNCMGRLOGERROR_ERRORID)) { pHandlerInfo->fHasErrorJumps = TRUE; msgLogErrors.mask |= SYNCMGRLOGERROR_ERRORID; msgLogErrors.ErrorID = lpSyncLogError->ErrorID; } if (lpSyncLogError && (lpSyncLogError->mask & SYNCMGRLOGERROR_ERRORFLAGS)) { if (SYNCMGRERRORFLAG_ENABLEJUMPTEXT & lpSyncLogError->dwSyncMgrErrorFlags) { pHandlerInfo->fHasErrorJumps = TRUE; msgLogErrors.fHasErrorJumps = TRUE; } } if (lpSyncLogError && (lpSyncLogError->mask & SYNCMGRLOGERROR_ITEMID)) { msgLogErrors.mask |= SYNCMGRLOGERROR_ITEMID; msgLogErrors.ItemID = lpSyncLogError->ItemID; } hr = S_OK; } } clockqueue.Leave(); if (S_OK == hr) { Assert(sizeof(WPARAM) >= sizeof(HANDLERINFO *)); SendMessage(hWndCallback, WM_PROGRESS_LOGERROR, (WPARAM) pHandlerId, (LPARAM) &msgLogErrors); } return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::DeleteLogError, public // // Synopsis: Deletes an Error from the Results pane that was previously logged. // // Arguments: // // Returns: Appropriate Error code // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::DeleteLogError(HANDLERINFO *pHandlerId,REFSYNCMGRERRORID ErrorID,DWORD dwReserved) { MSGDeleteLogErrors msgDeleteLogError; HWND hWndCallback = NULL; HANDLERINFO *pHandlerInfo; CLock clockqueue(this); clockqueue.Enter(); msgDeleteLogError.pHandlerId = pHandlerId; msgDeleteLogError.ErrorID = ErrorID; if (S_OK == LookupHandlerFromId(pHandlerId,&pHandlerInfo)) { hWndCallback = pHandlerInfo->hWndCallback; } // review, if handler doesn't have any more error jumps after the deletelogError we can now // release it (pHandlerInfo->fHasErrorJumps) clockqueue.Leave(); if (hWndCallback) { SendMessage(hWndCallback, WM_PROGRESS_DELETELOGERROR, (WPARAM) 0, (LPARAM) &msgDeleteLogError); } return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::CallCompletionRoutine, public // // Synopsis: Called by callback on handler thread // to indicate a call with a completion callback // has completed. // // Returns: Appropriate Error code // // Modifies: // // History: 02-Jun-98 rogerg Created. // //---------------------------------------------------------------------------- void CHndlrQueue::CallCompletionRoutine(HANDLERINFO *pHandlerId,DWORD dwThreadMsg,HRESULT hCallResult, ULONG cbNumItems,SYNCMGRITEMID *pItemIDs) { HWND hWndDlg = NULL; HRESULT hrHandlerCall = S_OK; BOOL fCallbackAlreadyCalled = FALSE; HANDLERINFO *pHandlerInfo; CLock clockqueue(this); if (!pHandlerId) { Assert(pHandlerId); return; } // Note: cbNumItems and pItemIDs is only valid for ShowErrors // make sure the handlid is valid and then if there // is a pdlg call its completion routine via the postmessage // method. clockqueue.Enter(); hWndDlg = m_hwndDlg; Assert(hWndDlg); if ( pHandlerId && (S_OK == LookupHandlerFromId(pHandlerId,&pHandlerInfo)) ) { // if flag isn't set for the message // then it was already handled i.e. handler called // us even though it returned an error. if (dwThreadMsg & pHandlerInfo->dwOutCallMessages) { pHandlerInfo->dwOutCallMessages &= ~dwThreadMsg; } else { AssertSz(0,"Callback called twice"); // test apps currently do this. fCallbackAlreadyCalled = TRUE; } // if already handled don't call these again. if (!fCallbackAlreadyCalled) { // fix up internal states before informing caller // the call is complete. switch(dwThreadMsg) { case ThreadMsg_ShowProperties: // don't need to do anything on show properties. break; case ThreadMsg_PrepareForSync: hrHandlerCall = PrepareForSyncCompleted(pHandlerInfo,hCallResult); break; case ThreadMsg_Synchronize: hrHandlerCall = SynchronizeCompleted(pHandlerInfo,hCallResult); break; case ThreadMsg_ShowError: hrHandlerCall = ShowErrorCompleted(pHandlerInfo,hCallResult,cbNumItems,pItemIDs); break; default: AssertSz(0,"Unknown Queue Completion Callback"); break; } } // possible completion routine comes in before handler has actually // returned from the original call. Wait until proxy is no longer in an // out call. // If switch to COM for messaging need to find a better way of doing this. if (pHandlerInfo->pThreadProxy && pHandlerInfo->pThreadProxy->IsProxyInOutCall() && hWndDlg) { // tell proxy to post the message whenver it gets done. // CODE REVIEW: // NOTENOTE: // Remove this code.. /* if ( 0 /* S_OK == pHandlerInfo->pThreadProxy->SetProxyCompletion(hWndDlg,WM_BASEDLG_COMPLETIONROUTINE,dwThreadMsg,hCallResult)*) { hWndDlg = NULL; } */ } } else { // on handler lookup assert but still post the message to the hwnd // so it won't get stuck waiting for the completion routine // this is only valid for setproperties in the // case the user clicked on a non-existant item but // this shouldn't really happen either AssertSz(dwThreadMsg == ThreadMsg_ShowProperties,"LookupHandler failed in CompletionRoutine"); } LPCALLCOMPLETIONMSGLPARAM lpCallCompletelParam = (LPCALLCOMPLETIONMSGLPARAM) ALLOC(sizeof(CALLCOMPLETIONMSGLPARAM)); if (lpCallCompletelParam) { lpCallCompletelParam->hCallResult = hCallResult; lpCallCompletelParam->clsidHandler = pHandlerId->clsidHandler; // itemID is GUID_NULL unless its a ShowProperties completed. if ((ThreadMsg_ShowProperties == dwThreadMsg) && (1 == cbNumItems)) { lpCallCompletelParam->itemID = *pItemIDs; } else { lpCallCompletelParam->itemID = GUID_NULL; } } clockqueue.Leave(); if (hWndDlg && !fCallbackAlreadyCalled) // if already out of out call or proxy failed post the messge ourselves. { // if alloc of completion lparam fails send message anyways so callback count // remains accurate. PostMessage(hWndDlg,WM_BASEDLG_COMPLETIONROUTINE,dwThreadMsg,(LPARAM) lpCallCompletelParam); } else { // if don't post message up to us to free the lpCallCopmlete. if (lpCallCompletelParam) { FREE(lpCallCompletelParam); } } } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::IsItemAlreadyInList, private // // Synopsis: Given a clsid and ItemID determines if a matchin // item is already in the list // Called in the context of the Handlers thread // // Arguments: [clsidHandler] - clsid of the handler // [ItemID] - ItemID of the item // [wHandlerId] - HandlerID of the item. // [ppHandlerMatched] - on out the handler that matched // [ppItemIdMatch] - on out Item that matched. // // Returns: Appropriate Error code // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- BOOL CHndlrQueue::IsItemAlreadyInList(CLSID clsidHandler,REFSYNCMGRITEMID ItemID, HANDLERINFO *pHandlerId, LPHANDLERINFO *ppHandlerMatched, LPITEMLIST *ppItemListMatch) { BOOL fFoundMatch = FALSE; LPHANDLERINFO pCurHandlerInfo = NULL; LPITEMLIST pCurItem = NULL; ASSERT_LOCKHELD(this); // caller of this function should have already locked the queue. pCurHandlerInfo = m_pFirstHandler; while (pCurHandlerInfo && !fFoundMatch) { if (pHandlerId == pCurHandlerInfo->pHandlerId) // when find hander know didn't find any before. break; if (clsidHandler == pCurHandlerInfo->clsidHandler) // see if CLSID matches { // see if handler info has a matching item pCurItem = pCurHandlerInfo->pFirstItem; while (pCurItem) { if (ItemID == pCurItem->offlineItem.ItemID) { *ppHandlerMatched = pCurHandlerInfo; *ppItemListMatch = pCurItem; fFoundMatch = TRUE; break; } pCurItem = pCurItem->pnextItem; } } pCurHandlerInfo = pCurHandlerInfo->pNextHandler; } return fFoundMatch; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::GetSelectedItemsInHandler, private // // Synopsis: Gets the number of selected items for this handler // // for our implementation if a cbCount is passed and it doesn't match // the number of actually selected then assert since we call this routine // internally. // // // Arguments: [pHandlerInfo] - Pointer to the HandlerInfo to look at. // [cbcount] - [in] cbCount == number of pItems allocated, // [out] cbCpimt == number of items actually written. // if the buffer is too small items written will be zero. // [pItems] - Pointer to array of SYNCMGRITEMs to be filled in. // // Returns: Returns the number of selectd items. // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- DWORD CHndlrQueue::GetSelectedItemsInHandler(LPHANDLERINFO pHandlerInfo,ULONG *cbCount, SYNCMGRITEMID* pItems) { LPITEMLIST pCurItem; DWORD dwSelectCount = 0; DWORD dwArraySizeIndex; DWORD dwArraySize; ASSERT_LOCKHELD(this); // caller of this function should have already locked the queue. if (cbCount) { dwArraySizeIndex = *cbCount; dwArraySize = *cbCount; *cbCount = 0; // initialize to zero. } else { dwArraySizeIndex = 0; dwArraySize = 0; } if ( dwArraySize && NULL == pItems) { Assert(0 == dwArraySize || pItems); return 0; } if (NULL == pHandlerInfo) { Assert(pHandlerInfo); return 0; } pCurItem = pHandlerInfo->pFirstItem; while (pCurItem) { // dwItemState if (SYNCMGRITEMSTATE_CHECKED == pCurItem->offlineItem.dwItemState) { ++dwSelectCount; if (dwArraySizeIndex) { *pItems = pCurItem->offlineItem.ItemID; *cbCount += 1; ++pItems; --dwArraySizeIndex; if (!pCurItem->fHiddenItem) // if not a hidden item { Assert(TRUE == pCurItem->fIncludeInProgressBar); Assert(HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE == pCurItem->iProgMaxValue); // reset iProgValue back to zero since may not be zero if retry came in while still synchronizing. SetItemProgressValues(pCurItem,0,HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE); } else { // if item is hidden,a assert it doesn't have UI Assert(FALSE == pCurItem->fIncludeInProgressBar); Assert(HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE == pCurItem->iProgValue); Assert(HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE == pCurItem->iProgMaxValue); } pCurItem->fSynchronizingItem = TRUE; // item is now synchronizing // once added to the array uncheck the item so on a retry we can just // always reset items to checked pCurItem->offlineItem.dwItemState = SYNCMGRITEMSTATE_UNCHECKED; } } else { Assert(FALSE == pCurItem->fSynchronizingItem); // Assert(FALSE == pCurItem->fIncludeInProgressBar); Can be included in progress bar if retry comes in before RemoveFinished is called. Assert(HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE == pCurItem->iProgValue); Assert(HNDRLQUEUE_DEFAULT_PROGRESS_MAXVALUE == pCurItem->iProgMaxValue); } pCurItem = pCurItem->pnextItem; } // internal call should always request a proper array size. Assert(dwSelectCount == dwArraySize || 0 == dwArraySize); return dwSelectCount; } // job info methods STDMETHODIMP CHndlrQueue::CreateJobInfo(JOBINFO **ppJobInfo,DWORD cbNumConnectionNames) { HRESULT hr = S_FALSE; JOBINFO *pNewJobInfo = NULL; ASSERT_LOCKHELD(this); // create a new job and add it to the the JobInfo list. // allocate space for JobInfo + number of connection objects that // will be associated with this job. Assert(cbNumConnectionNames); if (cbNumConnectionNames < 1) return S_FALSE; pNewJobInfo = (JOBINFO *) ALLOC(sizeof(JOBINFO) + sizeof(CONNECTIONOBJ)*(cbNumConnectionNames - 1)); if (pNewJobInfo) { memset(pNewJobInfo, 0, sizeof(JOBINFO)); pNewJobInfo->cRefs = 1; pNewJobInfo->pNextJobInfo = m_pFirstJobInfo; m_pFirstJobInfo = pNewJobInfo; *ppJobInfo = pNewJobInfo; hr = S_OK; } else { hr = E_OUTOFMEMORY; } return hr; } DWORD CHndlrQueue::ReleaseJobInfoExt(JOBINFO *pJobInfo) { DWORD dwRet; CLock clockqueue(this); clockqueue.Enter(); dwRet = ReleaseJobInfo(pJobInfo); clockqueue.Leave(); return dwRet; } DWORD CHndlrQueue::ReleaseJobInfo(JOBINFO *pJobInfo) { DWORD cRefs; ASSERT_LOCKHELD(this); --(pJobInfo->cRefs); cRefs = pJobInfo->cRefs; Assert( ((LONG) cRefs) >= 0); if (0 == cRefs) { JOBINFO *pCurJobInfo = NULL; DWORD dwConnObjIndex; // loop through release all connection objs on this job for (dwConnObjIndex = 0 ; dwConnObjIndex < pJobInfo->cbNumConnectionObjs; dwConnObjIndex++) { Assert(pJobInfo->pConnectionObj[dwConnObjIndex]); if (pJobInfo->pConnectionObj[dwConnObjIndex]) { ConnectObj_ReleaseConnectionObj(pJobInfo->pConnectionObj[dwConnObjIndex]); pJobInfo->pConnectionObj[dwConnObjIndex] = NULL; } } // remove this JobInfo from the list. if (pJobInfo == m_pFirstJobInfo) { m_pFirstJobInfo = pJobInfo->pNextJobInfo; } else { pCurJobInfo = m_pFirstJobInfo; while (pCurJobInfo->pNextJobInfo) { if (pJobInfo == pCurJobInfo->pNextJobInfo) { pCurJobInfo->pNextJobInfo = pJobInfo->pNextJobInfo; break; } pCurJobInfo = pCurJobInfo->pNextJobInfo; } } FREE(pJobInfo); } return cRefs; } DWORD CHndlrQueue::AddRefJobInfo(JOBINFO *pJobInfo) { DWORD cRefs; ASSERT_LOCKHELD(this); ++(pJobInfo->cRefs); cRefs = pJobInfo->cRefs; return cRefs; } // determines ifthe specified JobInfo's connection can be openned. // review should really call into connection Object help api. STDMETHODIMP CHndlrQueue::OpenConnection(JOBINFO *pJobInfo) { CONNECTIONOBJ *pConnectionObj; HRESULT hr; Assert(pJobInfo); if (NULL == pJobInfo) // if no job info go ahead and say the connection is open. return S_OK; // turn off workOffline during the sync CloseConnection will turn // it back on if the user had it off. ConnectObj_SetWorkOffline(FALSE); // if this is anything but a schedule go ahead and say S_OK; if (!(SYNCMGRFLAG_SCHEDULED == (pJobInfo->dwSyncFlags & SYNCMGRFLAG_EVENTMASK)) ) { return S_OK; } // for schedule sink we only support one connection Object. if (1 != pJobInfo->cbNumConnectionObjs) { Assert(1 == pJobInfo->cbNumConnectionObjs); return E_UNEXPECTED; } pConnectionObj = pJobInfo->pConnectionObj[0]; if (NULL == pConnectionObj) return S_OK; // if we aren't suppose to make a connection of there is already // a hRasConn as part of the connection object then just // return S_OK; // if connection is already open and we are on a job that // has already tried to open it then return S_OK; if (pJobInfo->pConnectionObj[0]->fConnectionOpen && pJobInfo->fTriedConnection) return S_OK; // if we haven't already tried to make the connection // on this job then call OpenConnection to make sure the // connection is still really open. if (!pJobInfo->fTriedConnection) { pJobInfo->fTriedConnection = TRUE; hr = ConnectObj_OpenConnection(pConnectionObj, pJobInfo->fCanMakeConnection, m_pDlg); } else { hr = S_FALSE; } // if get down to the bottom and still no hRasConn then return S_FALSE return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::ScrambleIdleHandlers, private // // Synopsis: Called on an Idle Choice queue just before transfer // so the lastHandler is placed at the back of the list. // // Arguments: // // Returns: // // Modifies: // // History: 17-Nov-97 rogerg Created. // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::ScrambleIdleHandlers(REFCLSID clsidLastHandler) { LPHANDLERINFO pMatchHandler; LPHANDLERINFO pLastHandler; CLock clockqueue(this); Assert(m_QueueType == QUEUETYPE_CHOICE); clockqueue.Enter(); // find the first occurance of specified handler and then place that handler // at the end of the list and everything after at the beginning of the list // no an error to not find the Handler since may have been deleted or // no longer has items. pMatchHandler = m_pFirstHandler; while (pMatchHandler) { if (pMatchHandler->clsidHandler == clsidLastHandler) { // if there are no items after the match then just break; if (NULL == pMatchHandler->pNextHandler) { break; } // loop until find the last handler. pLastHandler = pMatchHandler->pNextHandler; while (pLastHandler->pNextHandler) { pLastHandler = pLastHandler->pNextHandler; } // now set the handler after the matchHandler to be the // head and set the next pointer of the LastHandler in // the list to point to the MatchHandler. pLastHandler->pNextHandler = m_pFirstHandler; m_pFirstHandler = pMatchHandler->pNextHandler; pMatchHandler->pNextHandler = NULL; break; } pMatchHandler = pMatchHandler->pNextHandler; } clockqueue.Leave(); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::BeginSyncSession // // Synopsis: Called to signal the beginning of the core synchronization session // to setup up dial support. // // History: 28-Jul-98 SitaramR Created // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::BeginSyncSession() { HRESULT hr = ::BeginSyncSession(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::EndSyncSession // // Synopsis: Called to signal the end of the core synchronization session // to teardown dial support. // // History: 28-Jul-98 SitaramR Created // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::EndSyncSession() { HRESULT hr = ::EndSyncSession(); return hr; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::SortHandlersByConnection // // Synopsis: Moves hanlders that won't establish connection to the end, // ie after handlers that can establish connectoin. // // History: 28-Jul-98 SitaramR Created // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::SortHandlersByConnection() { CLock clockqueue(this); clockqueue.Enter(); Assert(m_QueueType == QUEUETYPE_CHOICE); LPHANDLERINFO pFirstCannotDialHandler = NULL; LPHANDLERINFO pLastCannotDialHandler = NULL; LPHANDLERINFO pPrevHandler = NULL; LPHANDLERINFO pCurHandler = m_pFirstHandler; while ( pCurHandler ) { if ( pCurHandler->SyncMgrHandlerInfo.SyncMgrHandlerFlags & SYNCMGRHANDLER_MAYESTABLISHCONNECTION ) { // // Move to next handler // pPrevHandler = pCurHandler; pCurHandler = pCurHandler->pNextHandler; } else { // // Move handler to cannot dial list // if ( pPrevHandler == NULL ) { // // This is the first handler in list // m_pFirstHandler = pCurHandler->pNextHandler; pCurHandler->pNextHandler = NULL; if ( pLastCannotDialHandler == NULL ) { Assert( pFirstCannotDialHandler == NULL ); pFirstCannotDialHandler = pLastCannotDialHandler = pCurHandler; } else { pLastCannotDialHandler->pNextHandler = pCurHandler; pLastCannotDialHandler = pCurHandler; } pCurHandler = m_pFirstHandler; } else { pPrevHandler->pNextHandler = pCurHandler->pNextHandler; pCurHandler->pNextHandler = NULL; if ( pLastCannotDialHandler == NULL ) { Assert( pFirstCannotDialHandler == NULL ); pFirstCannotDialHandler = pLastCannotDialHandler = pCurHandler; } else { pLastCannotDialHandler->pNextHandler = pCurHandler; pLastCannotDialHandler = pCurHandler; } pCurHandler = pPrevHandler->pNextHandler; } } } // // Attach cannot dial list at end of m_pFirstHandler list // if ( pPrevHandler ) { Assert( pPrevHandler->pNextHandler == NULL ); pPrevHandler->pNextHandler = pFirstCannotDialHandler; } else { // // Case where the original list became empty // Assert( m_pFirstHandler == NULL ); m_pFirstHandler = pFirstCannotDialHandler; } clockqueue.Leave(); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CHndlrQueue::EstablishConnection // // Synopsis: Called by handler to establish a connection. // // Arguments: [pHandlerID] -- Ptr to handler // [lpwszConnection] -- Connection to establish // [dwReserved] -- Must be zero for now // // History: 28-Jul-98 SitaramR Created // //---------------------------------------------------------------------------- STDMETHODIMP CHndlrQueue::EstablishConnection( LPHANDLERINFO pHandlerID, WCHAR const * lpwszConnection, DWORD dwReserved ) { HRESULT hr = S_OK; CLock clockqueue(this); clockqueue.Enter(); CONNECTIONOBJ *pConnObj = NULL; BOOL fAutoDial = FALSE; LPHANDLERINFO pHandlerInfo = NULL; hr = LookupHandlerFromId( pHandlerID, &pHandlerInfo ); if ( S_OK == hr ) { JOBINFO *pJobInfo = pHandlerInfo->pJobInfo; DWORD dwSyncFlags = pJobInfo->dwSyncFlags & SYNCMGRFLAG_EVENTMASK; if ( ( dwSyncFlags == SYNCMGRFLAG_MANUAL || dwSyncFlags == SYNCMGRFLAG_INVOKE ) && pHandlerInfo->SyncMgrHandlerInfo.SyncMgrHandlerFlags & SYNCMGRHANDLER_MAYESTABLISHCONNECTION ) { if ( lpwszConnection == NULL ) { // // Null connection means use the default autodial connection // fAutoDial = TRUE; } else { hr = ConnectObj_FindConnectionObj(lpwszConnection,TRUE,&pConnObj); } } else { // // Either the handler invoke type does not permit establishing connection, // or GetHandlerInfo flags did not specify the EstablishConnection flag. // hr = E_UNEXPECTED; } } clockqueue.Leave(); if (S_OK == hr) { if (fAutoDial) { hr = ConnectObj_AutoDial(INTERNET_AUTODIAL_FORCE_ONLINE,m_pDlg); } else { Assert( pConnObj ); if ( !pConnObj->fConnectionOpen ) { hr = ConnectObj_OpenConnection(pConnObj, TRUE, m_pDlg ); ConnectObj_ReleaseConnectionObj(pConnObj); } } } return hr; }