//+----------------------------------------------------------------------------
//
//  DS Administration MMC snapin.
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1999
//
//  File:      DataObj.cpp
//
//  Contents:   Data Object implementation.
//
//  Classes:    CDSDataObject
//
//  History:    02-Oct-96 WayneSc    Created
//              06-Feb-97 EricB - added Property Page Data support
//-----------------------------------------------------------------------------

#include "stdafx.h"
#include "resource.h"

#include "DSdirect.h"
#include "DataObj.h"
#include "dssnap.h"

#include <lm.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//+----------------------------------------------------------------------------
// MMC's clipboard formats:
//-----------------------------------------------------------------------------
// Snap-in NodeType in both GUID format and string format
CLIPFORMAT CDSDataObject::m_cfNodeType =
                                (CLIPFORMAT)RegisterClipboardFormat(CCF_NODETYPE);
CLIPFORMAT CDSDataObject::m_cfNodeTypeString = 
                                (CLIPFORMAT)RegisterClipboardFormat(CCF_SZNODETYPE);
CLIPFORMAT CDSDataObject::m_cfDisplayName =
                                (CLIPFORMAT)RegisterClipboardFormat(CCF_DISPLAY_NAME);
CLIPFORMAT CDSDataObject::m_cfCoClass =
                                (CLIPFORMAT)RegisterClipboardFormat(CCF_SNAPIN_CLASSID);
CLIPFORMAT CDSDataObject::m_cfpMultiSelDataObj =
                                (CLIPFORMAT)RegisterClipboardFormat(CCF_MMC_MULTISELECT_DATAOBJECT);
CLIPFORMAT CDSDataObject::m_cfMultiObjTypes =
                                (CLIPFORMAT)RegisterClipboardFormat(CCF_OBJECT_TYPES_IN_MULTI_SELECT);
CLIPFORMAT CDSDataObject::m_cfMultiSelDataObjs =
                                (CLIPFORMAT)RegisterClipboardFormat(CCF_MULTI_SELECT_SNAPINS);
CLIPFORMAT CDSDataObject::m_cfPreload =
                        (CLIPFORMAT)RegisterClipboardFormat(CCF_SNAPIN_PRELOADS);

//+----------------------------------------------------------------------------
// Our clipboard formats:
//-----------------------------------------------------------------------------

CLIPFORMAT CDSDataObject::m_cfInternal = 
                                (CLIPFORMAT)RegisterClipboardFormat(SNAPIN_INTERNAL);
CLIPFORMAT CDSDataObject::m_cfDsObjectNames = 
                                (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);
CLIPFORMAT CDSDataObject::m_cfDsDisplaySpecOptions =
                                (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DS_DISPLAY_SPEC_OPTIONS);
CLIPFORMAT CDSDataObject::m_cfDsSchemaPath =
                                (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DS_SCHEMA_PATH);
CLIPFORMAT CDSDataObject::m_cfPropSheetCfg =
                        (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DS_PROPSHEETCONFIG);
CLIPFORMAT CDSDataObject::m_cfParentHwnd =
                        (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DS_PARENTHWND);

CLIPFORMAT CDSDataObject::m_cfComponentData = 
                        (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DS_COMPDATA);

CLIPFORMAT CDSDataObject::m_cfColumnID =
                        (CLIPFORMAT)RegisterClipboardFormat(CCF_COLUMN_SET_ID);

CLIPFORMAT CDSDataObject::m_cfMultiSelectProppage =
                        (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DS_MULTISELECTPROPPAGE);

//+----------------------------------------------------------------------------
// CDSDataObject implementation
//-----------------------------------------------------------------------------

