/*
 *      ComponentData.cxx
 *
 *
 *      Copyright (c) 1998-1999 Microsoft Corporation
 *
 *      PURPOSE:        Defines the CComponentData class.
 *
 *
 *      OWNER:          ptousig
 */

#include "headers.hxx"

// -----------------------------------------------------------------------------
CComponentData::CComponentData(CBaseSnapin *psnapin)
{
    Trace(tagBaseSnapinIComponentData, _T("--> CComponentData::CComponentData(psnapin=0x%08X)"), psnapin);

    ASSERT(psnapin);

    m_psnapin = psnapin;
    m_fIsRealComponentData = FALSE;
    m_pitemStandaloneRoot = NULL;

    Trace(tagBaseSnapinIComponentData, _T("<-- CComponentData::CComponentData has constructed 0x%08X"), this);
}

// -----------------------------------------------------------------------------
// Destructor doesn't do anything, but it's useful to have one for debugging
// purposes.
//
CComponentData::~CComponentData(void)
{
    Trace(tagBaseSnapinIComponentData, _T("--> CComponentData::~CComponentData(), this=0x%08X"), this);

    Psnapin()->ScOwnerDying(this);
    if (m_pitemStandaloneRoot)
    {
        Psnapin()->ScReleaseIfRootItem(m_pitemStandaloneRoot);
    }

    Trace(tagBaseSnapinIComponentData, _T("<-- CComponentData::~CComponentData has destructed 0x%08X"), this);
}

// -----------------------------------------------------------------------------
// This version of Pitem() is a shortcut, it forwards the call to the
// CBaseSnapin with the correct CComponentData parameter.
//
CBaseSnapinItem *CComponentData::Pitem( LPDATAOBJECT lpDataObject,
                                        HSCOPEITEM hscopeitem,
                                        long cookie)
{
    return Psnapin()->Pitem(this, NULL, lpDataObject, hscopeitem, cookie);
}

// -----------------------------------------------------------------------------
// The registration routine expects to find this method on the CComponentData
// but the real implementation is on the CBaseSnapin, so we just forward
// the call.
//
SC CComponentData::ScRegister(BOOL fRegister)
{
    CBaseSnapin* pSnapin = Psnapin();
    return pSnapin->ScRegister(fRegister);
}

