text: #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;
  }
  
  