//+----------------------------------------------------------------------------
//
//  Method:     CDSDataObject::IDataObject::GetData
//
//  Synopsis:   Returns data, in this case the Prop Page format data.
//
//-----------------------------------------------------------------------------
STDMETHODIMP
CDSDataObject::GetData(FORMATETC * pFormatEtc, STGMEDIUM * pMedium)
{
  //TRACE(_T("xx.%03x> CDSDataObject(0x%x)::GetData\n"),
  //      GetCurrentThreadId(), this);
  HRESULT hr = S_OK;
  if (IsBadWritePtr(pMedium, sizeof(STGMEDIUM)))
  {
    return E_INVALIDARG;
  }
  if (!(pFormatEtc->tymed & TYMED_HGLOBAL))
  {
    return DV_E_TYMED;
  }

  if (pFormatEtc->cfFormat == m_cfDsObjectNames)
  {
    // make a deep copy of the cached data
    pMedium->hGlobal = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
                                        m_nDSObjCachedBytes);
    if (pMedium->hGlobal == NULL)
    {
      return E_OUTOFMEMORY;
    }
    memcpy(pMedium->hGlobal, m_pDSObjCached, m_nDSObjCachedBytes);
  } 
  else if (pFormatEtc->cfFormat == m_cfDsDisplaySpecOptions)
  {
    //
    // Get the DSDISPLAYSPECOPTIONS structure.
    // Use the value cached in the component data.
    //
    if (m_pDsComponentData != NULL)
    {
      PDSDISPLAYSPECOPTIONS pDsDisplaySpecOptions = m_pDsComponentData->GetDsDisplaySpecOptions();
      pMedium->hGlobal = (HGLOBAL)pDsDisplaySpecOptions;
      if (pDsDisplaySpecOptions == NULL)
        return E_OUTOFMEMORY;
    }
    else
    {
      return E_FAIL;
    }
  }
  else if (pFormatEtc->cfFormat == m_cfDsSchemaPath)
  {
    ASSERT(m_pDsComponentData);
    LPCWSTR lpszSchemaNamingCtx = m_pDsComponentData->GetBasePathsInfo()->GetSchemaNamingContext();
    size_t nSchemaNamingCtxLen = wcslen(lpszSchemaNamingCtx);
    if (nSchemaNamingCtxLen == 0)
    {
      return E_FAIL;
    }
    PWSTR pwzSchemaPath = (PWSTR)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
                                             (nSchemaNamingCtxLen +1) * sizeof(WCHAR));
    if (pwzSchemaPath == NULL)
    {
      return STG_E_MEDIUMFULL;
    }

    wcscpy(pwzSchemaPath, lpszSchemaNamingCtx);

    pMedium->hGlobal = pwzSchemaPath;
  }
  else if (pFormatEtc->cfFormat == m_cfPropSheetCfg)
  {
    // Return the property sheet notification handle.
    //
    PPROPSHEETCFG pSheetCfg;

    pSheetCfg = (PPROPSHEETCFG)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
                                           sizeof(PROPSHEETCFG));
    if (pSheetCfg == NULL)
    {
      return STG_E_MEDIUMFULL;
    }

    pSheetCfg->lNotifyHandle = m_lNotifyHandle;
    pSheetCfg->hwndParentSheet = m_hwndParentSheet;
    pSheetCfg->hwndHidden = m_pDsComponentData->GetHiddenWindow();
    pSheetCfg->wParamSheetClose = reinterpret_cast<WPARAM>(m_internal.m_cookie);

    pMedium->hGlobal = (HGLOBAL)pSheetCfg;
  }
  else if (pFormatEtc->cfFormat == m_cfParentHwnd)
  {
    // return the HWND of the MMC frame window
    HWND* pHWndMain = (HWND*)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
                                         sizeof(HWND));
    m_pDsComponentData->m_pFrame->GetMainWindow(pHWndMain);
    pMedium->hGlobal = (HGLOBAL)pHWndMain;
  }
  else if (pFormatEtc->cfFormat == m_cfComponentData)
  {
    // return the m_pDsComponentData the data object is bound to
    CDSComponentData** ppCD = (CDSComponentData**)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
                                           sizeof(CDSComponentData*));
    if (ppCD != NULL)
    {
      *ppCD = m_pDsComponentData;
      pMedium->hGlobal = (HGLOBAL)ppCD;
    }
    else
    {
      return STG_E_MEDIUMFULL;
    }
  }
  else if (pFormatEtc->cfFormat == m_cfMultiSelectProppage)
  {
    if (m_szUniqueID == _T(""))
    {
      return E_FAIL;
    }

    UINT nLength = m_szUniqueID.GetLength();
    PWSTR pszGuidString = (PWSTR)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
                                             (nLength +1) * sizeof(WCHAR));
    if (pszGuidString == NULL)
    {
      return STG_E_MEDIUMFULL;
    }

    wcscpy(pszGuidString, m_szUniqueID);

    pMedium->hGlobal = pszGuidString;
  }
  else if (pFormatEtc->cfFormat == m_cfMultiObjTypes)
  {
    hr = CreateMultiSelectObject(pMedium);
  } 
  else if (pFormatEtc->cfFormat == m_cfInternal)
  {
    hr = CreateInternal(pMedium);
  } 
  else if (pFormatEtc->cfFormat == m_cfColumnID)
  {
    hr = CreateColumnID(pMedium); 
  }
  else
  {
    return DV_E_FORMATETC;
  }

  pMedium->tymed = TYMED_HGLOBAL;
  pMedium->pUnkForRelease = NULL;

  return hr;
}