// -----------------------------------------------------------------------------
// Is called by the MMC to initialize the object. We QueryInterface
// for pointers to the name space and console, which we cache in
// local variables. This is called only once, when the user clicks on
// the snapin.
//
// $REVIEW (ptousig) I am not sure which of interfaces we are allowed to QI
//                                       for from the parameter. The MMC docs are no help (as usual),
//
SC CComponentData::ScInitialize(LPUNKNOWN pUnknown)
{
    SC                              sc = S_OK;
    IImageListPtr   ipScopeImageList;

    ASSERT(pUnknown != NULL);
    ASSERT(m_ipConsoleNameSpace == NULL);

    m_fIsRealComponentData = TRUE;

    sc = Psnapin()->ScInitBitmaps();
    if (sc)
        goto Error;

    // These are CComQIPtr so they will call QueryInterface.
    m_ipConsoleNameSpace = pUnknown;
    m_ipConsole = pUnknown;
    m_ipResultData = pUnknown;
    m_ipPropertySheetProvider = pUnknown;

    // Get a pointer to the scope pane's IImageList.
    sc = m_ipConsole->QueryScopeImageList(&ipScopeImageList);
    if (sc)
        goto Error;

    // Set the icon strip for the scope pane.
    sc = ipScopeImageList->ImageListSetStrip(
                                             reinterpret_cast<long *>(static_cast<HBITMAP>(Psnapin()->BitmapSmall())),
                                             reinterpret_cast<long *>(static_cast<HBITMAP>(Psnapin()->BitmapLarge())),
                                             0, RGB(255, 0, 255));
    if (sc)
        goto Error;

Cleanup:
    return sc;
Error:
    TraceError(_T("CComponentData::ScInitialize"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// Handles component data event notification.
// See MMC docs for the meaning of 'arg' and 'param'.
//
SC CComponentData::ScNotify(LPDATAOBJECT lpDataObject, MMC_NOTIFY_TYPE event, long arg, long param)
{
    SC sc = S_OK;

    switch (event)
    {
    case MMCN_BTN_CLICK:
        sc = ScOnButtonClick(lpDataObject, (MMC_CONSOLE_VERB) param);
        break;

    case MMCN_DELETE:
        sc = ScOnDelete(lpDataObject);
        break;

    case MMCN_RENAME:
        sc = Psnapin()->ScOnRename(lpDataObject, reinterpret_cast<LPCTSTR>(param), IpConsole());
        break;

    case MMCN_EXPAND:
        sc = ScOnExpand(lpDataObject, arg != FALSE, param);
        break;

    case MMCN_EXPANDSYNC:
        sc = ScOnExpandSync(lpDataObject, reinterpret_cast<MMC_EXPANDSYNC_STRUCT *>(param));
        break;

    case MMCN_PROPERTY_CHANGE:
        sc = Psnapin()->ScOnPropertyChange(arg != FALSE, param, IpConsoleNameSpace(), IpConsole());
        break;

    case MMCN_REMOVE_CHILDREN:
        sc = ScOnRemoveChildren(lpDataObject, arg);
        break;

    default:
        sc = S_FALSE;
        ASSERT(_T("CComponentData::ScNotify: unimplemented event %x"));
        break;
    }

    if (sc)
        goto Error;

Cleanup:
    return sc;
Error:
    TraceError(_T("CComponentData::ScNotify"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// Releases all interfaces.
//
SC CComponentData::ScDestroy(void)
{
    m_ipConsole.Release();
    m_ipConsoleNameSpace.Release();
    m_ipResultData.Release();
    m_ipPropertySheetProvider.Release();

    return S_OK;
}

/*+-------------------------------------------------------------------------*
 *
 * CComponentData::ScQueryDispatch
 *
 * PURPOSE: Dummy implementation. Does nothing.
 *
 * PARAMETERS:
 *    MMC_COOKIE         cookie :
 *    DATA_OBJECT_TYPES  type :
 *    LPDISPATCH*        ppDispatch :
 *
 * RETURNS:
 *    SC
 *
 *+-------------------------------------------------------------------------*/
SC
CComponentData::ScQueryDispatch(MMC_COOKIE cookie, DATA_OBJECT_TYPES type, LPDISPATCH* ppDispatch)
{
    DECLARE_SC(sc, TEXT("CComponentData::ScQueryDispatch"));

    CBaseSnapinItem *pitem = NULL;

    // The component data can handle cookie types of CCT_SNAPIN_MANAGER and CCT_SCOPE.
    // CCT_RESULT are handled by CComponent.
    ASSERT(type==CCT_SCOPE);

    //
    // If the cookie does not correspond to a known object, return E_UNEXPECTED.
    // This is correct and is also a workaround for an MMC bug. See X5:74405.
    //
    if (cookie && (Psnapin()->Pcookielist()->find(cookie) == Psnapin()->Pcookielist()->end() ) )
    {
        sc = E_UNEXPECTED;
        return sc;
    }

    pitem = Pitem(NULL, 0, cookie);
    ASSERT(pitem);

    sc = pitem->ScQueryDispatch(cookie, type, ppDispatch);
    if (sc)
        return sc;

    return sc;
}

// -----------------------------------------------------------------------------
// Load information from the first root item.
//
// $REVIEW (ptousig) Why does the root item implement ScLoad() ?
//                                       If a snapin extends two nodes it has two root items, yet
//                                       only one of which will be saved/loaded.
//
SC CComponentData::ScLoad(IStream *pstream)
{
    SC sc = S_OK;

    // Load the snapin's serialized information.
    sc = Psnapin()->ScLoad(pstream);
    if (sc)
        goto Error;

    // Load the root item's serialized information.
    // Only makes sense for standalone snapins.
    sc = Pitem()->ScLoad(pstream);
    if (sc)
        goto Error;

Cleanup:
    return sc;
Error:
    TraceError(_T("CComponentData::ScLoad"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// Determines whether any settings have changed since the last time the file
// was saved.
//
// $REVIEW (ptousig) We are never dirty !!! Shouldn't we ask the Pitem() ?
//
SC CComponentData::ScIsDirty(void)
{
    return S_FALSE;
}

// -----------------------------------------------------------------------------
// If one of the commands added to the context menu is
// subsequently selected, MMC calls Command.
//
// Even though this method "looks" like the one in CComponent,
// the use of the Pitem() shortcut makes them different. This
// version does not pass a component to the real Pitem().
//
SC CComponentData::ScCommand(long nCommandID, LPDATAOBJECT pDataObject)
{
    SC              sc = S_OK;
    CBaseSnapinItem *pitem = NULL;

    pitem = Pitem(pDataObject);
    ASSERT(pitem);

    sc = pitem->ScCommand(nCommandID);
    if (sc)
        goto Error;

Cleanup:
    return sc;
Error:
    TraceError(_T("CComponentData::ScCommand"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// Gets display information for a scope pane item
// Warning: This is called very very often (on WM_PAINT) so it is important
// we don't take any high latency actions (ie Network or Disk access).
//
SC CComponentData::ScGetDisplayInfo(LPSCOPEDATAITEM pScopeItem)
{
    SC sc = S_OK;
    CBaseSnapinItem *pitem = NULL;

    pitem = Pitem(NULL, 0, pScopeItem->lParam);
    ASSERT(pitem);

    sc = pitem->ScGetDisplayInfo(pScopeItem);
    if (sc)
        goto Error;

Cleanup:
    return sc;
Error:
    TraceError(_T("CComponentData::ScGetDisplayInfo"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::CompareObjects(LPDATAOBJECT lpDataObjectA, LPDATAOBJECT lpDataObjectB)
{
    DECLARE_SC(sc,_T("CComponentData::CompareObjects"));
    Trace(tagBaseSnapinIComponentData, _T("--> %s::IComponentData::CompareObjects(lpDataObjectA=0x%08X, lpDataObjectB=0x%08X), this=0x%08X"), StrSnapinClassName(), lpDataObjectA, lpDataObjectB, this);
    ADMIN_TRY;
    sc=Psnapin()->ScCompareObjects(lpDataObjectA, lpDataObjectB);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIComponentData, _T("<-- %s::IComponentData::CompareObjects is returning hr=%s"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::GetDisplayInfo(LPSCOPEDATAITEM pItem)
{
    DECLARE_SC(sc,_T("CComponentData::GetDisplayInfo"));
    Trace(tagBaseSnapinIComponentDataGetDisplayInfo, _T("--> %s::IComponentData::GetDisplayInfo(cookie=0x%08X), this=0x%08X"), StrSnapinClassName(), pItem->lParam, this);
    ADMIN_TRY;
    sc=ScGetDisplayInfo(pItem);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIComponentDataGetDisplayInfo, _T("<-- %s::IComponentData::GetDisplayInfo is returning hr=%s"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::QueryDataObject(long cookie, DATA_OBJECT_TYPES type, LPDATAOBJECT * ppDataObject)
{
    DECLARE_SC(sc,_T("CComponentData::QueryDataObject"));
    Trace(tagBaseSnapinIComponentDataQueryDataObject, _T("--> %s::IComponentData::QueryDataObject(cookie=0x%08X, type=%s), this=0x%08X"), StrSnapinClassName(), cookie, SzGetDebugNameOfDATA_OBJECT_TYPES(type), this);
    ADMIN_TRY;
    //
    // If we receive E_UNEXPECTED we don't want to call MMCHrFromSc because
    // that will bring up an error message. We don't want an error message
    // in this case because of a known MMC bug (see bug X5:74405).
    // The bug says that we might receive QueryDataObject on items that
    // we were told no longer exists (by MMCN_REMOVE_CHILDREN).
    //
    sc = ScQueryDataObject(cookie, type, ppDataObject);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIComponentDataQueryDataObject, _T("<-- %s::IComponentData::QueryDataObject is returning hr=%s, *ppDataObject=0x%08X"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()), *ppDataObject);
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::Notify(LPDATAOBJECT lpDataObject, MMC_NOTIFY_TYPE event, long arg, long param)
{
    DECLARE_SC(sc,_T("CComponentData::Notify"));
    Trace(tagBaseSnapinIComponentData, _T("--> %s::IComponentData::Notify(lpDataObject=0x%08X, event=%s, arg=0x%08X, param=0x%08X), this=0x%08X"), StrSnapinClassName(), lpDataObject, SzGetDebugNameOfMMC_NOTIFY_TYPE(event), arg, param, this);
    ADMIN_TRY;
    sc=ScNotify(lpDataObject, event, arg, param);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIComponentData, _T("<-- %s::IComponentData::Notify is returning hr=%s"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::CreateComponent(LPCOMPONENT * ppComponent)
{
    DECLARE_SC(sc,_T("CComponentData::CreateComponent"));
    Trace(tagBaseSnapinIComponentData, _T("--> %s::IComponentData::CreateComponent(...), this=0x%08X"), StrSnapinClassName(), this);
    ADMIN_TRY;
    sc=ScCreateComponent(ppComponent);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIComponentData, _T("<-- %s::IComponentData::CreateComponent is returning hr=%s, *ppComponent=0x%08X"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()), *ppComponent);
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::Initialize(LPUNKNOWN pUnknown)
{
    DECLARE_SC(sc,_T("CComponentData::Initialize"));
    Trace(tagBaseSnapinIComponentData, _T("--> %s::IComponentData::Initialize(pUnknown=0x%08X), this=0x%08X"), StrSnapinClassName(), pUnknown, this);
    ADMIN_TRY;
    sc=ScInitialize(pUnknown);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIComponentData, _T("<-- %s::IComponentData::Initialize is returning hr=%s"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::Destroy(void)
{
    DECLARE_SC(sc,_T("CComponentData::Destroy"));
    Trace(tagBaseSnapinIComponentData, _T("--> %s::IComponentData::Destroy(), , this=0x%08X"), StrSnapinClassName(), this);
    ADMIN_TRY;
    sc=ScDestroy();
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIComponentData, _T("<-- %s::IComponentData::Destroy is returning hr=%s"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::QueryDispatch(MMC_COOKIE cookie, DATA_OBJECT_TYPES type, LPDISPATCH* ppDispatch)
{
    DECLARE_SC(sc,_T("CComponentData::QueryDispatch"));
    Trace(tagBaseSnapinIComponentData, _T("--> %s::IComponentData::QueryDispatch(), , this=0x%08X"), StrSnapinClassName(), this);
    ADMIN_TRY;
    sc=ScQueryDispatch(cookie, type, ppDispatch);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIComponentData, _T("<-- %s::IComponentData::QueryDispatch is returning hr=%s"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}


// -----------------------------------------------------------------------------
HRESULT CComponentData::AddMenuItems(LPDATAOBJECT pDataObject, LPCONTEXTMENUCALLBACK ipContextMenuCallback, long *pInsertionAllowed)
{
    DECLARE_SC(sc,_T("CComponentData::AddMenuItems"));
    Trace(tagBaseSnapinIExtendContextMenu, _T("--> %s::IExtendContextMenu::AddMenuItems(pDataObject=0x%08X), this=0x%08X"), StrSnapinClassName(), pDataObject, this);
    ADMIN_TRY;
    // By calling Pitem() at this time, we will force the creation of the ghost
    // root node, if necessary.
    Pitem(pDataObject);
    sc=Psnapin()->ScAddMenuItems(pDataObject, ipContextMenuCallback, pInsertionAllowed);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIExtendContextMenu, _T("<-- %s::IExtendContextMenu::AddMenuItems is returning hr=%s"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::Command(long nCommandID, LPDATAOBJECT pDataObject)
{
    DECLARE_SC(sc,_T("CComponentData::Command"));
    Trace(tagBaseSnapinIExtendContextMenu, _T("--> %s::IExtendContextMenu::Command(nCommandID=%ld, pDataObject=0x%08X), this=0x%08X"), StrSnapinClassName(), nCommandID, pDataObject, this);
    ADMIN_TRY;
    sc=ScCommand(nCommandID, pDataObject);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIExtendContextMenu, _T("<-- %s::IExtendContextMenu::Command is returning hr=%s"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::CreatePropertyPages(LPPROPERTYSHEETCALLBACK lpProvider, long handle, LPDATAOBJECT lpDataObject)
{
    DECLARE_SC(sc,_T("CComponentData::CreatePropertyPages"));
    Trace(tagBaseSnapinIExtendPropertySheet, _T("--> %s::IExtendPropertySheet::CreatePropertyPages(lpDataObject=0x%08X), this=0x%08X"), StrSnapinClassName(), lpDataObject, this);
    ADMIN_TRY;
    // Why are we ignoring E_UNEXPECTED ?
    // Because when we are called by the snapin manager, and the user hits cancel we
    // need to return E_UNEXPECTED to MMC.
    sc = ScCreatePropertyPages(lpProvider, handle, lpDataObject);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIExtendPropertySheet, _T("<-- %s::IExtendPropertySheet::CreatePropertyPages is returning hr=%s"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::QueryPagesFor(LPDATAOBJECT lpDataObject)
{
    DECLARE_SC(sc,_T("CComponentData::QueryPagesFor"));
    Trace(tagBaseSnapinIExtendPropertySheet, _T("--> %s::IExtendPropertySheet::QueryPagesFor(lpDataObject=0x%08X), this=0x%08X"), StrSnapinClassName(), lpDataObject, this);
    ADMIN_TRY;
    sc=ScQueryPagesFor(lpDataObject);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIExtendPropertySheet, _T("<-- %s::IExtendPropertySheet::QueryPagesFor is returning hr=%s"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::GetSizeMax(ULARGE_INTEGER *pcbSize)
{
    DECLARE_SC(sc,_T("CComponentData::GetSizeMax"));
    Trace(tagBaseSnapinIPersistStreamInit, _T("--> %s::IPersistStreamInit::GetSizeMax(...), , this=0x%08X"), StrSnapinClassName(), this);
    ADMIN_TRY;
    pcbSize->LowPart = cMaxStreamSizeLow;
    pcbSize->HighPart = cMaxStreamSizeHigh;
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIPersistStreamInit, _T("<-- %s::IPersistStreamInit::GetSizeMax is returning hr=%s, (*pcbSize).LowPart=%d"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()), (*pcbSize).LowPart);
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::IsDirty(void)
{
    DECLARE_SC(sc,_T("CComponentData::IsDirty"));
    Trace(tagBaseSnapinIPersistStreamInit, _T("--> %s::IPersistStreamInit::IsDirty(), this=0x%08X"), StrSnapinClassName(), this);
    ADMIN_TRY;
    sc=ScIsDirty();
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIPersistStreamInit, _T("<-- %s::IPersistStreamInit::IsDirty is returning hr=%s"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::Load(IStream *pstream)
{
    DECLARE_SC(sc,_T("CComponentData::Load"));
    Trace(tagBaseSnapinIPersistStreamInit, _T("--> %s::IPersistStreamInit::Load(...), this=0x%08X"), StrSnapinClassName(), this);
    ADMIN_TRY;
    sc=ScLoad(pstream);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIPersistStreamInit, _T("<-- %s::IPersistStreamInit::Load is returning hr=%s"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::Save(IStream *pstream, BOOL fClearDirty)
{
    DECLARE_SC(sc,_T("CComponentData::Save"));
    Trace(tagBaseSnapinIPersistStreamInit, _T("--> %s::IPersistStreamInit::Save(fClearDirty=%S), this=0x%08X"), StrSnapinClassName(), fClearDirty ? "TRUE" : "FALSE", this);
    ADMIN_TRY;
    sc=ScSave(pstream, fClearDirty);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIPersistStreamInit, _T("<-- %s::IPersistStreamInit::Save is returning hr=%s"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::InitNew(void)
{
    DECLARE_SC(sc,_T("CComponentData::InitNew"));
    // We don't have anything to do, but we still want to log the call.
    Trace(tagBaseSnapinIPersistStreamInit, _T("--> %s::IPersistStreamInit::InitNew(), this=0x%08X"), StrSnapinClassName(), this);
    Trace(tagBaseSnapinIPersistStreamInit, _T("<-- %s::IPersistStreamInit::InitNew is returning hr=%s"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::GetClassID(CLSID *pclsid)
{
    DECLARE_SC(sc,_T("CComponentData::GetClassID"));
    Trace(tagBaseSnapinIPersistStreamInit, _T("--> %s::IPersistStreamInit::GetClassID(...), this=0x%08X"), StrSnapinClassName(), this);
    ADMIN_TRY;
    *pclsid = *(Psnapin()->PclsidSnapin());
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIPersistStreamInit, _T("<-- %s::IPersistStreamInit::GetClassID is returning hr=%s"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CComponentData::Compare(RDCOMPARE * prdc, int * pnResult)
{
    DECLARE_SC(sc,_T("CComponentData::Compare"));
    Trace(tagBaseSnapinIResultDataCompare, _T("--> %s::IResultDataCompare::Compare(cookieA=0x%08X, cookieB=0x%08X), this=0x%08X"), StrSnapinClassName(), prdc->prdch1->cookie, prdc->prdch2->cookie, this);
    ADMIN_TRY;
    ASSERT(pnResult);
    ASSERT(prdc);
    ASSERT(prdc->prdch1);
    ASSERT(prdc->prdch2);
    sc=Psnapin()->ScCompare(prdc->prdch1->cookie, prdc->prdch2->cookie, prdc->nColumn, pnResult);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIResultDataCompare, _T("<-- %s::IResultDataCompare::Compare is returning hr=%s, *pnResult=%d"), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()), *pnResult);
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
// Returns the full path of the compiled file (.chm) for the snapin.
//
HRESULT CComponentData::GetHelpTopic(LPOLESTR *lpCompiledHelpFile)
{
    DECLARE_SC(sc,_T("CComponentData::GetHelpTopic"));
    tstring strCompiledHelpFile;
    USES_CONVERSION;

    Trace(tagBaseSnapinISnapinHelp, _T("--> %s::ISnapinHelp::GetHelpTopic(...), this=0x%08X"), StrSnapinClassName(), this);
    ADMIN_TRY;

    if (lpCompiledHelpFile == NULL)
    {
        sc = E_POINTER;
        goto Error;
    }

    // Automatically displays an error box.
    sc = Psnapin()->ScGetHelpTopic(strCompiledHelpFile);
    if (sc)
        goto Error;

    if (strCompiledHelpFile.empty())
    {
        sc=S_FALSE;
    }
    else
    {
        *lpCompiledHelpFile = reinterpret_cast<LPOLESTR>(CoTaskMemAlloc( (strCompiledHelpFile.length()+1)*sizeof(TCHAR)) );
        sc = ScCheckPointers(*lpCompiledHelpFile, E_OUTOFMEMORY);
        if (sc)
            goto Error;

        wcscpy(*lpCompiledHelpFile, T2CW(strCompiledHelpFile.data()) );
    }

    if (sc)
        goto Error;             // an exception was caught

Cleanup:
    Trace(tagBaseSnapinISnapinHelp, _T("<-- %s::ISnapinHelp::GetHelpTopic is returning hr=%s, lpCompiledHelpFile=\"%s\""), StrSnapinClassName(), SzGetDebugNameOfHr(sc.ToHr()), strCompiledHelpFile.data());
    return(sc.ToHr());
Error:
    TraceError(_T("CComponentData::GetHelpTopic"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// Creates a data object of the appropriate type, and returns the IDataObject interface
// on it
//
SC CComponentData::ScQueryDataObject(long cookie, DATA_OBJECT_TYPES type, LPDATAOBJECT* ppDataObject)
{
    SC                              sc = S_OK;
    CBaseSnapinItem *pitem = NULL;

    // The component data can handle cookie types of CCT_SNAPIN_MANAGER and CCT_SCOPE.
    // CCT_RESULT are handled by CComponent.
    ASSERT(type==CCT_SNAPIN_MANAGER || type==CCT_SCOPE);

    //
    // If the cookie does not correspond to a known object, return E_UNEXPECTED.
    // This is correct and is also a workaround for an MMC bug. See X5:74405.
    //
    if (cookie && (Psnapin()->Pcookielist()->find(cookie) == Psnapin()->Pcookielist()->end() ) )
    {
        sc = E_UNEXPECTED;
        goto Cleanup;
    }

    pitem = Pitem(NULL, 0, cookie);
    ASSERT(pitem);

    sc = pitem->ScQueryDataObject(cookie, type, ppDataObject);
    if (sc)
        goto Error;

Cleanup:
    return sc;
Error:
    TraceError(_T("CComponentData::ScQueryDataObject"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// Handles the MMCN_EXPAND notification sent to IComponentData::Notify
//
SC CComponentData::ScOnExpand(LPDATAOBJECT lpDataObject, BOOL fExpand, HSCOPEITEM hscopeitem)
{
    SC              sc = S_OK;
    CBaseSnapinItem *pitem = NULL;

    if (fExpand == FALSE)                                    // do nothing on a "contract"
        goto Cleanup;

    pitem = Pitem(lpDataObject, hscopeitem);
    ASSERT(pitem);

    // Use this opportunity to correlate the CSnapinItem and the HSCOPEITEM.
    // $REVIEW (ptousig) Should be done inside Pitem().
    pitem->SetHscopeitem(hscopeitem);
    pitem->SetComponentData(this);

    if (pitem->PitemChild())
    {
        // We have a list of partial children.  We need to remove
        // them because we are going to ask the snapin to enumerate
        // all of it's children. Do not get rid of this node.
        // This can happen if a node creates new children before it is
        // expanded.
        //
        // $REVIEW (ptousig) Creating a child when the parent is not expanded
        //                                       should simply not add the child.
        //
        sc = pitem->ScDeleteSubTree(FALSE);
        if (sc)
            goto Error;
    }

    // If we're creating the children, make sure there aren't any around.
    ASSERT(pitem->PitemChild() == NULL);

    sc = pitem->ScCreateChildren();
    if (sc)
        goto Error;

    if (pitem->PitemChild())
    {
        // Add or remove children to/from the console.
        sc = pitem->PitemChild()->ScInsertScopeItem(this, fExpand, hscopeitem);
        if (sc)
            goto Error;
    }

    pitem->SetWasExpanded(TRUE);

Cleanup:
    return sc;
Error:
    TraceError(_T("CComponentData::ScOnExpand"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// Called by the MMC to delete the cookies for the entire subtree under a node. This
// is only to clean up allocated objects. Do NOT call IConsoleNameSpace::DeleteItem.
//
SC CComponentData::ScOnRemoveChildren(LPDATAOBJECT lpDataObject, HSCOPEITEM hscopeitem)
{
    SC sc = S_OK;
    CBaseSnapinItem *pitem = NULL;

    pitem = Pitem(lpDataObject, hscopeitem);
    ASSERT(pitem);

    // Get rid of the children.
    sc = pitem->ScDeleteSubTree(FALSE);
    if (sc)
        goto Error;

    // Release the given node if it is one of the root nodes
    sc = Psnapin()->ScReleaseIfRootItem(pitem);
    if (sc)
        goto Error;

Cleanup:
    return sc;
Error:
    TraceError(_T("CComponentData::ScOnRemoveChildren"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// Allows a snapin to control whether it will populate the scope pane
// in the background or not.
//
SC CComponentData::ScOnExpandSync(LPDATAOBJECT lpDataObject, MMC_EXPANDSYNC_STRUCT *pmes)
{
    // We don't care.
    return S_OK;
}

// -----------------------------------------------------------------------------
// The user has asked to delete this node.
//
SC CComponentData::ScOnDelete(LPDATAOBJECT pDataObject)
{
    SC                               sc                             = S_OK;
    CBaseSnapinItem *pitem                  = NULL;
    BOOL                     fDeleted               = FALSE;
    BOOL                     fPagesUp               = FALSE;
    tstring                  strMsg;

    pitem = Pitem(pDataObject);
    ASSERT(pitem);

    // The ComponentData should only receive notifications for scope pane items.
    ASSERT(pitem->FIsContainer());

    sc = pitem->ScIsPropertySheetOpen(&fPagesUp);
    if (sc)
        goto Error;

    if (fPagesUp)
    {
        ASSERT(FALSE && "Add below resource");
        //strMsg.LoadString(_Module.GetResourceInstance(), idsPropsUpNoDelete);
        strMsg += (*pitem->PstrDisplayName());
        MMCErrorBox(strMsg.data());
        goto Cleanup;
    }

    // Ask the item to delete the underlying object.
    sc = pitem->ScOnDelete(&fDeleted);
    if (sc)
        goto Error;

    if (fDeleted == FALSE)
        // The item did not want to be deleted.
        goto Cleanup;

    // Container items need to be deleted from the document
    // Delete the item and everything below it.
    sc = IpConsoleNameSpace()->DeleteItem(pitem->Hscopeitem(), TRUE);
    if (sc)
        goto Error;
    pitem->SetHscopeitem(0);

    // At this point, the item exists only in the tree, if at all.
    // Remove it from the tree.
    pitem->Unlink();
    // Get rid of it for good from the tree of items.
    pitem->Pdataobject()->Release();

Cleanup:
    return sc;
Error:
    TraceError(_T("CComponentData::ScOnDelete"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// Toolbar button clicked.
//
SC CComponentData::ScOnButtonClick(LPDATAOBJECT lpDataObject, MMC_CONSOLE_VERB mcvVerb)
{
    return S_OK;
}

// -----------------------------------------------------------------------------
// Used to insert a new item into the tree of items, and, if the item is a container,
// into the namespace as well. The tree and the namespace are document, not view,
// concept. This insertion is done only once.
//
SC CComponentData::ScOnDocumentChangeInsertItem(CBaseSnapinItem *pitemNew)
{
    SC                                      sc                              = S_OK;
    CBaseSnapinItem *       pitemParent             = NULL;

    ASSERT(pitemNew);

    // Insert exactly once.
    if (pitemNew->FInserted())
        goto Cleanup;

    // The parent should have already been filled in
    pitemParent = pitemNew->PitemParent();
    ASSERT(pitemParent);

    if (pitemParent->FIncludesChild(pitemNew) == FALSE)
    {
        sc = pitemParent->ScAddChild(pitemNew);
        if (sc)
            goto Error;
    }

    if (pitemNew->FIsContainer())
    {
        if (pitemParent->FWasExpanded())
        {
            sc = pitemNew->ScInsertScopeItem(this, TRUE, pitemParent->Hscopeitem());
            if (sc)
                goto Error;
        }
    }

    // Only insert the item once.
    pitemNew->SetInserted(TRUE);

Cleanup:
    return sc;
Error:
    TraceError(_T("CComponentData::ScOnDocumentChangeInsertItem"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// Tests whether the given item has already enumerated its children.
//
SC CComponentData::ScWasExpandedOnce(CBaseSnapinItem *pitem, BOOL *pfWasExpanded)
{
    SC sc = S_OK;
    SCOPEDATAITEM sdi;

    if (pitem->Hscopeitem() == 0)
    {
        //
        // If we don't have an HSCOPEITEM then we are not displayed in
        // the scope pane, therefore we have never been expanded (and
        // probably never will).
        //
        *pfWasExpanded = FALSE;
        goto Cleanup;
    }

    //
    // Ask for the state member of SCOPEDATAITEM
    //
    ::ZeroMemory(&sdi, sizeof(SCOPEDATAITEM));
    sdi.mask = SDI_STATE;
    sdi.ID = pitem->Hscopeitem();

    sc = IpConsoleNameSpace()->GetItem(&sdi);
    if (sc)
        goto Error;

    //
    // If the MMC_SCOPE_ITEM_STATE_EXPANDEDONCE is on it means we have
    // been asked to expand ourselves at least once before.
    //
    *pfWasExpanded = (sdi.nState & MMC_SCOPE_ITEM_STATE_EXPANDEDONCE) != 0;

Cleanup:
    return sc;

Error:
    TraceError(_T("CComponentData::ScWasExpandedOnce"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// Adds property pages for the given data object, taking into account the context and also
// whether the data object is for a new object that is being created.
//
SC CComponentData::ScCreatePropertyPages(LPPROPERTYSHEETCALLBACK ipPropertySheetCallback, long handle, LPDATAOBJECT pDataObject)
{
    SC              sc = S_OK;
    CBaseSnapinItem *pitem = NULL;

    pitem = Pitem(pDataObject);
    ASSERT(pitem);

    if (pitem->FIsSnapinManager())
    {
        sc = pitem->ScCreateSnapinMgrPropertyPages(ipPropertySheetCallback);
        if (sc)
            goto Error;
    }
    else
    {
        // Simple version
        sc = pitem->ScCreatePropertyPages(ipPropertySheetCallback);
        if (sc)
            goto Error;

        // Complete version - for snapins that need all information (like Recipients)
        sc = pitem->ScCreatePropertyPages(ipPropertySheetCallback, handle);
        if (sc)
            goto Error;
    }

Cleanup:
    return sc;
Error:
    TraceError(_T("CComponentData::ScCreatePropertyPages"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// Save information from the first root item.
//
// $REVIEW (ptousig) Why does the root item implement ScSave() ?
//                                       If a snapin extends two nodes it has two root items, yet
//                                       only one of which will be saved/loaded.
//
SC CComponentData::ScSave(IStream *pstream, BOOL fClearDirty)
{
    SC sc = S_OK;

    // Save the snapin's serialized information.
    sc = Psnapin()->ScSave(pstream, fClearDirty);
    if (sc)
        goto Error;

    // Load the root item's serialized information.
    // Only makes sense for standalone snapins.
    sc = Pitem()->ScSave(pstream, fClearDirty);
    if (sc)
        goto Error;

Cleanup:
    return sc;
Error:
    TraceError(_T("CComponentData::ScSave"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// Given a dataobject, determines whether or not pages exist.
// Actually, need to return S_OK here in both cases. If no pages exist
// it is CreatePropertyPages that should return S_FALSE. Sad but true.
//
SC CComponentData::ScQueryPagesFor(LPDATAOBJECT pDataObject)
{
    SC                              sc                      = S_OK;
    CBaseSnapinItem *       pitem           = NULL;

    pitem = Pitem(pDataObject);
    ASSERT(pitem);

    sc = pitem->ScQueryPagesFor();
    if (sc)
        goto Error;

Cleanup:
    return sc;
Error:
    TraceError(_T("CComponentData::ScQueryPagesFor"), sc);
    goto Cleanup;
}