Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5287 lines
171 KiB

//+-------------------------------------------------------------------------
//
// 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;
}