//+----------------------------------------------------------------------------
//
//  Method:     CDSDataObject::IDataObject::GetDataHere
//
//  Synopsis:   Returns data in callers storage medium
//
//-----------------------------------------------------------------------------
STDMETHODIMP
CDSDataObject::GetDataHere(LPFORMATETC lpFormatetc, LPSTGMEDIUM lpMedium)
{
    HRESULT hr = DV_E_CLIPFORMAT;

    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    // Based on the CLIPFORMAT write data to the stream
    const CLIPFORMAT cf = lpFormatetc->cfFormat;

    if(cf == m_cfNodeType)
    {
        hr = CreateNodeTypeData(lpMedium);
    }
    else if(cf == m_cfNodeTypeString) 
    {
        hr = CreateNodeTypeStringData(lpMedium);
    }
    else if (cf == m_cfDisplayName)
    {
        hr = CreateDisplayName(lpMedium);
    }
    else if (cf == m_cfCoClass)
    {
        hr = CreateCoClassID(lpMedium);
    }
    else if (cf == m_cfPreload)
    {
        // MMC notify the snapin before loading with the MMCN_PRELOAD notify message

        BOOL bPreload = TRUE;
        hr = Create(&bPreload, sizeof(BOOL), lpMedium);
    }
    return hr;
}

//+----------------------------------------------------------------------------
//
//  Method:     CDSDataObject::IDataObject::SetData
//
//  Synopsis:   Allows the caller to set data object values.
//
//-----------------------------------------------------------------------------
STDMETHODIMP CDSDataObject::SetData(FORMATETC * pFormatEtc, STGMEDIUM * pMedium,
                                    BOOL fRelease)
{
  HRESULT hr = S_OK;

  if (pFormatEtc->cfFormat == m_cfPropSheetCfg)
  {
    if (!(pFormatEtc->tymed & TYMED_HGLOBAL))
    {
      return DV_E_TYMED;
    }

    PPROPSHEETCFG pSheetCfg = (PPROPSHEETCFG)pMedium->hGlobal;

    if ( NULL != pSheetCfg->lNotifyHandle)
    {
      m_lNotifyHandle = pSheetCfg->lNotifyHandle;
    }

    if (NULL != pSheetCfg->hwndParentSheet)
    {
      m_hwndParentSheet = pSheetCfg->hwndParentSheet;
    }

    if (fRelease)
    {
      GlobalFree(pMedium->hGlobal);
    }
    return S_OK;
  }
  else if (pFormatEtc->cfFormat == m_cfMultiSelectProppage)
  {
    if (!(pFormatEtc->tymed & TYMED_HGLOBAL))
    {
      return DV_E_TYMED;
    }

    PWSTR pszGuidString = (PWSTR)pMedium->hGlobal;
    if (pszGuidString == NULL)
    {
      ASSERT(FALSE);
      return E_FAIL;
    }

    m_szUniqueID = pszGuidString;
  }
  else
  {
    return DV_E_FORMATETC;
  }
  return hr;
}


