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.
 
 
 
 
 
 

945 lines
22 KiB

#include "private.h"
#include "offsync.h"
#include "throttle.h"
#include "helper.h"
#include "subsmgrp.h"
#include <mluisupp.h>
#define TF_THISMODULE TF_DELAGENT
COfflineSync::COfflineSync()
{
// Maintain global count of objects
DllAddRef();
ASSERT(NULL == m_pThrottler);
ASSERT(FALSE == m_fCookiesSpecified);
// Initialize object
m_cRef = 1;
DBG("Creating COfflineSync object");
}
COfflineSync::~COfflineSync()
{
DllRelease();
Cleanup();
DBG("Destroying COfflineSync object");
if (m_pSyncCallback)
{
m_pSyncCallback->Release();
}
SAFERELEASE(m_pSubsMgr2);
}
void COfflineSync::Cleanup()
{
if (NULL != m_pThrottler)
{
m_pThrottler->Unadvise(this);
m_pThrottler->Release();
m_pThrottler = NULL;
}
m_hWndParent = NULL;
if (NULL != m_pSyncCallback)
{
m_pSyncCallback->SynchronizeCompleted(m_hrResult);
}
m_nItemsToRun = 0;
SAFEDELETE(m_pItems);
}
//
// IUnknown members
//
STDMETHODIMP_(ULONG) COfflineSync::AddRef(void)
{
return ++m_cRef;
}
STDMETHODIMP_(ULONG) COfflineSync::Release(void)
{
if( 0L != --m_cRef )
return m_cRef;
delete this;
return 0L;
}
STDMETHODIMP COfflineSync::QueryInterface(REFIID riid, void ** ppv)
{
*ppv=NULL;
// Validate requested interface
if ((IID_IUnknown == riid) ||
(IID_ISyncMgrSynchronize == riid))
{
*ppv = (ISyncMgrSynchronize *)this;
}
else
{
return E_NOINTERFACE;
}
// Addref through the interface
((LPUNKNOWN)*ppv)->AddRef();
return S_OK;
}
//
// IOfflineSynchronize members
//
HRESULT COfflineSync::Initialize(DWORD dwReserved, DWORD dwSyncFlags,
DWORD cbCookie, const BYTE *lpCookie)
{
HRESULT hr = S_OK;
if (SYNCMGRFLAG_INVOKE == (dwSyncFlags & SYNCMGRFLAG_EVENTMASK) )
{
ASSERT((0 == cbCookie) || (0 == cbCookie % sizeof(SUBSCRIPTIONCOOKIE)));
if ((cbCookie != 0) &&
((0 != (cbCookie % sizeof(SUBSCRIPTIONCOOKIE))) || (NULL == lpCookie)))
{
return E_INVALIDARG;
}
if (cbCookie > 0)
{
hr = DupItems(cbCookie / sizeof(SUBSCRIPTIONCOOKIE), (SUBSCRIPTIONCOOKIE *)lpCookie);
if (SUCCEEDED(hr))
{
m_fCookiesSpecified = TRUE;
}
}
}
m_dwSyncFlags = dwSyncFlags;
return hr;
}
HRESULT COfflineSync::GetHandlerInfo(LPSYNCMGRHANDLERINFO *ppSyncMgrHandlerInfo)
{
HRESULT hr;
if (NULL == ppSyncMgrHandlerInfo)
{
return E_INVALIDARG;
}
*ppSyncMgrHandlerInfo = (SYNCMGRHANDLERINFO *)CoTaskMemAlloc(sizeof(SYNCMGRHANDLERINFO));
if (NULL != *ppSyncMgrHandlerInfo)
{
(*ppSyncMgrHandlerInfo)->cbSize = sizeof(SYNCMGRHANDLERINFO);
(*ppSyncMgrHandlerInfo)->hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_WEBCHECK));
(*ppSyncMgrHandlerInfo)->SyncMgrHandlerFlags = 0; //SYNCMGRHANDLER_HASPROPERTIES
MLLoadStringW(IDS_SYNCMGR_NAME,
(*ppSyncMgrHandlerInfo)->wszHandlerName,
ARRAYSIZE((*ppSyncMgrHandlerInfo)->wszHandlerName));
hr = S_OK;
}
else
{
hr = E_OUTOFMEMORY;
}
return hr;
}
HRESULT COfflineSync::EnumSyncMgrItems(ISyncMgrEnumItems **ppSyncMgrEnumItems)
{
HRESULT hr;
if (NULL == ppSyncMgrEnumItems)
{
return E_INVALIDARG;
}
hr = GetSubsMgr2();
if (SUCCEEDED(hr))
{
COfflineEnum *pEnum = new COfflineEnum();
if (NULL != pEnum)
{
ASSERT(((TRUE == m_fCookiesSpecified) && ((m_nItemsToRun > 0) && (NULL != m_pItems))) ||
((FALSE == m_fCookiesSpecified) && ((m_nItemsToRun == 0) && (NULL == m_pItems))))
hr = pEnum->Init(m_pSubsMgr2, m_nItemsToRun, m_pItems, ppSyncMgrEnumItems, m_dwSyncFlags);
pEnum->Release();
}
else
{
hr = E_OUTOFMEMORY;
}
}
return hr;
}
HRESULT COfflineSync::GetItemObject(REFSYNCMGRITEMID ItemID, REFIID riid, void **ppv)
{
return E_NOTIMPL;
}
HRESULT COfflineSync::ShowProperties(HWND hWndParent, REFSYNCMGRITEMID ItemID)
{
HRESULT hr = S_OK;
HRESULT hrTmp;
ISubscriptionItem *pSubsItem;
hrTmp = SubscriptionItemFromCookie(FALSE, &ItemID, &pSubsItem);
if (SUCCEEDED(hrTmp))
{
BSTR bstrURL;
hrTmp = ReadBSTR(pSubsItem, c_szPropURL, &bstrURL);
if (SUCCEEDED(hrTmp))
{
ISubscriptionMgr2 *pSubsMgr2;
hrTmp = CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER,
IID_ISubscriptionMgr2, (void **)&pSubsMgr2);
if (SUCCEEDED(hrTmp))
{
BOOL bIsSubscribed;
hrTmp = pSubsMgr2->ShowSubscriptionProperties(bstrURL, hWndParent);
if (FAILED(pSubsMgr2->IsSubscribed(bstrURL, &bIsSubscribed)) || !bIsSubscribed)
{
hr = S_SYNCMGR_ITEMDELETED;
}
pSubsMgr2->Release();
}
SysFreeString(bstrURL);
}
pSubsItem->Release();
}
if (NULL != m_pSyncCallback)
{
m_pSyncCallback->ShowPropertiesCompleted(hr);
}
return hr;
}
HRESULT COfflineSync::SetProgressCallback(ISyncMgrSynchronizeCallback *lpCallBack)
{
SAFERELEASE(m_pSyncCallback);
m_pSyncCallback = lpCallBack;
if (m_pSyncCallback)
{
m_pSyncCallback->AddRef();
}
return S_OK;
}
HRESULT COfflineSync::PrepareForSync(ULONG cbNumItems, SYNCMGRITEMID *pItemIDs,
HWND hWndParent, DWORD dwReserved)
{
HRESULT hr;
DBG("PrepareForSync");
if ((0 == cbNumItems) ||
(NULL == pItemIDs))
{
hr = E_INVALIDARG;
}
if (NULL == m_pSyncCallback)
{
return E_UNEXPECTED;
}
hr = DupItems(cbNumItems, pItemIDs);
m_pSyncCallback->PrepareForSyncCompleted(hr);
return hr;
}
HRESULT COfflineSync::Synchronize(HWND hWndParent)
{
HRESULT hr;
m_hrResult = E_FAIL;
m_hWndParent = hWndParent;
hr = CThrottler::GetThrottler(&m_pThrottler);
if (SUCCEEDED(hr))
{
ASSERT(NULL != m_pThrottler);
hr = m_pThrottler->Advise(this);
if (SUCCEEDED(hr))
{
hr = m_pThrottler->RunCookies(m_nItemsToRun, m_pItems, m_dwSyncFlags);
}
// ************************************************************************
// Don't access m_pThrottler after this without checking for NULL since
// we could have released it during the call to RunCookies!
// ************************************************************************
}
if (FAILED(hr))
{
Cleanup();
}
return hr;
}
HRESULT COfflineSync::SetItemStatus(REFSYNCMGRITEMID ItemID, DWORD dwSyncMgrStatus)
{
HRESULT hr = S_OK;
if (NULL != m_pThrottler)
{
switch (dwSyncMgrStatus)
{
case SYNCMGRSTATUS_SKIPPED:
hr = m_pThrottler->AbortItems(1, &ItemID);
break;
case SYNCMGRSTATUS_STOPPED:
hr = m_pThrottler->AbortItems(m_nItemsToRun, m_pItems);
break;
}
}
else
{
ULONG i;
// This means we are getting called before Synchronize is called
switch (dwSyncMgrStatus)
{
case SYNCMGRSTATUS_SKIPPED:
for (i = 0; i < m_nItemsToRun; i++)
{
if (ItemID == m_pItems[i])
{
m_pItems[i] = GUID_NULL;
}
}
break;
case SYNCMGRSTATUS_STOPPED:
m_nItemsToRun = 0;
break;
}
}
return hr;
}
HRESULT COfflineSync::ShowError(HWND hWndParent,REFSYNCMGRERRORID ErrorID)
{
return E_NOTIMPL;
}
HRESULT COfflineSync::UpdateBegin(
const SUBSCRIPTIONCOOKIE *pSubscriptionCookie)
{
UpdateProgress(pSubscriptionCookie, -1, -1, -1, S_OK, NULL);
return S_OK;
}
HRESULT COfflineSync::UpdateProgress(
const SUBSCRIPTIONCOOKIE *pSubscriptionCookie,
long lSizeDownloaded,
long lProgressCurrent,
long lProgressMax,
HRESULT hrStatus,
LPCWSTR wszStatus)
{
HRESULT hr;
ASSERT(NULL != m_pSyncCallback);
ASSERT(NULL != m_pThrottler);
if ((FindCookie(pSubscriptionCookie) != -1) &&
(NULL != m_pSyncCallback))
{
int iProgValue;
switch (hrStatus)
{
case WC_INTERNAL_S_PAUSED:
iProgValue = SYNCMGRSTATUS_PAUSED;
break;
case WC_INTERNAL_S_RESUMING:
iProgValue = SYNCMGRSTATUS_RESUMING;
break;
case WC_INTERNAL_S_PENDING:
iProgValue = SYNCMGRSTATUS_PENDING;
break;
default:
iProgValue = SYNCMGRSTATUS_UPDATING;
break;
}
CallSyncMgrProgress(pSubscriptionCookie, wszStatus, iProgValue,
lProgressCurrent, lProgressMax);
hr = S_OK;
}
else
{
hr = S_FALSE;
}
return hr;
}
HRESULT COfflineSync::UpdateEnd(
const SUBSCRIPTIONCOOKIE *pSubscriptionCookie,
long lSizeDownloaded,
HRESULT hrResult,
LPCWSTR wszResult)
{
HRESULT hr;
int index = FindCookie(pSubscriptionCookie);
if (index != -1)
{
DWORD dwStatus;
m_pItems[index] = CLSID_NULL; // Forget about it
m_nItemsCompleted++;
if (SUCCEEDED(hrResult))
{
dwStatus = SYNCMGRSTATUS_SUCCEEDED;
}
else if (E_ABORT == hrResult)
{
dwStatus = SYNCMGRSTATUS_SKIPPED;
}
else
{
dwStatus = SYNCMGRSTATUS_FAILED;
}
CallSyncMgrProgress(pSubscriptionCookie, wszResult, dwStatus, 100, 100);
AreWeDoneYet();
hr = S_OK;
}
else
{
hr = S_FALSE;
}
return hr;
}
STDMETHODIMP COfflineSync::ReportError(
const SUBSCRIPTIONCOOKIE *pSubscriptionCookie,
HRESULT hrError,
LPCWSTR wszError)
{
HRESULT hr;
if ((FindCookie(pSubscriptionCookie) != -1) &&
(NULL != m_pSyncCallback))
{
DWORD dwErrorLevel;
switch (hrError)
{
case E_ABORT:
dwErrorLevel = SYNCMGRLOGLEVEL_INFORMATION;
break;
case INET_E_AGENT_MAX_SIZE_EXCEEDED:
case INET_E_SCHEDULED_UPDATES_DISABLED:
case INET_E_SCHEDULED_UPDATES_RESTRICTED:
case INET_E_SCHEDULED_UPDATE_INTERVAL:
case INET_E_SCHEDULED_EXCLUDE_RANGE:
case INET_E_AGENT_WARNING:
dwErrorLevel = SYNCMGRLOGLEVEL_WARNING;
break;
default:
dwErrorLevel = FAILED(hrError) ? SYNCMGRLOGLEVEL_ERROR : SYNCMGRLOGLEVEL_INFORMATION;
break;
}
SYNCMGRLOGERRORINFO errInfo;
errInfo.cbSize = sizeof(SYNCMGRLOGERRORINFO);
errInfo.mask = SYNCMGRLOGERROR_ITEMID;
errInfo.ItemID = *pSubscriptionCookie;
m_pSyncCallback->LogError(dwErrorLevel, wszError, &errInfo);
hr = S_OK;
}
else
{
hr = S_FALSE;
}
return hr;
}
HRESULT COfflineSync::CallSyncMgrProgress(const SUBSCRIPTIONCOOKIE *pSubscriptionCookie,
const WCHAR *lpcStatusText, DWORD dwStatusType, INT iProgValue, INT iMaxValue)
{
HRESULT hr = S_OK;
SYNCMGRPROGRESSITEM smpi;
ASSERT(NULL != m_pSyncCallback);
smpi.cbSize = sizeof(SYNCMGRPROGRESSITEM);
smpi.mask = 0;
if (NULL != lpcStatusText)
{
smpi.mask |= SYNCMGRPROGRESSITEM_STATUSTEXT;
smpi.lpcStatusText = lpcStatusText;
}
if ((DWORD)(-1) != dwStatusType)
{
smpi.mask |= SYNCMGRPROGRESSITEM_STATUSTYPE;
smpi.dwStatusType = dwStatusType;
}
if (iProgValue >= 0)
{
smpi.mask |= SYNCMGRPROGRESSITEM_PROGVALUE;
smpi.iProgValue = iProgValue;
}
if (iMaxValue >= 0)
{
smpi.mask |= SYNCMGRPROGRESSITEM_MAXVALUE;
smpi.iMaxValue = iMaxValue;
}
// We still call progress even if smpi.mask is 0 just in case we should
// respond to a cancel.
HRESULT hrProgress = m_pSyncCallback->Progress(*pSubscriptionCookie, &smpi);
switch(hrProgress)
{
case S_SYNCMGR_CANCELITEM:
m_pThrottler->AbortItems(1, pSubscriptionCookie);
break;
case S_SYNCMGR_CANCELALL:
m_pThrottler->AbortAll();
break;
}
return hr;
}
HRESULT COfflineSync::UpdateSyncMgrStatus(const SUBSCRIPTIONCOOKIE *pSubscriptionCookie,
LPCWSTR wszStatusMsg, DWORD dwStatus)
{
HRESULT hr;
if ((FindCookie(pSubscriptionCookie) != -1) &&
(NULL != m_pSyncCallback))
{
CallSyncMgrProgress(pSubscriptionCookie, wszStatusMsg, dwStatus, -1, -1);
hr = S_OK;
}
else
{
hr = S_FALSE;
}
return hr;
}
HRESULT COfflineSync::DupItems(ULONG cbNumItems, SUBSCRIPTIONCOOKIE *pItemIDs)
{
HRESULT hr;
ASSERT(0 != cbNumItems);
ASSERT(NULL != pItemIDs);
ASSERT((0 == m_nItemsToRun) || (TRUE == m_fCookiesSpecified));
if (NULL != m_pItems)
{
delete [] m_pItems;
}
m_pItems = new SUBSCRIPTIONCOOKIE[cbNumItems];
if (NULL != m_pItems)
{
memcpy(m_pItems, pItemIDs, cbNumItems * sizeof(SUBSCRIPTIONCOOKIE));
m_nItemsToRun = cbNumItems;
hr = S_OK;
}
else
{
hr = E_OUTOFMEMORY;
}
return hr;
}
BOOL COfflineSync::AreWeDoneYet()
{
BOOL rc;
if (m_nItemsCompleted != m_nItemsToRun)
{
rc = FALSE;
}
else
{
Cleanup();
rc = TRUE;
}
return rc;
}
HRESULT COfflineSync::GetSubsMgr2()
{
HRESULT hr = S_FALSE;
if (NULL == m_pSubsMgr2)
{
hr = CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER,
IID_ISubscriptionMgr2, (void**)&m_pSubsMgr2);
if (FAILED(hr))
{
DBG_WARN("Failed to allocate subscription store (aborting)");
}
}
return hr;
}
// returns index into m_pItems on success, -1 on failure
int COfflineSync::FindCookie(const SUBSCRIPTIONCOOKIE *pCookie)
{
int index = -1;
if (NULL != m_pItems)
{
for (ULONG i = 0; i < m_nItemsToRun; i++)
{
if (m_pItems[i] == *pCookie)
{
index = i;
break;
}
}
}
return index;
}
COfflineEnum::COfflineEnum()
{
ASSERT(NULL == m_pItems);
ASSERT(0 == m_iNumItems);
// Maintain global count of objects
DllAddRef();
// Initialize object
m_cRef = 1;
}
COfflineEnum::~COfflineEnum()
{
DllRelease();
SAFELOCALFREE(m_pItems);
}
HRESULT COfflineEnum::LoadItem(ISubscriptionMgr2 *pSubsMgr2,
const SUBSCRIPTIONCOOKIE *pCookie, SYNCMGRITEM *pItem, DWORD dwItemState)
{
HRESULT hr;
ISubscriptionItem *pSubItem;
ASSERT(NULL != pSubsMgr2);
hr = pSubsMgr2->GetItemFromCookie(pCookie, &pSubItem);
if (SUCCEEDED(hr))
{
BSTR bstrName;
pItem->cbSize = sizeof(SYNCMGRITEM);
pItem->dwFlags = SYNCMGRITEM_HASPROPERTIES;
pItem->ItemID = *pCookie;
pItem->dwItemState = dwItemState;
DATE dt;
if (SUCCEEDED(ReadDATE(pSubItem, c_szPropCompletionTime, &dt)))
{
FILETIME ft;
pItem->dwFlags |= SYNCMGRITEM_LASTUPDATETIME;
VariantTimeToFileTime(dt, ft);
LocalFileTimeToFileTime(&ft, &pItem->ftLastUpdate);
}
ReadBSTR(pSubItem, c_szPropName, &bstrName);
if (NULL != bstrName)
{
StrCpyNW(pItem->wszItemName, bstrName, ARRAYSIZE(pItem->wszItemName));
SysFreeString(bstrName);
}
else
{
ASSERT(L'\0' == pItem->wszItemName[0]);
}
pItem->hIcon = LoadItemIcon(pSubItem, FALSE);
pSubItem->Release();
}
return hr;
}
HRESULT COfflineEnum::Init(ISubscriptionMgr2 *pSubsMgr2, ULONG nItems,
SUBSCRIPTIONCOOKIE *pInitCookies, ISyncMgrEnumItems **ppenum, DWORD dwSyncFlags)
{
HRESULT hr = E_FAIL;
DWORD dwCheckState;
ASSERT(NULL != ppenum);
if ((NULL == pSubsMgr2) ||
(NULL == ppenum) ||
((nItems > 0) && (pInitCookies == NULL)))
{
return E_UNEXPECTED;
}
switch (dwSyncFlags & SYNCMGRFLAG_EVENTMASK)
{
case SYNCMGRFLAG_CONNECT: // Sync was invoked by a network connect
case SYNCMGRFLAG_PENDINGDISCONNECT: // Sync was invoked by a pending network disconnect
case SYNCMGRFLAG_MANUAL: // Sync was invoked manually
case SYNCMGRFLAG_IDLE: // Sync was programmatically invokd
case SYNCMGRFLAG_INVOKE: // Sync was programmatically invokd
case SYNCMGRFLAG_SCHEDULED: // Sync was invoked by a scheduled update
dwCheckState = SYNCMGRITEMSTATE_CHECKED;
break;
default:
dwCheckState = SYNCMGRITEMSTATE_UNCHECKED;
break;
}
// Enumerate cookies
m_iEnumPtr = 0;
if (0 == nItems)
{
IEnumSubscription *pEnumSubscriptions;
hr = pSubsMgr2->EnumSubscriptions(0, &pEnumSubscriptions);
if (SUCCEEDED(hr))
{
ASSERT(NULL != pEnumSubscriptions);
pEnumSubscriptions->GetCount(&m_iNumItems);
SUBSCRIPTIONCOOKIE *pCookies = (SUBSCRIPTIONCOOKIE *)MemAlloc(LMEM_FIXED,
m_iNumItems * sizeof(SUBSCRIPTIONCOOKIE));
m_pItems = (SYNCMGRITEM *)MemAlloc(LMEM_FIXED, m_iNumItems * sizeof(SYNCMGRITEM));
if ((NULL != m_pItems) && (NULL != pCookies))
{
hr = pEnumSubscriptions->Next(m_iNumItems, pCookies, &m_iNumItems);
SYNCMGRITEM *pItem = m_pItems;
for (ULONG i = 0; i < m_iNumItems; i++, pItem++)
{
hr = LoadItem(pSubsMgr2, &pCookies[i], pItem,
dwCheckState);
if (FAILED(hr))
{
break;
}
}
}
else
{
hr = E_OUTOFMEMORY;
}
SAFELOCALFREE(pCookies);
pEnumSubscriptions->Release();
}
}
else
{
m_pItems = (SYNCMGRITEM *)MemAlloc(LMEM_FIXED, nItems * sizeof(SYNCMGRITEM));
if (NULL != m_pItems)
{
SYNCMGRITEM *pItem = m_pItems;
SUBSCRIPTIONCOOKIE *pCurCookie = pInitCookies;
m_iNumItems = nItems;
for (ULONG i = 0; i < m_iNumItems; i++, pCurCookie++, pItem++)
{
hr = LoadItem(pSubsMgr2, pCurCookie, pItem, dwCheckState);
if (FAILED(hr))
{
break;
}
}
}
else
{
hr = E_OUTOFMEMORY;
}
}
if (SUCCEEDED(hr))
{
// If we were invoked programatically, then tell syncmgr to leave
// item preferences alone.
hr = (nItems == 0) ? S_OK : S_OK; // TODO: S_SYNCMGR_MISSINGITEMS;
*ppenum = this;
AddRef();
}
return hr;
}
//
// IUnknown members
//
STDMETHODIMP_(ULONG) COfflineEnum::AddRef(void)
{
return ++m_cRef;
}
STDMETHODIMP_(ULONG) COfflineEnum::Release(void)
{
if( 0L != --m_cRef )
return m_cRef;
delete this;
return 0L;
}
STDMETHODIMP COfflineEnum::QueryInterface(REFIID riid, void ** ppv)
{
*ppv=NULL;
// Validate requested interface
if ((IID_IUnknown == riid) ||
(IID_ISyncMgrEnumItems == riid))
{
*ppv = (ISyncMgrEnumItems *)this;
}
else
{
return E_NOINTERFACE;
}
// Addref through the interface
((LPUNKNOWN)*ppv)->AddRef();
return S_OK;
}
// IEnumOfflineItems members
STDMETHODIMP COfflineEnum::Next(ULONG celt, LPSYNCMGRITEM rgelt, ULONG *pceltFetched)
{
if ((0 == celt) ||
((celt > 1) && (NULL == pceltFetched)) ||
(NULL == rgelt))
{
return E_INVALIDARG;
}
if (!m_pItems)
{
return E_FAIL;
}
*pceltFetched = 0;
ULONG ul;
for (ul=0; (ul<celt) && (m_iEnumPtr<m_iNumItems); m_iEnumPtr++)
{
*rgelt = m_pItems[m_iEnumPtr];
rgelt ++; ul++;
}
*pceltFetched = ul;
return (ul == celt) ? S_OK : S_FALSE;
}
STDMETHODIMP COfflineEnum::Skip(ULONG celt)
{
m_iEnumPtr += celt;
if (m_iEnumPtr > m_iNumItems)
{
m_iEnumPtr = m_iNumItems;
return S_FALSE;
}
return S_OK;
}
STDMETHODIMP COfflineEnum::Reset(void)
{
m_iEnumPtr = 0;
return S_OK;
}
STDMETHODIMP COfflineEnum::Clone(ISyncMgrEnumItems **ppenum)
{
return E_NOTIMPL;
}