//+----------------------------------------------------------------------------
//
//  Method:     CDSDataObject::IDataObject::EnumFormatEtc
//
//  Synopsis:   
//
//-----------------------------------------------------------------------------
STDMETHODIMP CDSDataObject::EnumFormatEtc(DWORD, LPENUMFORMATETC*)
{
	return E_NOTIMPL;
}

//+----------------------------------------------------------------------------
//
//  Method:     CDSDataObject::Create
//
//  Synopsis:   
//
//-----------------------------------------------------------------------------
HRESULT
CDSDataObject::Create(const void * pBuffer, int len, LPSTGMEDIUM pMedium)
{
    HRESULT hr = DV_E_TYMED;

    // Do some simple validation
    if (pBuffer == NULL || pMedium == NULL)
        return E_POINTER;

    // Make sure the type medium is HGLOBAL
    if (pMedium->tymed == TYMED_HGLOBAL)
    {
        // Create the stream on the hGlobal passed in
        LPSTREAM lpStream;
        hr = CreateStreamOnHGlobal(pMedium->hGlobal, FALSE, &lpStream);

        if (SUCCEEDED(hr))
        {
            // Write to the stream the number of bytes
            unsigned long written;
		    hr = lpStream->Write(pBuffer, len, &written);

            // Because we told CreateStreamOnHGlobal with 'FALSE', 
            // only the stream is released here.
            // Note - the caller (i.e. snap-in, object) will free the HGLOBAL 
            // at the correct time.  This is according to the IDataObject specification.
            lpStream->Release();
        }
    }

    return hr;
}

//+----------------------------------------------------------------------------
//
//  Method:     CDSDataObject::CreateNodeTypeData
//
//  Synopsis:   Create the node type object in GUID format
//
//-----------------------------------------------------------------------------
HRESULT
CDSDataObject::CreateNodeTypeData(LPSTGMEDIUM lpMedium)
{
    TRACE(_T("xx.%03x> GetDataHere on Node Type\n"), GetCurrentThreadId());
    GUID* pGuid = m_internal.m_cookie->GetGUID();
    return Create(pGuid, sizeof(GUID), lpMedium);
}

//+----------------------------------------------------------------------------
//
//  Method:     CDSDataObject::CreateNodeTypeStringData
//
//  Synopsis:   Create the node type object in GUID string format
//
//-----------------------------------------------------------------------------
HRESULT
CDSDataObject::CreateNodeTypeStringData(LPSTGMEDIUM lpMedium)
{
    TRACE(_T("xx.%03x> GetDataHere on Node Type String\n"), GetCurrentThreadId());
    GUID* pGuid = m_internal.m_cookie->GetGUID();
    CString strGUID;
    WCHAR * szGUID;
    StringFromCLSID(*pGuid, &szGUID);
    strGUID = szGUID;
    return Create (strGUID, strGUID.GetLength()+ sizeof(wchar_t),
                   lpMedium);

}

//+----------------------------------------------------------------------------
//
//  Method:     CDSDataObject::CreateDisplayName
//
//  Synopsis:   This is the display named used in the scope pane and snap-in
//              manager.
//
//-----------------------------------------------------------------------------
HRESULT
CDSDataObject::CreateDisplayName(LPSTGMEDIUM lpMedium)
{
    TRACE(_T("xx.%03x> GetDataHere on Display Name\n"), GetCurrentThreadId());

    // Load the name from resource
    // Note - if this is not provided, the console will used the snap-in name

    CString szDispName;
    szDispName.LoadString( ResourceIDForSnapinType[ m_internal.m_snapintype ]);

    return Create(szDispName, ((szDispName.GetLength()+1) * sizeof(wchar_t)), lpMedium);
}

//+----------------------------------------------------------------------------
//
//  Method:     CDSDataObject::CreateMultiSelectObject
//
//  Synopsis:   this is to create the list of types selected
//
//-----------------------------------------------------------------------------

HRESULT
CDSDataObject::CreateMultiSelectObject(LPSTGMEDIUM lpMedium)
{
  TRACE(_T("xx.%03x> GetDataHere on MultiSelectObject\n"), GetCurrentThreadId());
    
  CUINode** cookieArray = NULL;
  cookieArray = (CUINode**) GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
                                          m_internal.m_cookie_count*sizeof(CUINode*));
  if (!cookieArray) {
    return E_OUTOFMEMORY;
  }
  for (UINT k=0; k<m_internal.m_cookie_count; k++)
  {
    if (k==0)
      cookieArray[k] = m_internal.m_cookie;
    else
      cookieArray[k] = m_internal.m_p_cookies[k-1];
  }
  BOOL* bDuplicateArr = NULL;
  bDuplicateArr = (BOOL*)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
                                     m_internal.m_cookie_count*sizeof(BOOL));
  if (!bDuplicateArr) {
    if (cookieArray)
      GlobalFree (cookieArray);
    return E_OUTOFMEMORY;
  }
  //ZeroMemory(bDuplicateArr, m_internal.m_cookie_count*sizeof(BOOL));

  UINT cCount = 0;
  for (UINT index=0; index<m_internal.m_cookie_count; index++)
  {
    for (UINT j=0; j<index;j++)
    {
      GUID Guid1 = *(cookieArray[index]->GetGUID());
      GUID Guid2 = *(cookieArray[j]->GetGUID());
      if (IsEqualGUID (Guid1, Guid2)) 
      {
        bDuplicateArr[index] = TRUE;
        break; //repeated GUID
      }
    }
    if (!bDuplicateArr[index])
      cCount++;
  }      

   
  UINT size = sizeof(SMMCObjectTypes) + (cCount - 1) * 
    sizeof(GUID);
  void * pTmp = ::GlobalAlloc(GPTR, size);
  if (!pTmp) {
    if (cookieArray) {
      GlobalFree (cookieArray);
    }
    if (bDuplicateArr) {
      GlobalFree (bDuplicateArr);
    }
    return E_OUTOFMEMORY;
  }
    
  SMMCObjectTypes* pdata = reinterpret_cast<SMMCObjectTypes*>(pTmp);
  pdata->count = cCount;
  UINT i = 0;
  for (index=0; index<m_internal.m_cookie_count; index++)
  {
    if (!bDuplicateArr[index])
    pdata->guid[i++] = *(cookieArray[index]->GetGUID());
  }
  ASSERT(i == cCount);
  lpMedium->hGlobal = pTmp;

  GlobalFree (cookieArray);
  GlobalFree (bDuplicateArr);

  return S_OK;
}

//+----------------------------------------------------------------------------
//
//  Method:     CDSDataObject::CreateInternal
//
//  Synopsis:   
//
//-----------------------------------------------------------------------------
HRESULT
CDSDataObject::CreateInternal(LPSTGMEDIUM lpMedium)
{
  HRESULT hr = S_OK;
  INTERNAL * pInt = NULL;
  void * pBuf = NULL;

  UINT size = sizeof(INTERNAL);
  size += sizeof(CUINode *) * (m_internal.m_cookie_count - 1);
  pBuf = GlobalAlloc (GPTR, size);
  pInt = (INTERNAL *) pBuf;
  lpMedium->hGlobal = pBuf;
  
  if (pInt != NULL &&
      m_internal.m_cookie_count > 1) 
  {
    // copy the data
    pInt->m_type = m_internal.m_type;
    pInt->m_cookie = m_internal.m_cookie;
    pInt->m_snapintype = m_internal.m_snapintype;
    pInt->m_cookie_count = m_internal.m_cookie_count;
    
    pInt->m_p_cookies = (CUINode **) ((BYTE *)pInt + sizeof(INTERNAL));
    memcpy (pInt->m_p_cookies, m_internal.m_p_cookies,
            sizeof(CUINode *) * (m_internal.m_cookie_count - 1));
    hr = Create(pBuf, size, lpMedium);
  }
  else 
  {
    hr = Create(&m_internal, sizeof(INTERNAL), lpMedium);
  }
  return hr;
}

//+----------------------------------------------------------------------------
//
//  Method:     CDSDataObject::CreateCoClassID
//
//  Synopsis:   
//
//-----------------------------------------------------------------------------
HRESULT
CDSDataObject::CreateCoClassID(LPSTGMEDIUM lpMedium)
{
  TRACE(_T("xx.%03x> GetDataHere on CoClass\n"), GetCurrentThreadId());
  CLSID CoClassID;
  
  switch (m_internal.m_snapintype) {
  case SNAPINTYPE_DS:
    CoClassID = CLSID_DSSnapin;
    break;
  case SNAPINTYPE_SITE:
    CoClassID = CLSID_SiteSnapin;
    break;
  default:
    memset (&CoClassID,0,sizeof(CLSID));
  }
  return Create(&CoClassID, sizeof(CLSID), lpMedium);
}


//+----------------------------------------------------------------------------
//
//  Method:     CDSDataObject::CreateColumnID
//
//  Synopsis:   
//
//-----------------------------------------------------------------------------
HRESULT 
CDSDataObject::CreateColumnID(LPSTGMEDIUM lpMedium)
{
  // build the column id
  CDSColumnSet* pColumnSet = (m_internal.m_cookie)->GetColumnSet(m_pDsComponentData);

  if (pColumnSet == NULL)
	  return DV_E_TYMED;

  LPCWSTR lpszID = pColumnSet->GetColumnID();
  size_t iLen = wcslen(lpszID);

  // allocate enough memory for the struct and the string for the column id
  SColumnSetID* pColumnID = (SColumnSetID*)malloc(sizeof(SColumnSetID) + (iLen * sizeof(WCHAR)));
  if (pColumnID != NULL)
  {
    memset(pColumnID, 0, sizeof(SColumnSetID) + (iLen * sizeof(WCHAR)));
    pColumnID->cBytes = static_cast<ULONG>(iLen * sizeof(WCHAR));
    memcpy(pColumnID->id, lpszID, (iLen * sizeof(WCHAR)));

    // copy the column id to global memory
    size_t cb = sizeof(SColumnSetID) + (iLen * sizeof(WCHAR));

    lpMedium->tymed = TYMED_HGLOBAL;
    lpMedium->hGlobal = ::GlobalAlloc(GMEM_SHARE|GMEM_MOVEABLE, cb);

    if (lpMedium->hGlobal == NULL)
      return STG_E_MEDIUMFULL;

    BYTE* pb = reinterpret_cast<BYTE*>(::GlobalLock(lpMedium->hGlobal));
    memcpy(pb, pColumnID, cb);

    ::GlobalUnlock(lpMedium->hGlobal);

    free(pColumnID);
  }
	return S_OK;
}


HRESULT CDSDataObject::CreateDsObjectNamesCached()
{
  if (m_pDSObjCached != NULL)
  {
    ::free(m_pDSObjCached);
    m_pDSObjCached = NULL;
    m_nDSObjCachedBytes = 0;
  }


  // figure out how much storage we need
  DWORD cbStorage = 0;
  INT cbStruct = sizeof(DSOBJECTNAMES) + 
    ((m_internal.m_cookie_count - 1) * sizeof(DSOBJECT));
  CString strPath;
  CString strClass;
  CUINode* pNode;
  CDSCookie * pCookie;

  //
  // this loop is to calc how much storage we need.
  //
  for (UINT index = 0; index < m_internal.m_cookie_count; index++)
  {
    if (index == 0) 
    {
      pNode = m_internal.m_cookie;
    } 
    else 
    {
      pNode = m_internal.m_p_cookies[index - 1];
    }

    pCookie = NULL;
    if (IS_CLASS(*pNode, CDSUINode))
    {
      pCookie = GetDSCookieFromUINode(pNode);
    }

    //
    // All the nodes must be of type CDSUINode or else we fail
    //
    if (pCookie == NULL)
    {
      return E_FAIL;
    }

    m_pDsComponentData->GetBasePathsInfo()->ComposeADsIPath(strPath, pCookie->GetPath());
    strClass = pCookie->GetClass();
    if (_wcsicmp(strClass, L"Unknown") == 0)
    {
      strClass = L"";
    }
    cbStorage += (strPath.GetLength() + 1 + strClass.GetLength() + 1) * sizeof(TCHAR);
  }

  //
  // Allocate the needed storage
  //
  m_pDSObjCached = (LPDSOBJECTNAMES)malloc(cbStruct + cbStorage);
  
  if (m_pDSObjCached == NULL)
  {
    return STG_E_MEDIUMFULL;
  }
  m_nDSObjCachedBytes = cbStruct + cbStorage;

  switch (m_internal.m_snapintype)
  {
    case SNAPINTYPE_DS:
      m_pDSObjCached->clsidNamespace = CLSID_DSSnapin;
      break;
    case SNAPINTYPE_SITE:
      m_pDSObjCached->clsidNamespace = CLSID_SiteSnapin;
      break;
    default:
      memset (&m_pDSObjCached->clsidNamespace, 0, sizeof(CLSID));
  }

  m_pDSObjCached->cItems = m_internal.m_cookie_count;
  DWORD NextOffset = cbStruct;
  for (index = 0; index < m_internal.m_cookie_count; index++)
  {
    if (index == 0) 
    {
      pNode = m_internal.m_cookie;
    } 
    else 
    {
      pNode = m_internal.m_p_cookies[index - 1];
    }

    pCookie = NULL;
    if (IS_CLASS(*pNode, CDSUINode))
    {
      pCookie = GetDSCookieFromUINode(pNode);
    }

    //
    // All nodes must be of type CDSUINode or else we fail
    //
    if (pCookie == NULL)
    {
      return E_FAIL;
    }

    //
    // Set the data from the node and node data
    //
    m_pDSObjCached->aObjects[index].dwFlags = pNode->IsContainer() ? DSOBJECT_ISCONTAINER : 0;
    m_pDSObjCached->aObjects[index].dwProviderFlags = (m_pDsComponentData->IsAdvancedView()) ?
      DSPROVIDER_ADVANCED : 0;
    m_pDsComponentData->GetBasePathsInfo()->ComposeADsIPath(strPath, pCookie->GetPath());
    strClass = pCookie->GetClass();
    if (_wcsicmp(strClass, L"Unknown") == 0)
    {
      strClass = L"";
    }

    m_pDSObjCached->aObjects[index].offsetName = NextOffset;
    m_pDSObjCached->aObjects[index].offsetClass = NextOffset + 
      (strPath.GetLength() + 1) * sizeof(TCHAR);

    _tcscpy((LPTSTR)((BYTE *)m_pDSObjCached + NextOffset), strPath);
    NextOffset += (strPath.GetLength() + 1) * sizeof(TCHAR);

    _tcscpy((LPTSTR)((BYTE *)m_pDSObjCached + NextOffset), strClass);
    NextOffset += (strClass.GetLength() + 1) * sizeof(TCHAR);
  }
  return S_OK;
}


//+----------------------------------------------------------------------------
//
//  Method:     CDSDataObject::AddCookie
//
//  Synopsis:   adds a cookie to the data object. if this is
//              the first cookie it goes in m_cookie, else it
//              goes into the cookie list m_p_cookies.
//
//-----------------------------------------------------------------------------
void 
CDSDataObject::AddCookie(CUINode* pUINode)
{
  const UINT MEM_CHUNK_SIZE = 10;
  void * pTMP = NULL;
  if (m_internal.m_cookie) {  // already have a cookie
    if ((m_internal.m_cookie_count - 1) % MEM_CHUNK_SIZE == 0) {
      if (m_internal.m_p_cookies) {
        pTMP = realloc (m_internal.m_p_cookies,
                        (m_internal.m_cookie_count - 1 +
                         MEM_CHUNK_SIZE) * sizeof (CUINode *));
      } else {
        pTMP = malloc (MEM_CHUNK_SIZE * sizeof (CUINode *));
      }
      if (pTMP == NULL) {
        TRACE(_T("CDataObject::AddCookie - malloc/realloc failed.."));
        ASSERT (pTMP != NULL);
      }
      m_internal.m_p_cookies = (CUINode **)pTMP;
    }
    (*(m_internal.m_p_cookies + m_internal.m_cookie_count - 1)) = pUINode;
    m_internal.m_cookie_count++;
  } else {
    m_internal.m_cookie = pUINode;
    m_internal.m_cookie_count = 1;
  }
  CreateDsObjectNamesCached();
}