//____________________________________________________________________________
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1996 - 1999
//
//  File:       MTNode.cpp
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    9/17/1996   RaviR   Created
//____________________________________________________________________________
//


#include "stdafx.h"
#include "nodemgr.h"
#include "comdbg.h"
#include "regutil.h"
#include "bitmap.h"
#include "dummysi.h"
#include "tasks.h"
#include "policy.h"
#include "bookmark.h"
#include "nodepath.h"
#include "siprop.h"
#include "util.h"
#include "addsnpin.h"
#include "about.h"
#include "nodemgrdebug.h"

extern const CLSID CLSID_FolderSnapin;
extern const CLSID CLSID_OCXSnapin;
extern const CLSID CLSID_HTMLSnapin;


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

// {118B559C-6D8C-11d0-B503-00C04FD9080A}
const GUID IID_PersistData =
{ 0x118b559c, 0x6d8c, 0x11d0, { 0xb5, 0x3, 0x0, 0xc0, 0x4f, 0xd9, 0x8, 0xa } };

//############################################################################
//############################################################################
//
//  Implementation of class CStorage
//
//############################################################################
//############################################################################

/*+-------------------------------------------------------------------------*
 * class CStorage
 *
 *
 * PURPOSE: Wrapper for IStorage. Provides several utility functions.
 *
 *+-------------------------------------------------------------------------*/
class CStorage
{
    IStoragePtr m_spStorage;

public:
    CStorage() {}

    CStorage(IStorage *pStorage)
    {
        m_spStorage = pStorage;
    }

    CStorage & operator = (const CStorage &rhs)
    {
        m_spStorage = rhs.m_spStorage;
        return *this;
    }

    void Attach(IStorage *pStorage)
    {
        m_spStorage = pStorage;
    }

    IStorage *Get()
    {
        return m_spStorage;
    }

    // create this storage below the specified storage
    SC  ScCreate(CStorage &storageParent, const wchar_t* name, DWORD grfMode, const wchar_t* instanceName)
    {
        SC sc;
        sc = CreateDebugStorage(storageParent.Get(), name, grfMode, instanceName, &m_spStorage);
        return sc;
    }

    SC  ScMoveElementTo(const wchar_t *name, CStorage &storageDest, const wchar_t *newName, DWORD grfFlags)
    {
        SC sc;
        if(!Get() || ! storageDest.Get())
            goto PointerError;

        sc = m_spStorage->MoveElementTo(name, storageDest.Get(), newName, grfFlags);
        // error STG_E_FILENOTFOUND must be treated differently, since it is expected
        // to occur and means the end of move operation (loop) in ScConvertLegacyNode.
        // Do not trace in this case.
        if(sc == SC(STG_E_FILENOTFOUND))
            goto Cleanup;
        if(sc)
            goto Error;

        Cleanup:
            return sc;
        PointerError:
            sc = E_POINTER;
        Error:
            TraceError(TEXT("CStorage::ScMoveElementTo"), sc);
            goto Cleanup;
    }

};

//############################################################################
//############################################################################
//
//  Implementation of class CStream
//
//############################################################################
//############################################################################
/*+-------------------------------------------------------------------------*
 * class CStream
 *
 *
 * PURPOSE: Wrapper for IStream. Provides several utility functions.
 *
 *+-------------------------------------------------------------------------*/
class CStream
{
    IStreamPtr m_spStream;
    typedef IStream *PSTREAM;

public:
    CStream() {}

    CStream(IStream *pStream)
    {
        m_spStream = pStream;
    }

    CStream & operator = (const CStream &rhs)
    {
        m_spStream = rhs.m_spStream;
        return *this;
    }

    void Attach(IStream *pStream)
    {
        m_spStream = pStream;
    }

    IStream *Get()
    {
        return m_spStream;
    }

    operator IStream&()
    {
        return *m_spStream;
    }

    // create this stream below the specified storage
    SC ScCreate(CStorage& storageParent, const wchar_t* name, DWORD grfMode, const wchar_t* instanceName)
    {
        SC sc;
        sc = CreateDebugStream(storageParent.Get(), name, grfMode, instanceName, &m_spStream);
        return sc;
    }


    /*+-------------------------------------------------------------------------*
     *
     * ScRead
     *
     * PURPOSE: Reads the specified object from the stream.
     *
     * PARAMETERS:
     *    void *  pv :      The location of the object.
     *    size_t  size :    The size of the object.
     *
     * RETURNS:
     *    SC
     *
     *+-------------------------------------------------------------------------*/
    SC  ScRead(void *pv, size_t size, bool bIgnoreErrors = false)
    {
        DECLARE_SC(sc, TEXT("CStream::ScRead"));

        // parameter check
        sc = ScCheckPointers(pv);
        if (sc)
            return sc;

        // internal pointer check
        sc = ScCheckPointers(m_spStream, E_POINTER);
        if (sc)
            return sc;

        // read the data
        ULONG bytesRead = 0;
        sc = m_spStream->Read(pv, size, &bytesRead);

        // if we need to ignore errors, just return.
        if(bIgnoreErrors)
            return sc.Clear(), sc;

        if (sc)
            return sc;

        // since this function does not return the number of bytes read,
        // failure to read as may as requested should be treated as error
        if (sc == SC(S_FALSE) || bytesRead != size)
            return sc = E_FAIL;

        return sc;
    }

    /*+-------------------------------------------------------------------------*
     *
     * ScWrite
     *
     * PURPOSE: Writes the specified object to the stream
     *
     * PARAMETERS:
     *    const   void :
     *    size_t  size :
     *
     * RETURNS:
     *    SC
     *
     *+-------------------------------------------------------------------------*/
    SC  ScWrite(const void *pv, size_t size)
    {
        DECLARE_SC(sc, TEXT("CStream::ScWrite"));

        // parameter check
        sc = ScCheckPointers(pv);
        if (sc)
            return sc;

        // internal pointer check
        sc = ScCheckPointers(m_spStream, E_POINTER);
        if (sc)
            return sc;

        // write the data

        ULONG   bytesWritten = 0;
        sc = m_spStream->Write(pv, size, &bytesWritten);
        if (sc)
            return sc;

        // since this function does not return the number of bytes written,
        // failure to write as may as requested should be treated as error
        if (bytesWritten != size)
            return sc = E_FAIL;

        return sc;
    }
};



/////////////////////////////////////////////////////////////////////////////
// Forward declaration of helper functions defined below

SC  ScLoadBitmap (CStream &stream, HBITMAP* pBitmap);
void PersistBitmap (CPersistor &persistor, LPCTSTR name, HBITMAP& hBitmap);

static inline SC ScWriteEmptyNode(CStream &stream)
{
    SC          sc;
    int         nt = 0;

    sc = stream.ScWrite(&nt, sizeof(nt));
    if(sc)
        goto Error;

Cleanup:
    return sc;
Error:
    TraceError(TEXT("ScWriteEmptyNode"), sc);
    goto Cleanup;
}

static inline CLIPFORMAT GetPreLoadFormat (void)
{
    static CLIPFORMAT s_cfPreLoads = 0;
    if (s_cfPreLoads == 0) {
        USES_CONVERSION;
        s_cfPreLoads = (CLIPFORMAT) RegisterClipboardFormat (W2T(CCF_SNAPIN_PRELOADS));
    }
    return s_cfPreLoads;
}

//############################################################################
//############################################################################
//
//  Implementation of class CMTNode
//
//############################################################################
//############################################################################
DEBUG_DECLARE_INSTANCE_COUNTER(CMTNode);

// Static member
MTNODEID CMTNode::m_NextID = ROOTNODEID;


CMTNode::CMTNode()
: m_ID(GetNextID()), m_pNext(NULL), m_pChild(NULL), m_pParent(NULL),
  m_bIsDirty(true), m_cRef(1), m_usFlags(0), m_bLoaded(false),
  m_bookmark(NULL)
{
    DEBUG_INCREMENT_INSTANCE_COUNTER(CMTNode);
	Reset();
    m_nImage = eStockImage_Folder;
    m_nOpenImage = eStockImage_OpenFolder;
    m_nState = 0;
}


void CMTNode::Reset()
{
	m_idOwner               = TVOWNED_MAGICWORD;
	m_lUserParam            = 0;
	m_pPrimaryComponentData = NULL;
	m_bInit                 = false;
	m_bExtensionsExpanded   = false;
	m_usExpandFlags         = 0;

    ResetExpandedAtLeastOnce();
}


CMTNode::~CMTNode()
{
    DEBUG_DECREMENT_INSTANCE_COUNTER(CMTNode);
    DECLARE_SC(sc, TEXT("CMTNode::~CMTNode"));

    if (IsPropertyPageDisplayed() == TRUE)
        MMCIsMTNodeValid(this, TRUE);

    ASSERT(m_pNext == NULL);
    ASSERT(m_pParent == NULL);
    ASSERT(m_cRef == 0);

    CScopeTree *pScopeTree = CScopeTree::GetScopeTree();
    sc = ScCheckPointers(pScopeTree, E_UNEXPECTED);
    if (!sc)
    {
        sc = pScopeTree->ScUnadviseMTNode(this);
    }

    if (m_pChild != NULL)
    {
        // Don't recurse the siblings of the child.
        CMTNode* pMTNodeCurr = m_pChild;
        while (pMTNodeCurr)
        {
            m_pChild = pMTNodeCurr->Next();
            pMTNodeCurr->AttachNext(NULL);
            pMTNodeCurr->AttachParent(NULL);
            pMTNodeCurr->Release();
            pMTNodeCurr = m_pChild;
        }

        m_pChild = NULL;
    }

    // DON'T CHANGE THE ORDER OF THESE NULL ASSIGNMENTS!!!!!!!!!
    m_spTreeStream = NULL;
    m_spViewStorage = NULL;
    m_spCDStorage = NULL;
    m_spNodeStorage = NULL;
    m_spPersistData = NULL;

    if (m_pParent != NULL)
    {
        if (m_pParent->m_pChild == this)
        {
            m_pParent->m_pChild = NULL;
            if (GetStaticParent() == this)
                m_pParent->SetDirty();
        }
    }
}

// Was MMCN_REMOVE_CHILDREN sent to the snapin owning this node or its parent
bool CMTNode::AreChildrenBeingRemoved ()
{
    if (_IsFlagSet(FLAG_REMOVING_CHILDREN))
        return true;

    if (Parent())
        return Parent()->AreChildrenBeingRemoved ();

    return false;
}

CMTNode* CMTNode::FromScopeItem (HSCOPEITEM item)
{
    CMTNode* pMTNode = reinterpret_cast<CMTNode*>(item);

    try
    {
        pMTNode = dynamic_cast<CMTNode*>(pMTNode);
    }
    catch (...)
    {
        pMTNode = NULL;
    }

    return (pMTNode);
}

/*+-------------------------------------------------------------------------*
 * class CMMCSnapIn
 *
 *
 * PURPOSE: The COM 0bject that exposes the SnapIn interface.
 *
 *+-------------------------------------------------------------------------*/
class CMMCSnapIn :
    public CMMCIDispatchImpl<SnapIn>, // the View interface
    public CTiedComObject<CMTSnapInNode>
{
    typedef CMTSnapInNode CMyTiedObject;
    typedef std::auto_ptr<CSnapinAbout> SnapinAboutPtr;

public:
    BEGIN_MMC_COM_MAP(CMMCSnapIn)
    END_MMC_COM_MAP()

public:
    MMC_METHOD1(get_Name,       PBSTR      /*pbstrName*/);
    STDMETHOD(get_Vendor)( PBSTR pbstrVendor );
    STDMETHOD(get_Version)( PBSTR pbstrVersion );
    MMC_METHOD1(get_Extensions, PPEXTENSIONS  /*ppExtensions*/);
    MMC_METHOD1(get_SnapinCLSID,PBSTR      /*pbstrSnapinCLSID*/);
    MMC_METHOD1(get_Properties, PPPROPERTIES /*ppProperties*/);
    MMC_METHOD1(EnableAllExtensions, BOOL    /*bEnable*/);

    // not an interface method,
    // just a convenient way to reach for tied object's method
    MMC_METHOD1(GetSnapinClsid, CLSID& /*clsid*/);

    CMTSnapInNode *GetMTSnapInNode();

private:
    ::SC ScGetSnapinAbout(CSnapinAbout*& pAbout);

private:
    SnapinAboutPtr m_spSnapinAbout;
};


/*+-------------------------------------------------------------------------*
 * class CExtension
 *
 *
 * PURPOSE: The COM 0bject that exposes the SnapIn interface.
 *
 *          This extension is not tied to any object. An extension snapin instance
 *          can be uniquely identified by combination of its class-id & its primary
 *          snapin's class-id. So this object just stores this data.
 *          See addsnpin.h for more comments.
 *
 *+-------------------------------------------------------------------------*/
class CExtension :
    public CMMCIDispatchImpl<Extension>
{
    typedef std::auto_ptr<CSnapinAbout> SnapinAboutPtr;

public:
    BEGIN_MMC_COM_MAP(CExtension)
    END_MMC_COM_MAP()

public:
    STDMETHODIMP get_Name( PBSTR  pbstrName);
    STDMETHODIMP get_Vendor( PBSTR  pbstrVendor);
    STDMETHODIMP get_Version( PBSTR  pbstrVersion);
    STDMETHODIMP get_Extensions( PPEXTENSIONS ppExtensions);
    STDMETHODIMP get_SnapinCLSID( PBSTR  pbstrSnapinCLSID);
    STDMETHODIMP EnableAllExtensions(BOOL bEnable);
    STDMETHODIMP Enable(BOOL bEnable = TRUE);

    CExtension() : m_clsidAbout(GUID_NULL) {}

    void Init(const CLSID& clsidExtendingSnapin, const CLSID& clsidThisExtension, const CLSID& clsidAbout)
    {
        m_clsidExtendingSnapin = clsidExtendingSnapin;
        m_clsidThisExtension   = clsidThisExtension;
        m_clsidAbout           = clsidAbout;
    }

    LPCOLESTR GetVersion()
    {
        CSnapinAbout *pSnapinAbout = GetSnapinAbout();
        if (! pSnapinAbout)
            return NULL;

        return pSnapinAbout->GetVersion();
    }

    LPCOLESTR GetVendor()
    {
        CSnapinAbout *pSnapinAbout = GetSnapinAbout();
        if (! pSnapinAbout)
            return NULL;

        return pSnapinAbout->GetCompanyName();
    }

private:
    CSnapinAbout* GetSnapinAbout()
    {
        // If about object is already created just return it.
        if (m_spExtensionAbout.get())
            return m_spExtensionAbout.get();

        if (m_clsidAbout == GUID_NULL)
            return NULL;

        // Else create & initialize the about object.
        m_spExtensionAbout = SnapinAboutPtr (new CSnapinAbout);
        if (! m_spExtensionAbout.get())
            return NULL;

        if (m_spExtensionAbout->GetSnapinInformation(m_clsidAbout))
            return m_spExtensionAbout.get();

        return NULL;
    }

private:
    CLSID         m_clsidThisExtension;
    CLSID         m_clsidExtendingSnapin;

    CLSID         m_clsidAbout;

    SnapinAboutPtr m_spExtensionAbout;
};


//############################################################################
//############################################################################
//
//  Implementation of class CExtensions
//
//############################################################################
//############################################################################
/*+-------------------------------------------------------------------------*
 * class CExtensions
 *
 *
 * PURPOSE: Implements the Extensions automation interface.
 *
 * The Scget_Extensions uses this class as a template parameter to the typedef
 * below. The typedef is an array of Extension objects, that needs atleast below
 * empty class declared. Scget_Extensions adds the extensions to the array.
 *
 *    typedef CComObject< CMMCArrayEnum<Extensions, Extension> > CMMCExtensions;
 *
 *+-------------------------------------------------------------------------*/
class CExtensions :
    public CMMCIDispatchImpl<Extensions>,
    public CTiedObject                     // enumerators are tied to it
{
protected:
    typedef void CMyTiedObject;
};


// Helper functions used by both CMMCSnapIn as well as CExtension.
SC Scget_Extensions(const CLSID& clsidPrimarySnapin, PPEXTENSIONS  ppExtensions);
SC ScEnableAllExtensions (const CLSID& clsidPrimarySnapin, BOOL bEnable);


//+-------------------------------------------------------------------
//
//  Member:      Scget_Extensions
//
//  Synopsis:    Helper function, given class-id of primary creates &
//               returns the extensions collection for this snapin.
//
//  Arguments:   [clsidPrimarySnapin] -
//               [ppExtensions]       - out param, extensions collection.
//
//  Returns:     SC
//
//  Note:        Collection does not include dynamic extensions.
//
//--------------------------------------------------------------------
SC Scget_Extensions(const CLSID& clsidPrimarySnapin, PPEXTENSIONS  ppExtensions)
{
    DECLARE_SC(sc, TEXT("Scget_Extensions"));
    sc = ScCheckPointers(ppExtensions);
    if (sc)
        return sc;

    *ppExtensions = NULL;

    // Create the extensions collection (which also implements the enumerator).
    typedef CComObject< CMMCArrayEnum<Extensions, Extension> > CMMCExtensions;
    CMMCExtensions *pMMCExtensions = NULL;
    sc = CMMCExtensions::CreateInstance(&pMMCExtensions);
    if (sc)
        return sc;

    sc = ScCheckPointers(pMMCExtensions, E_UNEXPECTED);
    if (sc)
        return sc;

    typedef CComPtr<Extension> CMMCExtensionPtr;
    typedef std::vector<CMMCExtensionPtr> ExtensionSnapins;
    ExtensionSnapins extensions;

    // Now get the extensions for this collection from this snapin.
    CExtensionsCache extnsCache;
    sc = MMCGetExtensionsForSnapIn(clsidPrimarySnapin, extnsCache);
    if (sc)
        return sc;

    // Create Extension object for each non-dynamic extension.
    CExtensionsCacheIterator it(extnsCache);

    for (; it.IsEnd() == FALSE; it.Advance())
    {
        // Collection does not include dynamic extensions.
        if (CExtSI::EXT_TYPE_DYNAMIC & it.GetValue())
            continue;

        typedef CComObject<CExtension> CMMCExtensionSnap;
        CMMCExtensionSnap *pExtension = NULL;

        sc = CMMCExtensionSnap::CreateInstance(&pExtension);
        if (sc)
            return sc;

        sc = ScCheckPointers(pExtension, E_UNEXPECTED);
        if (sc)
            return sc;

        CLSID clsidAbout;
        sc = ScGetAboutFromSnapinCLSID(it.GetKey(), clsidAbout);
        if (sc)
            sc.TraceAndClear();

        // Make the Extension aware of its primary snapin & about object.
        pExtension->Init(clsidPrimarySnapin, it.GetKey(), clsidAbout);

        extensions.push_back(pExtension);
    }

    // Fill this data into the extensions collection.
    pMMCExtensions->Init(extensions.begin(), extensions.end());

    sc = pMMCExtensions->QueryInterface(ppExtensions);
    if (sc)
        return sc;

    return (sc);
}


//+-------------------------------------------------------------------
//
//  Member:      ScEnableAllExtensions
//
//  Synopsis:    Helper function, given class-id of primary enables
//               all extensions or un-checks the enable all so that
//               individual extension can be disabled.
//
//  Arguments:   [clsidPrimarySnapin] -
//               [bEnable]            - enable or disable.
//
//  Returns:     SC
//
//  Note:        Collection does not include dynamic extensions.
//
//--------------------------------------------------------------------
SC ScEnableAllExtensions (const CLSID& clsidPrimarySnapin, BOOL bEnable)
{
    DECLARE_SC(sc, _T("ScEnableAllExtensions"));

    // Create snapin manager.
    CScopeTree *pScopeTree = CScopeTree::GetScopeTree();
    sc = ScCheckPointers(pScopeTree, E_UNEXPECTED);
    if (sc)
        return sc.ToHr();

    CSnapinManager snapinMgr(pScopeTree->GetRoot());

    // Ask the snapinMgr to enable/disable its extensions.
    sc = snapinMgr.ScEnableAllExtensions(clsidPrimarySnapin, bEnable);
    if (sc)
        return sc.ToHr();

    // Update the scope tree with changes made by snapin manager.
    sc = pScopeTree->ScAddOrRemoveSnapIns(snapinMgr.GetDeletedNodesList(),
                                          snapinMgr.GetNewNodes());
    if (sc)
        return sc.ToHr();

    return (sc);
}


//+-------------------------------------------------------------------
//
//  Member:      CExtension::get_Name
//
//  Synopsis:    Return the name of this extension.
//
//  Arguments:
//
//  Returns:     SC
//
//--------------------------------------------------------------------
STDMETHODIMP CExtension::get_Name (PBSTR  pbstrName)
{
    DECLARE_SC(sc, _T("CExtension::get_Name"));
    sc = ScCheckPointers(pbstrName);
    if (sc)
        return sc.ToHr();

    *pbstrName = NULL;

    tstring tszSnapinName;
    bool bRet = GetSnapinNameFromCLSID(m_clsidThisExtension, tszSnapinName);
    if (!bRet)
        return (sc = E_FAIL).ToHr();

    USES_CONVERSION;
    *pbstrName = SysAllocString(T2COLE(tszSnapinName.data()));
    if ( (! *pbstrName) && (tszSnapinName.length() > 0) )
        return (sc = E_OUTOFMEMORY).ToHr();

    return sc.ToHr();
}


//+-------------------------------------------------------------------
//
//  Member:      CExtension::get_Vendor
//
//  Synopsis:    Get the vendor information for this extension if it exists.
//
//  Arguments:   [pbstrVendor] - out param, ptr to vendor info.
//
//  Returns:     HRESULT
//
//--------------------------------------------------------------------
STDMETHODIMP CExtension::get_Vendor (PBSTR  pbstrVendor)
{
    DECLARE_SC(sc, _T("CExtension::get_Vendor"));
    sc = ScCheckPointers(pbstrVendor);
    if (sc)
        return sc.ToHr();

    LPCOLESTR lpszVendor = GetVendor();

    *pbstrVendor = SysAllocString(lpszVendor);
    if ((lpszVendor) && (! *pbstrVendor))
        return (sc = E_OUTOFMEMORY).ToHr();

    return (sc.ToHr());
}


//+-------------------------------------------------------------------
//
//  Member:      CExtension::get_Version
//
//  Synopsis:    Get the version info for this extension if it exists.
//
//  Arguments:   [pbstrVersion] - out param, ptr to version info.
//
//  Returns:     HRESULT
//
//--------------------------------------------------------------------
STDMETHODIMP CExtension::get_Version (PBSTR  pbstrVersion)
{
    DECLARE_SC(sc, _T("CExtension::get_Version"));
    sc = ScCheckPointers(pbstrVersion);
    if (sc)
        return sc.ToHr();

    LPCOLESTR lpszVersion = GetVersion();

    *pbstrVersion = SysAllocString(lpszVersion);
    if ((lpszVersion) && (! *pbstrVersion))
        return (sc = E_OUTOFMEMORY).ToHr();

    return (sc.ToHr());
}

//+-------------------------------------------------------------------
//
//  Member:      CExtension::get_SnapinCLSID
//
//  Synopsis:    Get the extension snapin class-id.
//
//  Arguments:   [pbstrSnapinCLSID] - out param, snapin class-id.
//
//  Returns:     HRESULT
//
//--------------------------------------------------------------------
STDMETHODIMP CExtension::get_SnapinCLSID (PBSTR  pbstrSnapinCLSID)
{
    DECLARE_SC(sc, _T("CExtension::get_SnapinCLSID"));
    sc = ScCheckPointers(pbstrSnapinCLSID);
    if (sc)
        return sc.ToHr();

    CCoTaskMemPtr<OLECHAR> szSnapinClsid;

    sc = StringFromCLSID(m_clsidThisExtension, &szSnapinClsid);
    if (sc)
        return sc.ToHr();

    *pbstrSnapinCLSID = SysAllocString(szSnapinClsid);
    if (! *pbstrSnapinCLSID)
        sc = E_OUTOFMEMORY;

    return (sc.ToHr());
}


//+-------------------------------------------------------------------
//
//  Member:      CExtension::ScEnable
//
//  Synopsis:    Enable or disable this extension
//
//  Arguments:   [bEnable] - enable or disable.
//
//  Returns:     SC
//
//--------------------------------------------------------------------
STDMETHODIMP CExtension::Enable (BOOL bEnable /*= TRUE*/)
{
    DECLARE_SC(sc, _T("CExtension::ScEnable"));

    /*
     * 1. Create snapin manager.
     * 2. Ask snapin mgr to disable this snapin.
     */

    // Create snapin manager.
    CScopeTree *pScopeTree = CScopeTree::GetScopeTree();
    sc = ScCheckPointers(pScopeTree, E_UNEXPECTED);
    if (sc)
        return sc.ToHr();

    CSnapinManager snapinMgr(pScopeTree->GetRoot());

    // Ask the snapinMgr to disable this extension.
    sc = snapinMgr.ScEnableExtension(m_clsidExtendingSnapin, m_clsidThisExtension, bEnable);
    if (sc)
        return sc.ToHr();

    // Update the scope tree with changes made by snapin manager.
    sc = pScopeTree->ScAddOrRemoveSnapIns(snapinMgr.GetDeletedNodesList(),
                                          snapinMgr.GetNewNodes());
    if (sc)
        return sc.ToHr();

    return sc.ToHr();
}


//+-------------------------------------------------------------------
//
//  Member:      CExtension::Scget_Extensions
//
//  Synopsis:    Get the extensions collection for this snapin.
//
//  Arguments:   [ppExtensions] - out ptr to extensions collection.
//
//  Returns:     SC
//
//--------------------------------------------------------------------
HRESULT CExtension::get_Extensions( PPEXTENSIONS  ppExtensions)
{
    DECLARE_SC(sc, _T("CExtension::get_Extensions"));
    sc = ScCheckPointers(ppExtensions);
    if (sc)
        return sc.ToHr();

    *ppExtensions = NULL;

    sc = ::Scget_Extensions(m_clsidThisExtension, ppExtensions);
    if (sc)
        return sc.ToHr();

    return (sc.ToHr());
}


//+-------------------------------------------------------------------
//
//  Member:      CExtension::EnableAllExtensions
//
//  Synopsis:    Enable/Disable all the extensions of this snapin.
//
//  Arguments:
//
//  Returns:     HRESULT
//
//--------------------------------------------------------------------
STDMETHODIMP CExtension::EnableAllExtensions(BOOL bEnable)
{
    DECLARE_SC(sc, TEXT("CExtension::EnableAllExtensions"));

    sc = ::ScEnableAllExtensions(m_clsidThisExtension, bEnable);
    if (sc)
        return sc.ToHr();

    return sc.ToHr();
}

//+-------------------------------------------------------------------
//
//  Member:      CMTSnapInNode::ScGetCMTSnapinNode
//
//  Synopsis:    Static function, given PSNAPIN (SnapIn interface)
//               return the CMTSnapInNode of that snapin.
//
//  Arguments:   [pSnapIn] - Snapin interface.
//
//  Returns:     SC
//
//--------------------------------------------------------------------
SC CMTSnapInNode::ScGetCMTSnapinNode(PSNAPIN pSnapIn, CMTSnapInNode **ppMTSnapInNode)
{
    DECLARE_SC(sc, _T("CMTSnapInNode::GetCMTSnapinNode"));
    sc = ScCheckPointers(pSnapIn, ppMTSnapInNode);
    if (sc)
        return sc;

    *ppMTSnapInNode = NULL;

    CMMCSnapIn *pMMCSnapIn = dynamic_cast<CMMCSnapIn*>(pSnapIn);
    if (!pMMCSnapIn)
        return (sc = E_UNEXPECTED);

    *ppMTSnapInNode = pMMCSnapIn->GetMTSnapInNode();

    return (sc);
}


//+-------------------------------------------------------------------
//
//  Member:      CMTSnapInNode::Scget_Name
//
//  Synopsis:    Return the name of this snapin.
//
//  Arguments:
//
//  Returns:     SC
//
//--------------------------------------------------------------------
SC CMTSnapInNode::Scget_Name (PBSTR pbstrName)
{
    DECLARE_SC(sc, _T("CMTSnapInNode::Scget_Name"));
    sc = ScCheckPointers(pbstrName);
    if (sc)
        return sc;

    *pbstrName = NULL;

    CSnapIn *pSnapin =  GetPrimarySnapIn();
    sc = ScCheckPointers(pSnapin, E_UNEXPECTED);
    if (sc)
        return sc;

    WTL::CString strSnapInName;
    sc = pSnapin->ScGetSnapInName(strSnapInName);
    if (sc)
        return sc;

    USES_CONVERSION;
    *pbstrName = strSnapInName.AllocSysString();
    if ( (! *pbstrName) && (strSnapInName.GetLength() > 0) )
        return (sc = E_OUTOFMEMORY);

    return (sc);
}



//+-------------------------------------------------------------------
//
//  Member:      CMTSnapInNode::Scget_Extensions
//
//  Synopsis:    Get the extensions collection for this snapin.
//
//  Arguments:   [ppExtensions] - out ptr to extensions collection.
//
//  Returns:     SC
//
//--------------------------------------------------------------------
SC CMTSnapInNode::Scget_Extensions( PPEXTENSIONS  ppExtensions)
{
    DECLARE_SC(sc, _T("CMTSnapInNode::Scget_Extensions"));
    sc = ScCheckPointers(ppExtensions);
    if (sc)
        return sc;

    *ppExtensions = NULL;

    CSnapIn *pSnapin =  GetPrimarySnapIn();
    sc = ScCheckPointers(pSnapin, E_UNEXPECTED);
    if (sc)
        return sc;

    sc = ::Scget_Extensions(pSnapin->GetSnapInCLSID(), ppExtensions);
    if (sc)
        return sc;

    return (sc);
}

//+-------------------------------------------------------------------
//
//  Member:      CMTSnapInNode::ScGetSnapinClsid
//
//  Synopsis:    Gets the CLSID of snapin
//
//  Arguments:   CLSID& clsid [out] - class id of snapin.
//
//  Returns:     SC
//
//--------------------------------------------------------------------
SC CMTSnapInNode::ScGetSnapinClsid(CLSID& clsid)
{
    DECLARE_SC(sc, TEXT("CMTSnapInNode::ScGetAboutClsid"));

    // init out param
    clsid = GUID_NULL;

    CSnapIn *pSnapin =  GetPrimarySnapIn();
    sc = ScCheckPointers(pSnapin, E_UNEXPECTED);
    if (sc)
        return sc;

    clsid = pSnapin->GetSnapInCLSID();

    return sc;
}

//+-------------------------------------------------------------------
//
//  Member:      CMTSnapInNode::Scget_SnapinCLSID
//
//  Synopsis:    Get the CLSID for this snapin.
//
//  Arguments:   [pbstrSnapinCLSID] - out ptr to CLSID.
//
//  Returns:     SC
//
//--------------------------------------------------------------------
SC CMTSnapInNode::Scget_SnapinCLSID(     PBSTR      pbstrSnapinCLSID)
{
    DECLARE_SC(sc, TEXT("CMTSnapInNode::Scget_SnapinCLSID"));
    sc = ScCheckPointers(pbstrSnapinCLSID);
    if (sc)
        return sc;

    CSnapIn *pSnapin =  GetPrimarySnapIn();
    sc = ScCheckPointers(pSnapin, E_UNEXPECTED);
    if (sc)
        return sc;

    CCoTaskMemPtr<OLECHAR> szSnapinClsid;

    sc = StringFromCLSID(pSnapin->GetSnapInCLSID(), &szSnapinClsid);
    if (sc)
        return sc.ToHr();

    *pbstrSnapinCLSID = SysAllocString(szSnapinClsid);
    if (! *pbstrSnapinCLSID)
        sc = E_OUTOFMEMORY;

    return sc;
}


//+-------------------------------------------------------------------
//
//  Member:      CMTSnapInNode::ScEnableAllExtensions
//
//  Synopsis:    Enable or not enable all extensions of this snapin.
//
//  Arguments:
//
//  Returns:     SC
//
//--------------------------------------------------------------------
SC CMTSnapInNode::ScEnableAllExtensions (BOOL bEnable)
{
    DECLARE_SC(sc, _T("CMTSnapInNode::ScEnableAllExtensions"));

    CSnapIn *pSnapin =  GetPrimarySnapIn();
    sc = ScCheckPointers(pSnapin, E_UNEXPECTED);
    if (sc)
        return sc;

    sc = ::ScEnableAllExtensions(pSnapin->GetSnapInCLSID(), bEnable);
    if (sc)
        return sc;

    return (sc);
}


/*+-------------------------------------------------------------------------*
 * CMTSnapInNode::Scget_Properties
 *
 * Returns a pointer to the snap-in's Properties object
 *--------------------------------------------------------------------------*/

SC CMTSnapInNode::Scget_Properties( PPPROPERTIES ppProperties)
{
    DECLARE_SC (sc, _T("CMTSnapInNode::Scget_Properties"));

    /*
     * validate parameters
     */
    sc = ScCheckPointers (ppProperties);
    if (sc)
        return (sc);

    *ppProperties = m_spProps;

    /*
     * If the snap-in doesn't support ISnapinProperties, don't return
     * a Properties interface.  This is not an error, but rather a valid
     * unsuccessful return, so we return E_NOINTERFACE directly instead
     * of assigning to sc first.
     */
    if (m_spProps == NULL)
        return (E_NOINTERFACE);

    /*
     * put a ref on for the client
     */
    (*ppProperties)->AddRef();

    return (sc);
}



/*+-------------------------------------------------------------------------*
 *
 * CMTSnapInNode::ScGetSnapIn
 *
 * PURPOSE: Returns a pointer to the SnapIn object.
 *
 * PARAMETERS:
 *    PPSNAPIN  ppSnapIn :
 *
 * RETURNS:
 *    SC
 *
 *+-------------------------------------------------------------------------*/
SC
CMTSnapInNode::ScGetSnapIn(PPSNAPIN ppSnapIn)
{
    DECLARE_SC(sc, TEXT("CMTSnapInNode::ScGetSnapIn"));

    sc = ScCheckPointers(ppSnapIn);
    if(sc)
        return sc;

    // initialize out parameter
    *ppSnapIn = NULL;

    // create a CMMCView if needed.
    sc = CTiedComObjectCreator<CMMCSnapIn>::ScCreateAndConnect(*this, m_spSnapIn);
    if(sc)
        return sc;

    if(m_spSnapIn == NULL)
    {
        sc = E_UNEXPECTED;
        return sc;
    }

    // addref the pointer for the client.
    m_spSnapIn->AddRef();
    *ppSnapIn = m_spSnapIn;

    return sc;
}


HRESULT CMTNode::OpenStorageForNode()
{
    if (m_spNodeStorage != NULL)
        return S_OK;

    ASSERT(m_spPersistData != NULL);
    if (m_spPersistData == NULL)
        return E_POINTER;

    // Get the storage for all of the nodes
    IStorage* const pAllNodes = m_spPersistData->GetNodeStorage();
    ASSERT(pAllNodes != NULL);
    if (pAllNodes == NULL)
        return E_POINTER;

    // Create the outer storage for this node
    WCHAR name[MAX_PATH];
    HRESULT hr = OpenDebugStorage(pAllNodes, GetStorageName(name),
        STGM_READWRITE | STGM_SHARE_EXCLUSIVE, L"\\node\\#", &m_spNodeStorage);
    return hr == S_OK ? S_OK : E_FAIL;
}

HRESULT CMTNode::OpenStorageForView()
{
    if (m_spViewStorage != NULL)
        return S_OK;

    // Get the storage for all of the nodes
    IStorage* const pNodeStorage = GetNodeStorage();
    ASSERT(pNodeStorage != NULL);
    if (pNodeStorage == NULL)
        return E_FAIL;

    // Create the outer storage for this node
    WCHAR name[MAX_PATH];
    HRESULT hr = OpenDebugStorage(pNodeStorage, L"view",
                        STGM_READWRITE | STGM_SHARE_EXCLUSIVE, L"\\node\\#\\view",
                                                     &m_spViewStorage);
    return hr == S_OK ? S_OK : E_FAIL;
}

HRESULT CMTNode::OpenStorageForCD()
{
    if (m_spCDStorage != NULL)
        return S_OK;

    // Get the storage for all of the nodes
    IStorage* const pNodeStorage = GetNodeStorage();
    ASSERT(pNodeStorage != NULL);
    if (pNodeStorage == NULL)
        return E_FAIL;

    // Create the outer storage for this node
    WCHAR name[MAX_PATH];
    HRESULT hr = OpenDebugStorage(pNodeStorage, L"data",
                        STGM_READWRITE | STGM_SHARE_EXCLUSIVE, L"\\node\\#\\data",
                                                     &m_spCDStorage);
    return hr == S_OK ? S_OK : E_FAIL;
}

HRESULT CMTNode::OpenTreeStream()
{
    if (m_spTreeStream != NULL)
    {
        const LARGE_INTEGER loc = {0,0};
        ULARGE_INTEGER newLoc;
        HRESULT hr = m_spTreeStream->Seek(loc, STREAM_SEEK_SET, &newLoc);
        ASSERT(SUCCEEDED(hr));
        if (FAILED(hr))
            return E_FAIL;

        return S_OK;
    }

    HRESULT hr = OpenStorageForNode();
    ASSERT(SUCCEEDED(hr));
    if (FAILED(hr))
        return E_FAIL;

    hr = OpenStorageForView();
    ASSERT(SUCCEEDED(hr));
    if (FAILED(hr))
        return E_FAIL;

    hr = OpenStorageForCD();
    ASSERT(SUCCEEDED(hr));
    if (FAILED(hr))
        return E_FAIL;

    IStorage* const pTreeNodes = GetNodeStorage();
    ASSERT(pTreeNodes != NULL);
    if (pTreeNodes == NULL)
        return E_POINTER;

    hr = OpenDebugStream(pTreeNodes, L"tree",
                STGM_READWRITE | STGM_SHARE_EXCLUSIVE, L"\\node\\#\\tree", &m_spTreeStream);
    ASSERT(SUCCEEDED(hr) && m_spTreeStream != NULL);
    return SUCCEEDED(hr) ? S_OK : E_FAIL;
}

/*+-------------------------------------------------------------------------*
 *
 * CMTSnapInNode::NextStaticNode
 *
 * PURPOSE:
 *
 * PARAMETERS:
 *
 * RETURNS:   NULL if not found, else the next CMTSnapInNode.
 *    inline
 *
 * NOTE: This performance is poor! Improve by indexing all CMTSnapInNodes
 *       separately.
 *+-------------------------------------------------------------------------*/
CMTNode*
CMTNode::NextStaticNode()
{
    CMTNode *pNext = this;

    while (pNext)
    {
        if (pNext->IsStaticNode())
            return pNext;
        pNext = pNext->Next();
    }
    return NULL;
}




HRESULT CMTNode::IsDirty()
{
    if (GetDirty())
    {
        TraceDirtyFlag(TEXT("CMTNode"), true);
        return S_OK;
    }

    HRESULT hr;
    CMTNode* const pChild = m_pChild->NextStaticNode();
    if (pChild)
    {
        hr = pChild->IsDirty();
        ASSERT(SUCCEEDED(hr));
        if (FAILED(hr))
            return hr;
        if (hr != S_FALSE)
        {
            TraceDirtyFlag(TEXT("CMTNode"), true);
            return hr;
        }
    }

    CMTNode* const pNext = m_pNext->NextStaticNode();
    if (pNext)
    {
        hr = pNext->IsDirty();
        ASSERT(SUCCEEDED(hr));
        if (FAILED(hr))
            return hr;
        if (hr != S_FALSE)
        {
            TraceDirtyFlag(TEXT("CMTNode"), true);
            return hr;
        }
    }

    TraceDirtyFlag(TEXT("CMTNode"), false);
    return S_FALSE;
}


/*+-------------------------------------------------------------------------*
 *
 * CMTNode::InitNew
 *
 * PURPOSE:
 *
 * PARAMETERS:
 *    PersistData* d :
 *
 * RETURNS:
 *    HRESULT
 *
 *+-------------------------------------------------------------------------*/
HRESULT
CMTNode::InitNew(PersistData* d)
{
    SC      sc;
    CStream treeStream;

    if ( (m_spPersistData != NULL) || (d==NULL) || !IsStaticNode())
        goto FailedError;

    m_spPersistData = d;
    if (m_spPersistData == NULL)
        goto ArgumentError;

    sc = InitNew();
    if(sc)
        goto Error;

    // Get the stream for persistence of the tree

    treeStream.Attach( m_spPersistData->GetTreeStream());

    // recurse thru children
    {
        CMTNode* const pChild = m_pChild->NextStaticNode();
        if (pChild)
        {
            sc = pChild->InitNew(d);
            if(sc)
                goto Error;
        }
        else
        {
            sc = ScWriteEmptyNode(treeStream);
            if(sc)
                goto Error;
        }
    }

    // chain to next node.
    {
        CMTNode* const pNext = m_pNext->NextStaticNode();
        if (pNext)
        {
            sc = pNext->InitNew(d);
            if(sc)
                goto Error;
        }
        else
        {
            sc = ScWriteEmptyNode(treeStream);
            if(sc)
                goto Error;
        }
    }

Cleanup:
    return HrFromSc(sc);
FailedError:
    sc = E_FAIL;
    goto Error;
ArgumentError:
    sc = E_INVALIDARG;
Error:
    TraceError(TEXT("CMTNode::InitNew"), sc);
    goto Cleanup;

}

/*+-------------------------------------------------------------------------*
 *
 * CMTNode::Persist
 *
 * PURPOSE: Persists the CMTNode to the specified persistor.
 *
 * PARAMETERS:
 *    CPersistor& persistor :
 *
 * RETURNS:
 *    void
 *
 *+-------------------------------------------------------------------------*/
void CMTNode::Persist(CPersistor& persistor)
{
    MTNODEID id = GetID();       // persist the node id
    persistor.PersistAttribute(XML_ATTR_MT_NODE_ID, id);
    SetID(id);

    // Save the children
    CPersistor persistorSubNode(persistor, XML_TAG_SCOPE_TREE_NODES);
    if (persistor.IsStoring())
    {
        CMTNode* pChild = m_pChild->NextStaticNode();
        while (pChild)
        {
            persistorSubNode.Persist(*pChild);
            // get next node
            pChild = pChild->Next();
            // advance if it is not a static node
            pChild = (pChild ? pChild->NextStaticNode() : NULL);
        }
        ClearDirty();
    }
    else
    {
        XMLListCollectionBase::Persist(persistorSubNode);
    }

    UINT nImage = m_nImage;
    if (nImage > eStockImage_Max)       // if SnapIn changed icon dynamically, then
        nImage = eStockImage_Folder;    // this value will be bogus next time:
                                            // replace w/ 0 (closed folder)
    persistor.PersistAttribute(XML_ATTR_MT_NODE_IMAGE, nImage);
    persistor.PersistString(XML_ATTR_MT_NODE_NAME,  m_strName);
}

/*+-------------------------------------------------------------------------*
 *
 * CMTNode::OnNewElement
 *
 * PURPOSE: called for each new child node found in XML doc
 *
 * PARAMETERS:
 *    CPersistor& persistor :
 *
 * RETURNS:
 *    void
 *
 *+-------------------------------------------------------------------------*/
void CMTNode::OnNewElement(CPersistor& persistor)
{
    DECLARE_SC(sc, TEXT("CMTNode::OnNewElement"));

    // load the child
    CMTNode* pChild;
    // attach to the list
    PersistNewNode(persistor, &pChild);
    if (pChild)
    {
        pChild->SetParent(this);
        CMTNode** ppLast = &m_pChild;
        while (*ppLast) ppLast = &(*ppLast)->m_pNext;
        *ppLast = pChild;
    }
}


/*+-------------------------------------------------------------------------*
 *
 * CMTNode::ScLoad
 *
 * PURPOSE: Loads the MTNode from the specified stream.
 *          COMPATIBILITY issues: MMC1.0 through MMC1.2 used special built-in
 *          node types to represent Folder, Web Link, and ActiveX control nodes.
 *          MMC2.0 and higher use snap-ins instead. The only special node is
 *          Console Root, which is still saved and loaded as a Folder node with
 *          ID = 1.
 *
 *
 * RETURNS:
 *    SC
 *
 *+-------------------------------------------------------------------------*/
SC CMTNode::ScLoad(PersistData* d, CMTNode** ppNode)
{
    DECLARE_SC(sc, TEXT("CMTNode::ScLoad"));
    CMTSnapInNode* pmtSnapInNode = NULL;
    CStream        treeStream;

    // check parameters
    sc = ScCheckPointers(d, ppNode);
    if(sc)
        return sc;

    *ppNode = NULL;

    // Read the type of node from the stream.
    treeStream.Attach(d->GetTreeStream());

    int nt;
    sc = treeStream.ScRead(&nt, sizeof(nt));
    if(sc)
        return sc;

    if (!nt)
        return sc;

    if (!(nt == NODE_CODE_SNAPIN || nt == NODE_CODE_FOLDER ||
          nt == NODE_CODE_HTML   || nt == NODE_CODE_OCX))
        return (sc = E_FAIL); // invalid node type.

    // Read the storage key
    MTNODEID id;
    sc = treeStream.ScRead(&id, sizeof(id));
    if(sc)
        return sc;

    // Create a node of the appropriate type. Everything, including Console Root
    // uses CMTSnapInNode.
    if( nt == NODE_CODE_FOLDER || nt == NODE_CODE_SNAPIN || nt == NODE_CODE_HTML || nt == NODE_CODE_OCX )
    {
        pmtSnapInNode = new CMTSnapInNode (NULL);

        ASSERT(pmtSnapInNode != NULL);
        if (pmtSnapInNode == NULL)
            return E_POINTER;

        *ppNode = pmtSnapInNode;
    }
    else
        return (sc = E_UNEXPECTED); // should never happen

    (*ppNode)->m_bLoaded = true;

    ASSERT((*ppNode)->m_spPersistData == NULL);
    ASSERT(d != NULL);
    (*ppNode)->m_spPersistData = d;
    ASSERT((*ppNode)->m_spPersistData != NULL);
    if ((*ppNode)->m_spPersistData == NULL)
        return E_INVALIDARG;


    (*ppNode)->SetID(id);
    if (id >= m_NextID)
        m_NextID = id+1;

    // Open the stream for the nodes data
    sc = (*ppNode)->OpenTreeStream();
    if (sc)
    {
        (*ppNode)->Release();
        *ppNode = NULL;
        return sc;
    }

    // Load the node
    // If old style node, then convert to snap-in type node

    switch (nt)
    {
    case NODE_CODE_SNAPIN:
        sc = (*ppNode)->ScLoad();
        break;

    // All folder nodes, INCLUDING old-style console root nodes, are upgraded to snap-ins.
    case NODE_CODE_FOLDER:
            if(pmtSnapInNode == NULL)
                return (sc = E_UNEXPECTED);

            sc = pmtSnapInNode->ScConvertLegacyNode(CLSID_FolderSnapin);
            break;
    case NODE_CODE_HTML:
        sc = pmtSnapInNode->ScConvertLegacyNode(CLSID_HTMLSnapin);
        break;

    case NODE_CODE_OCX:
        sc = pmtSnapInNode->ScConvertLegacyNode(CLSID_OCXSnapin);
        break;

    default:
        ASSERT(0 && "Invalid node type");
        sc = E_FAIL;
    }

    if (sc)
    {
        (*ppNode)->Release();
        *ppNode = NULL;
        return sc;
    }

    // load the children
    CMTNode* pChild;
    sc = ScLoad(d, &pChild);
    if (sc)
    {
        (*ppNode)->Release();
        *ppNode = NULL;
        return sc;
    }
    if (pChild)
        pChild->SetParent(*ppNode);
    (*ppNode)->m_pChild = pChild;

    // Load siblings
    CMTNode* pNext;
    sc = ScLoad(d, &(*ppNode)->m_pNext);
    if (sc)
    {
        (*ppNode)->Release();
        *ppNode = NULL;
        return sc;
    }

    (*ppNode)->SetDirty(false);

    return sc;
}

/*+-------------------------------------------------------------------------*
 *
 * CMTNode::PersistNewNode
 *
 * PURPOSE: Loads the MTNode from the persistor.
 *
 *+-------------------------------------------------------------------------*/
void CMTNode::PersistNewNode(CPersistor &persistor, CMTNode** ppNode)
{
    DECLARE_SC(sc, TEXT("CMTNode::PersistNewNode"));

    CMTSnapInNode* pmtSnapInNode = NULL;

    const int CONSOLE_ROOT_ID = 1;
    // check parameters
    sc = ScCheckPointers(ppNode);
    if (sc)
        sc.Throw();

    *ppNode = NULL;

    // Create a node of the snapin type. Everything uses CMTSnapInNode.

    pmtSnapInNode = new CMTSnapInNode(NULL);
    sc = ScCheckPointers(pmtSnapInNode,E_OUTOFMEMORY);
    if (sc)
        sc.Throw();

    *ppNode = pmtSnapInNode;

    (*ppNode)->m_bLoaded = true;

    ASSERT((*ppNode)->m_spPersistData == NULL);

    try
    {
        persistor.Persist(**ppNode);
    }
    catch(...)
    {
        // ensure cleanup here
        (*ppNode)->Release();
        *ppNode = NULL;
        throw;
    }
    // update index for new nodes
    MTNODEID id = (*ppNode)->GetID();
    if (id >= m_NextID)
        m_NextID = id+1;

    (*ppNode)->SetDirty(false);
}

HRESULT CMTNode::DestroyElements()
{
    if (!IsStaticNode())
        return S_OK;

    HRESULT hr;

    if (m_pChild != NULL)
    {
        hr = m_pChild->DestroyElements();
        ASSERT(SUCCEEDED(hr));
        if (FAILED(hr))
            return hr;
    }

    return DoDestroyElements();
}

HRESULT CMTNode::DoDestroyElements()
{
    if (m_spPersistData == NULL)
        return S_OK;

    IStorage* const pNodeStorage = m_spPersistData->GetNodeStorage();
    ASSERT(pNodeStorage != NULL);
    if (pNodeStorage == NULL)
        return S_OK;

    WCHAR name[MAX_PATH];
    HRESULT hr = pNodeStorage->DestroyElement(GetStorageName(name));

    SetDirty();
    CMTNode* const psParent = m_pParent != NULL ? m_pParent->GetStaticParent() : NULL;
    if (psParent != NULL)
        psParent->SetDirty();

    return S_OK;
}

void CMTNode::SetParent(CMTNode* pParent)
{
    m_pParent = pParent;
    if (m_pNext)
        m_pNext->SetParent(pParent);
}


HRESULT CMTNode::CloseView(int idView)
{
    if (!IsStaticNode())
        return S_OK;

    HRESULT hr;
    CMTNode* const pChild = m_pChild->NextStaticNode();
    if (pChild)
    {
        hr = pChild->CloseView(idView);
        ASSERT(SUCCEEDED(hr));
        if (FAILED(hr))
            return E_FAIL;
    }

    CMTNode* const pNext = m_pNext->NextStaticNode();
    if (pNext)
    {
        hr = pNext->CloseView(idView);
        ASSERT(SUCCEEDED(hr));
        if (FAILED(hr))
            return E_FAIL;
    }

    return S_OK;
}


HRESULT CMTNode::DeleteView(int idView)
{
    if (!IsStaticNode())
        return S_OK;

    HRESULT hr;
    CMTNode* const pChild = m_pChild->NextStaticNode();
    if (pChild)
    {
        hr = pChild->DeleteView(idView);
        ASSERT(SUCCEEDED(hr));
        if (FAILED(hr))
            return E_FAIL;
    }

    CMTNode* const pNext = m_pNext->NextStaticNode();
    if (pNext)
    {
        hr = pNext->DeleteView(idView);
        ASSERT(SUCCEEDED(hr));
        if (FAILED(hr))
            return E_FAIL;
    }

    return S_OK;
}

//+-------------------------------------------------------------------
//
//  Member:     GetBookmark
//
//  Synopsis:   Get bookmark for this MTNode.
//
//  Arguments:  None.
//
//  Returns:    auto pointer to CBookmark.
//
//  History:    04-23-1999   AnandhaG   Created
//
//--------------------------------------------------------------------
CBookmark* CMTNode::GetBookmark()
{
    DECLARE_SC(sc, TEXT("CMTNode::GetBookmark"));

    // If the bookmark is not created, create one.
    if (NULL == m_bookmark.get())
    {
        m_bookmark = std::auto_ptr<CBookmarkEx>(new CBookmarkEx);
        if (NULL == m_bookmark.get())
            return NULL;

        m_bookmark->Reset();

        SC sc = m_bookmark->ScInitialize(this, GetStaticParent(), false /*bFastRetrievalOnly*/);
        if(sc)
            sc.TraceAndClear(); // change
    }

    return m_bookmark.get();
}

void
CMTNode::SetCachedDisplayName(LPCTSTR pszName)
{
    if (m_strName.str() != pszName)
    {
        m_strName = pszName;
        SetDirty();

        if (Parent())
            Parent()->OnChildrenChanged();
    }
}

UINT
CMTNode::GetState(void)
{
   UINT nState = 0;
   if (WasExpandedAtLeastOnce())
   {
       nState |= MMC_SCOPE_ITEM_STATE_EXPANDEDONCE;
   }

   return nState;
}


/*+-------------------------------------------------------------------------*
 *
 * CMTNode::ScLoad
 *
 * PURPOSE: Loads the node from the tree stream
 *
 * RETURNS:
 *    SC
 *
 *+-------------------------------------------------------------------------*/
SC
CMTNode::ScLoad()
{
    ASSERT (IsStaticNode());
    SC      sc;
    CStream stream;

    stream.Attach(GetTreeStream());

    HRESULT hr;

    IStringTablePrivate* pStringTable = CScopeTree::GetStringTable();
    ASSERT (pStringTable != NULL);


    /*
     * read the "versioned stream" marker
     */
    StreamVersionIndicator nVersionMarker;
    sc = stream.ScRead(&nVersionMarker, sizeof(nVersionMarker));
    if(sc)
        goto Error;

    /*
     * Determine the stream version number.  If this is a versioned
     * stream, the version is the next DWORD in the stream, otherwise
     * it must be it's a version 1 stream
     */
    StreamVersionIndicator nVersion;

    if (nVersionMarker == VersionedStreamMarker)
    {
        sc = stream.ScRead(&nVersion, sizeof(nVersion));
        if(sc)
            goto Error;
    }
    else
        nVersion = Stream_V0100;


    switch (nVersion)
    {
        /*
         * MMC 1.0 stream
         */
        case Stream_V0100:
        {
            /*
             * Version 1 streams didn't have a version marker; they began with
             * the image index as the first DWORD.  The first DWORD has
             * already been read (version marker), so we can recycle that
             * value for the image index.
             */
            m_nImage = nVersionMarker;

            /*
             * Continue reading with the display name (length then characters)
             */
            unsigned int stringLength = 0;
            sc = stream.ScRead(&stringLength, sizeof(stringLength));
            if(sc)
                goto Error;

            if (stringLength)
            {
                wchar_t* str = reinterpret_cast<wchar_t*>(alloca((stringLength+1)*2));
                ASSERT(str != NULL);
                if (str == NULL)
                    return E_POINTER;
                sc = stream.ScRead(str, stringLength*2);
                if(sc)
                    goto Error;

                str[stringLength] = 0;

                USES_CONVERSION;
                m_strName = W2T (str);
            }

            break;
        }

        /*
         * MMC 1.1 stream
         */
        case Stream_V0110:
        {
            /*
             * read the image index
             */
            sc = stream.ScRead(&m_nImage, sizeof(m_nImage));
            if(sc)
                goto Error;

            /*
             * read the name (stream insertion operators will throw
             * _com_error's, so we need an exception block here)
             */
            try
            {
                IStream *pStream = stream.Get();
                if(!pStream)
                    goto PointerError;

                *pStream >> m_strName;
            }
            catch (_com_error& err)
            {
                hr = err.Error();
                ASSERT (false && "Caught _com_error");
                return (hr);
            }
            break;
        }

        default:
#ifdef DBG
            TCHAR szTraceMsg[80];
            wsprintf (szTraceMsg, _T("Unexpected stream version 0x08x\n"), nVersion);
            TRACE (szTraceMsg);
            ASSERT (FALSE);
#endif
            return (E_FAIL);
            break;
    }

Cleanup:
    return sc;
PointerError:
    sc = E_POINTER;
Error:
    TraceError(TEXT("CMTNode::Load"), sc);
    goto Cleanup;
}

HRESULT CMTNode::Init(void)
{
    DECLARE_SC(sc, TEXT("CMTNode::Init"));

    if (m_bInit == TRUE)
        return S_FALSE;

    ASSERT(WasExpandedAtLeastOnce() == FALSE);

    if (!m_pPrimaryComponentData)
        return E_FAIL;


    CMTSnapInNode* pMTSnapIn = GetStaticParent();
    HMTNODE hMTNode = CMTNode::ToHandle(pMTSnapIn);

    if (!m_pPrimaryComponentData->IsInitialized())
    {

        sc = m_pPrimaryComponentData->Init(hMTNode);
        if(sc)
            return sc.ToHr();

        sc = pMTSnapIn->ScInitIComponentData(m_pPrimaryComponentData);
        if (sc)
            return sc.ToHr();
    }

    // Init the extensions
    m_bInit = TRUE;

    BOOL fProblem = FALSE;

    // Get node's node-type
    GUID guidNodeType;
    sc = GetNodeType(&guidNodeType);
    if (sc)
        return sc.ToHr();


    CExtensionsIterator it;
    // TODO: try to use the easier form of it.ScInitialize()
    sc = it.ScInitialize(m_pPrimaryComponentData->GetSnapIn(), guidNodeType, g_szNameSpace,
                            m_arrayDynExtCLSID.GetData(), m_arrayDynExtCLSID.GetSize());
    if(sc)
        return sc.ToHr();
    else
    {
        CComponentData* pCCD = NULL;

        for (; it.IsEnd() == FALSE; it.Advance())
        {
            pCCD = pMTSnapIn->GetComponentData(it.GetCLSID());
            if (pCCD == NULL)
            {
                CSnapInPtr spSnapIn;

                // If a dynamic extension, we have to get the snap-in ourselves
                // otherwise the iterator has it
                if (it.IsDynamic())
                {
                    CSnapInsCache* const pCache = theApp.GetSnapInsCache();
                    ASSERT(pCache != NULL);

                    SC sc = pCache->ScGetSnapIn(it.GetCLSID(), &spSnapIn);
                    ASSERT(!sc.IsError());

                    // On failure, continue with other extensions
                    if (sc)
                        continue;
                }
                else
                {
                    spSnapIn = it.GetSnapIn();
                }

                ASSERT(spSnapIn != NULL);

                pCCD = new CComponentData(spSnapIn);
                pMTSnapIn->AddComponentDataToArray(pCCD);
            }

            ASSERT(pCCD != NULL);

            if (pCCD != NULL && pCCD->IsInitialized() == FALSE)
            {
                sc = pCCD->Init(hMTNode);

                if ( !sc.IsError() )
                    sc = pMTSnapIn->ScInitIComponentData(pCCD);

                if ( sc )
                {
                    sc.TraceAndClear();
                    fProblem = TRUE;
                }
            }
        }

        pMTSnapIn->CompressComponentDataArray();

    }

    if (fProblem == TRUE)
    {
        Dbg(DEB_TRACE, _T("Failed to load some extensions"));
    }

    return S_OK;
}

HRESULT CMTNode::Expand(void)
{
    DECLARE_SC(sc, TEXT("CMTNode::Expand"));

    CComponentData* pCCD = m_pPrimaryComponentData;
    if (WasExpandedAtLeastOnce() == FALSE)
        Init();

    SetExpandedAtLeastOnce();

    ASSERT(pCCD != NULL);
    if (pCCD == NULL)
        return E_FAIL;

    // Get the data object for the cookie from the owner snap-in
    IDataObjectPtr spDataObject;
    HRESULT hr = pCCD->QueryDataObject(GetUserParam(), CCT_SCOPE, &spDataObject);
    CHECK_HRESULT(hr);
    if (FAILED(hr))
        return hr;

//  hr = pCCD->Notify (spDataObject, MMCN_EXPAND, TRUE,
//                     reinterpret_cast<LPARAM>(this));
    hr = Expand (pCCD, spDataObject, TRUE);

    CHECK_HRESULT(hr);
    if (FAILED(hr))
        return hr;

    // Mark the folder for the master tree item as expanded
    CMTSnapInNode* pSIMTNode = GetStaticParent();

    //
    // Deal with extension snap-ins
    //

    m_bExtensionsExpanded = TRUE;

    // Get node's node-type
    GUID guidNodeType;
    hr = GetNodeType(&guidNodeType);
    if (FAILED(hr))
        return hr;

    CExtensionsIterator it;

    // TODO: try to use the easier form of it.ScInitialize()
    sc = it.ScInitialize(GetPrimarySnapIn(), guidNodeType, g_szNameSpace,
                    m_arrayDynExtCLSID.GetData(), m_arrayDynExtCLSID.GetSize());
    if (sc)
        return S_FALSE;     // The snapin is not loaded on the m/c.

    if (it.IsEnd())  // No extensions.
        return S_OK;

    BOOL fProblem = FALSE;

    for (; it.IsEnd() == FALSE; it.Advance())
    {
        CComponentData* pCCD = pSIMTNode->GetComponentData(it.GetCLSID());
        if (pCCD == NULL)
            continue;

//      hr = pCCD->Notify (spDataObject, MMCN_EXPAND, TRUE,
//                         reinterpret_cast<LPARAM>(this));
        hr = Expand (pCCD, spDataObject, TRUE);
        CHECK_HRESULT(hr);

        // continue even if an error occurs with extension snapins
        if (FAILED(hr))
            fProblem = TRUE;
    }

    return (fProblem == TRUE) ? S_FALSE : S_OK;
}


CNode* CMTNode::GetNode(CViewData* pViewData, BOOL fRootNode)
{
    CMTSnapInNode* pMTSnapInNode = GetStaticParent();
    if (pMTSnapInNode == NULL)
        return (NULL);

    if (fRootNode)
    {
        /*
         * create a static parent node for this non-static
         * root node (it will be deleted in the CNode dtor)
         */
        CNode* pNodeTemp = pMTSnapInNode->GetNode(pViewData, FALSE);
        if (pNodeTemp == NULL)
            return NULL;
    }

    CNode* pNode = new CNode(this, pViewData, fRootNode);

    if (pNode != NULL)
    {
        CComponent* pCC = pMTSnapInNode->GetComponent(pViewData->GetViewID(),
                                    GetPrimaryComponentID(), GetPrimarySnapIn());
        if (pCC==NULL)
        {
            delete pNode;
            return NULL;
        }
        else
            pNode->SetPrimaryComponent(pCC);
    }

    return pNode;
}

HRESULT CMTNode::AddExtension(LPCLSID lpclsid)
{
    DECLARE_SC(sc, TEXT("CMTNode::AddExtension"));
    sc = ScCheckPointers(lpclsid);
    if (sc)
        return sc.ToHr();

    CMTSnapInNode* pMTSnapIn = GetStaticParent();
    CSnapInsCache* const pCache = theApp.GetSnapInsCache();

    sc = ScCheckPointers(pMTSnapIn, pCache, E_UNEXPECTED);
    if (sc)
        return sc.ToHr();

    do // not a loop
    {
        // Get node's node-type
        GUID guidNodeType;
        sc = GetNodeType(&guidNodeType);
        if (sc)
            return sc.ToHr();

        // Must be a namespace extension
        if (!ExtendsNodeNameSpace(guidNodeType, lpclsid))
            return (sc = E_INVALIDARG).ToHr();

        // Check if extension is already enabled
        CExtensionsIterator it;
        // TODO: try to use the easier form of it.ScInitialize()
        sc = it.ScInitialize(GetPrimarySnapIn(), guidNodeType, g_szNameSpace,
                             m_arrayDynExtCLSID.GetData(), m_arrayDynExtCLSID.GetSize());
        for (; it.IsEnd() == FALSE; it.Advance())
        {
            if (IsEqualCLSID(*lpclsid, it.GetCLSID()))
                return (sc = S_FALSE).ToHr();
        }

        // Add extension to dynamic list
        m_arrayDynExtCLSID.Add(*lpclsid);

        // No errors returned if node is not initialized in MMC1.2.
        if (!m_bInit)
            break;

        HMTNODE hMTNode = CMTNode::ToHandle(pMTSnapIn);

        CSnapInPtr spSI;

        CComponentData* pCCD = pMTSnapIn->GetComponentData(*lpclsid);
        if (pCCD == NULL)
        {
            sc = pCache->ScGetSnapIn(*lpclsid, &spSI);
            if (sc)
                return sc.ToHr();

            pCCD = new CComponentData(spSI);
            sc = ScCheckPointers(pCCD, E_OUTOFMEMORY);
            if (sc)
                return sc.ToHr();

            pMTSnapIn->AddComponentDataToArray(pCCD);
        }

        sc = ScCheckPointers(pCCD, E_UNEXPECTED);
        if (sc)
            return sc.ToHr();

        if (pCCD->IsInitialized() == FALSE)
        {
            sc = pCCD->Init(hMTNode);

            if (sc)
            {
                // Init failed.
                pMTSnapIn->CompressComponentDataArray();
                return sc.ToHr();
            }
            else
            {
                // Above Init is successful.
                sc = pMTSnapIn->ScInitIComponentData(pCCD);
                sc.TraceAndClear(); // to maintain compatibility
            }
        }

        // Create and initialize a CComponent for all initialized nodes
        CNodeList& nodes = pMTSnapIn->GetNodeList();
        POSITION pos = nodes.GetHeadPosition();
        CNode* pNode = NULL;

        while (pos)
        {
            pNode = nodes.GetNext(pos);
            CSnapInNode* pSINode = dynamic_cast<CSnapInNode*>(pNode);
			sc = ScCheckPointers(pSINode, E_UNEXPECTED);
			if (sc)
            {
                sc.TraceAndClear();
                continue;
            }

            // Create component if hasn't been done yet
            CComponent* pCC = pSINode->GetComponent(pCCD->GetComponentID());
            if (pCC == NULL)
            {
                // Create and initialize one
                pCC = new CComponent(pCCD->GetSnapIn());

                sc = ScCheckPointers(pCC, E_OUTOFMEMORY);
                if (sc)
                    return sc.ToHr();

                pCC->SetComponentID(pCCD->GetComponentID());
                pSINode->AddComponentToArray(pCC);

                sc = pCC->Init(pCCD->GetIComponentData(), hMTNode, CNode::ToHandle(pNode),
                                 pCCD->GetComponentID(), pNode->GetViewID());

                sc.Trace_(); // Just trace for MMC1.2 compatibility.
            }
        }

        // if extensions are already expanded, expand the new one now
        if (AreExtensionsExpanded())
        {
            // Get the data object for the cookie from the owner snap-in
            IDataObjectPtr spDataObject;
            sc = GetPrimaryComponentData()->QueryDataObject(GetUserParam(), CCT_SCOPE, &spDataObject);
            if (sc)
                return sc.ToHr();

//              hr = pCCD->Notify (spDataObject, MMCN_EXPAND, TRUE,
//                                 reinterpret_cast<LPARAM>(this));
            sc = Expand (pCCD, spDataObject, TRUE);
            if (sc)
                sc.Trace_(); // Just trace for MMC1.2 compatibility.
        }
    }
    while(0);

    return sc.ToHr();
}


HRESULT CMTNode::IsExpandable()
{
    DECLARE_SC(sc, TEXT("CMTNode::IsExpandable"));

    // if already expanded, we know if there are children
    if (WasExpandedAtLeastOnce())
        return (Child() != NULL) ? S_OK : S_FALSE;

    // Even if not expanded there might be static children
    if (Child() != NULL)
        return S_OK;

    // if primary snap-in can add children, return TRUE
    // Note: When primary declares no children, it is also declaring
    // there will be no dynamic namespace extensions
    if (!(m_usExpandFlags & FLAG_NO_CHILDREN_FROM_PRIMARY))
        return S_OK;

    // Check enabled static extensions if haven't already
    if (!(m_usExpandFlags & FLAG_NAMESPACE_EXTNS_CHECKED))
    {
        m_usExpandFlags |= FLAG_NAMESPACE_EXTNS_CHECKED;

        do
        {
            // Do quick check for no extensions first
            if (GetPrimarySnapIn()->GetExtensionSnapIn() == NULL)
            {
                m_usExpandFlags |= FLAG_NO_NAMESPACE_EXTNS;
                break;
            }

            // Use iterator to find statically enabled namespace extens
            GUID guidNodeType;
            HRESULT hr = GetNodeType(&guidNodeType);
            ASSERT(SUCCEEDED(hr));
            if (FAILED(hr))
                break;

            CExtensionsIterator it;
            // TODO: try to use the easier form of it.ScInitialize()
            sc = it.ScInitialize(GetPrimarySnapIn(), guidNodeType, g_szNameSpace, NULL, 0);

            // if no extensions found, set the flag
            if (sc.IsError() || it.IsEnd())
                m_usExpandFlags |= FLAG_NO_NAMESPACE_EXTNS;
        }
        while (FALSE);
    }

    // if no namespace extensions, there will be no children
    if (m_usExpandFlags & FLAG_NO_NAMESPACE_EXTNS)
        return S_FALSE;

    return S_OK;
}


HRESULT CMTNode::Expand (
    CComponentData* pComponentData,
    IDataObject*    pDataObject,
    BOOL            bExpanding)
{
    HRESULT hr          = E_FAIL;
    bool    fSendExpand = true;

    if (CScopeTree::_IsSynchronousExpansionRequired())
    {
        MMC_EXPANDSYNC_STRUCT   ess;
        ess.bHandled   = FALSE;
        ess.bExpanding = bExpanding;
        ess.hItem      = reinterpret_cast<HSCOPEITEM>(this);

        hr = pComponentData->Notify (pDataObject, MMCN_EXPANDSYNC, 0,
                                     reinterpret_cast<LPARAM>(&ess));

        fSendExpand = !ess.bHandled;
    }

    if (fSendExpand)
    {
        hr = pComponentData->Notify (pDataObject, MMCN_EXPAND, bExpanding,
                                     reinterpret_cast<LPARAM>(this));
    }

    return (hr);
}

SC CMTNode::ScQueryDispatch(DATA_OBJECT_TYPES type,
                                      PPDISPATCH ppScopeNodeObject)
{
    DECLARE_SC(sc, _T("CMTNode::QueryDispatch"));
    sc = ScCheckPointers(ppScopeNodeObject);
    if (sc)
        return sc;

    *ppScopeNodeObject = NULL;

    CMTSnapInNode* pMTSINode = GetStaticParent();
    sc = ScCheckPointers(pMTSINode, E_UNEXPECTED);
    if (sc)
        return sc;

    CComponentData* pCCD = pMTSINode->GetComponentData(GetPrimarySnapInCLSID());
    sc = ScCheckPointers(pCCD, E_UNEXPECTED);
    if (sc)
        return sc;

    sc = pCCD->ScQueryDispatch(GetUserParam(), type, ppScopeNodeObject);

    return sc;
}


/*+-------------------------------------------------------------------------*
 * CMTNode::SetDisplayName
 *
 *
 *--------------------------------------------------------------------------*/

void CMTNode::SetDisplayName (LPCTSTR pszName)
{
    // This function should never be called as it does nothing. Display names
    DECLARE_SC(sc, TEXT("CMTNode::SetDisplayName"));

    if (pszName != (LPCTSTR) MMC_TEXTCALLBACK)
    {
        sc = E_INVALIDARG;
        TraceError(TEXT("The string should be MMC_TEXTCALLBACK"), sc);
        sc.Clear();
    }
}


/*+-------------------------------------------------------------------------*
 *
 * CMTNode::GetDisplayName
 *
 * PURPOSE: Returns the display name of the node.
 *
 * RETURNS:
 *    LPCTSTR
 *
 *+-------------------------------------------------------------------------*/
tstring
CMTNode::GetDisplayName()
{
    CComponentData* pCCD = GetPrimaryComponentData();
    if (pCCD)
    {
        SCOPEDATAITEM ScopeDataItem;
        ZeroMemory(&ScopeDataItem, sizeof(ScopeDataItem));
        ScopeDataItem.mask   = SDI_STR;
        ScopeDataItem.lParam = GetUserParam();

        HRESULT hr = pCCD->GetDisplayInfo(&ScopeDataItem);
        CHECK_HRESULT(hr);

        /*
         * if we succeeded, cache the name returned to us for
         * persistence
         */
        if (SUCCEEDED(hr))
        {
            USES_CONVERSION;
            if (ScopeDataItem.displayname)
                SetCachedDisplayName(OLE2T(ScopeDataItem.displayname));
            else
                SetCachedDisplayName(_T(""));
        }
    }

    return GetCachedDisplayName();
}

/***************************************************************************\
 *
 * METHOD:  CMTNode::ScGetPropertyFromINodeProperties
 *
 * PURPOSE: gets SnapIn property thru INodeProperties interface
 *
 * PARAMETERS:
 *    LPDATAOBJECT pDataObject  [in] - data object
 *    BSTR bstrPropertyName     [in] - property name
 *    PBSTR  pbstrPropertyValue [out] - property value
 *
 * RETURNS:
 *    SC    - result code
 *
\***************************************************************************/
SC CMTNode::ScGetPropertyFromINodeProperties(LPDATAOBJECT pDataObject, BSTR bstrPropertyName, PBSTR  pbstrPropertyValue)
{
    DECLARE_SC(sc, TEXT("CMTNode::ScGetPropertyFromINodeProperties"));

    SC sc_no_trace; // for 'valid' error - not to be traced

    // parameter check
    sc = ScCheckPointers(pDataObject, bstrPropertyName, pbstrPropertyValue);
    if(sc)
        return sc;

    // get the CComponentData
    CComponentData *pComponentData = GetPrimaryComponentData();
    sc = ScCheckPointers(pComponentData, E_UNEXPECTED);
    if(sc)
        return sc;

    // QI for INodeProperties from IComponentData
    INodePropertiesPtr spNodeProperties = pComponentData->GetIComponentData();

    // at this point we should have a valid interface if it is supported
    sc_no_trace = ScCheckPointers(spNodeProperties, E_NOINTERFACE);
    if(sc_no_trace)
        return sc_no_trace;

    // get the property
    sc_no_trace = spNodeProperties->GetProperty(pDataObject,  bstrPropertyName, pbstrPropertyValue);

    return sc_no_trace;
}

//############################################################################
//############################################################################
//
//  Implementation of class CComponentData
//
//############################################################################
//############################################################################


//____________________________________________________________________________
//
//  Class:      CComponentData Inlines
//____________________________________________________________________________
//

DEBUG_DECLARE_INSTANCE_COUNTER(CComponentData);

CComponentData::CComponentData(CSnapIn * pSnapIn)
    : m_spSnapIn(pSnapIn), m_ComponentID(-1), m_bIComponentDataInitialized(false)

{
    TRACE_CONSTRUCTOR(CComponentData);
    DEBUG_INCREMENT_INSTANCE_COUNTER(CComponentData);

    ASSERT(m_spSnapIn != NULL);
}

CComponentData::~CComponentData()
{
    TRACE_DESTRUCTOR(CComponentData);
    DEBUG_DECREMENT_INSTANCE_COUNTER(CComponentData);

    if (m_spIComponentData != NULL)
        m_spIComponentData->Destroy();
}

HRESULT CComponentData::Notify(LPDATAOBJECT lpDataObject, MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param)
{
    ASSERT(m_spIComponentData != NULL);
    if (m_spIComponentData == NULL)
        return E_FAIL;

    HRESULT hr = S_OK;
    __try
    {
        hr = m_spIComponentData->Notify(lpDataObject, event, arg, param);
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        hr = E_FAIL;
        if (m_spSnapIn)
            TraceSnapinException(m_spSnapIn->GetSnapInCLSID(), TEXT("IComponentData::Notify"), event);
    }

    return hr;
}


SC CComponentData::ScQueryDispatch(MMC_COOKIE cookie,
                                   DATA_OBJECT_TYPES type,
                                   PPDISPATCH ppScopeNodeObject)
{
    DECLARE_SC(sc, _T("CComponentData::ScQueryDispatch"));
    sc = ScCheckPointers(m_spIComponentData, E_UNEXPECTED);
    if (sc)
        return sc;

    IComponentData2Ptr spCompData2 = m_spIComponentData;
    sc = ScCheckPointers(spCompData2.GetInterfacePtr(), E_NOINTERFACE);
    if (sc)
        return sc;

    ASSERT(type != CCT_RESULT); // Cant Ask Disp for resultpane objects.
    sc = spCompData2->QueryDispatch(cookie, type, ppScopeNodeObject);

    return sc;
}



/*+-------------------------------------------------------------------------*
 *
 * CreateSnapIn
 *
 * PURPOSE: Create a name space snapin (standalone or extension).
 *
 * PARAMETERS:
 *    clsid                 - class id of the snapin to be created.
 *    ppICD                 - IComponentData ptr of created snapin.
 *    fCreateDummyOnFailure - Create dummy snapin if Create snapin fails.
 *
 * RETURNS:
 *    HRESULT
 *
 *+-------------------------------------------------------------------------*/
HRESULT CreateSnapIn (const CLSID& clsid, IComponentData** ppICD,
                    bool fCreateDummyOnFailure /* =true */)
{
    DECLARE_SC(sc, TEXT("CreateSnapIn"));

    EDummyCreateReason eReason = eSnapCreateFailed;
    IComponentDataPtr  spICD;

    sc = ScCheckPointers(ppICD);
    if(sc)
        return sc.ToHr();

    // initialize the out parameter
    *ppICD = NULL;

    CPolicy policy;
    sc = policy.ScInit();
    if (sc)
    {
        eReason = eSnapPolicyFailed;
    }
    else if (policy.IsPermittedSnapIn(clsid))
    {
        /*
         * Bug 258270: creating the snap-in might result in MSI running to
         * install it.  The MSI status window is modeless, but may spawn a
         * modal dialog.  If we don't manually disable MMC's main window,
         * the user might start clicking around in the scope tree while that
         * modal dialog is up, leading to reentrancy and all of the resulting
         * calamity that one would expect.
         */
        bool fReenableMMC = false;
        CScopeTree* pScopeTree = CScopeTree::GetScopeTree();
        HWND hwndMain = (pScopeTree) ? pScopeTree->GetMainWindow() : NULL;

        if (IsWindow (hwndMain))
        {
            fReenableMMC = IsWindowEnabled (hwndMain);

            if (fReenableMMC)
                EnableWindow (hwndMain, false);
        }

        //create the snapin
        sc = spICD.CreateInstance(clsid, NULL,MMC_CLSCTX_INPROC);
        if(!sc.IsError() && (spICD==NULL))
           sc = E_NOINTERFACE;

        /*
         * re-enable the main window if we disabled it
         */
        if (fReenableMMC)
            EnableWindow (hwndMain, true);

        if (sc)
        {
            ReportSnapinInitFailure(clsid);

            // Create a dummy snapin with snapin
            // creation failed message.
            eReason = eSnapCreateFailed;
        }
        else // creation succeeded. return
        {
            *ppICD = spICD.Detach();
            return sc.ToHr();
        }
    }
    else
    {
        // Display a message that policies does not
        // allow this snapin to be created.
        DisplayPolicyErrorMessage(clsid, FALSE);

        // Create a dummy snapin with policy
        // restriction message.
        sc = E_FAIL;
        eReason = eSnapPolicyFailed;
    }

    // If we've reached here, an error occurred

    // create dummy snap-in that only displays error message
    if (fCreateDummyOnFailure)
    {
        sc = ScCreateDummySnapin (&spICD, eReason, clsid);
        if(sc)
            return sc.ToHr();

        sc = ScCheckPointers(spICD, E_UNEXPECTED);
        if(sc)
            return sc.ToHr();

        *ppICD = spICD.Detach();
    }

    return sc.ToHr();
}


CExtSI* AddExtension(CSnapIn* pSnapIn, CLSID& rclsid, CSnapInsCache* pCache)
{
    ASSERT(pSnapIn != NULL);

    // See if extension is already present
    CExtSI* pExt = pSnapIn->FindExtension(rclsid);

    // if not, create one
    if (pExt == NULL)
    {
        // Create cache entry for extension snapin
        if (pCache == NULL)
            pCache = theApp.GetSnapInsCache();

        ASSERT(pCache != NULL);

        CSnapInPtr spExtSnapIn;
        SC sc = pCache->ScGetSnapIn(rclsid, &spExtSnapIn);
        ASSERT(!sc.IsError() && spExtSnapIn != NULL);

        // Attach extension to snap-in
        if (!sc.IsError())
            pExt = pSnapIn->AddExtension(spExtSnapIn);
    }
    else
    {
        // Clear deletion flag
        pExt->MarkDeleted(FALSE);
    }

    return pExt;
}


HRESULT LoadRequiredExtensions (
    CSnapIn*        pSnapIn,
    IComponentData* pICD,
    CSnapInsCache*  pCache /*=NULL*/)
{
    SC sc;

    ASSERT(pSnapIn != NULL);

    // if already loaded, just return
    if (pSnapIn->RequiredExtensionsLoaded())
        goto Cleanup;

    do
    {
        // Set extensions loaded, so we don't try again
        pSnapIn->SetRequiredExtensionsLoaded();

        // if snapin was enabling all extensions
        // clear the flags before asking again
        if (pSnapIn->DoesSnapInEnableAll())
        {
            pSnapIn->SetSnapInEnablesAll(FALSE);
            pSnapIn->SetAllExtensionsEnabled(FALSE);
        }

        // Mark all required extensions for deletion
        CExtSI* pExt = pSnapIn->GetExtensionSnapIn();
        while (pExt != NULL)
        {
            if (pExt->IsRequired())
                pExt->MarkDeleted(TRUE);

            pExt = pExt->Next();
        }

        // Check for interface
        IRequiredExtensionsPtr spReqExtn = pICD;

        // if snap-in wants all extensions enabled
        if (spReqExtn != NULL && spReqExtn->EnableAllExtensions() == S_OK)
        {
            // Set the "enable all" flags
            pSnapIn->SetSnapInEnablesAll(TRUE);
            pSnapIn->SetAllExtensionsEnabled(TRUE);
        }

        // if either user or snap-in wants all extensions
        if (pSnapIn->AreAllExtensionsEnabled())
        {
            // Get list of all extensions
            CExtensionsCache  ExtCache;
            sc = MMCGetExtensionsForSnapIn(pSnapIn->GetSnapInCLSID(), ExtCache);
            if (sc)
                goto Cleanup;

            // Add each extension to snap-in's extension list
            CExtensionsCacheIterator ExtIter(ExtCache);
            for (; ExtIter.IsEnd() == FALSE; ExtIter.Advance())
            {
                // Only add extensions that can be statically enabled
                if ((ExtIter.GetValue() & CExtSI::EXT_TYPE_STATIC) == 0)
                    continue;

                GUID clsid = ExtIter.GetKey();
                CExtSI* pExt = AddExtension(pSnapIn, clsid, pCache);

                // Mark required if enabled by the snap-in
                if (pExt != NULL && pSnapIn->DoesSnapInEnableAll())
                    pExt->SetRequired();
            }
        }

        CPolicy policy;
        sc = policy.ScInit();
        if (sc)
            goto Error;

        // if snap-in supports the interface and didn't enable all
        // ask for specific required extensions
        // Note: this is done even if the user has enabled all because
        //       we need to know which ones the snap-in requires
        if (spReqExtn != NULL && !pSnapIn->DoesSnapInEnableAll())
        {
            CLSID clsid;
            sc = spReqExtn->GetFirstExtension(&clsid);

            // Do while snap-in provides extension CLSIDs
            while (HrFromSc(sc) == S_OK)
            {
                // See if the extension is restricted by policy.
                // If so display a message.
                if (! policy.IsPermittedSnapIn(clsid))
                    DisplayPolicyErrorMessage(clsid, TRUE);

                // Add as required extension
                CExtSI* pExt = AddExtension(pSnapIn, clsid, pCache);
                if (pExt != NULL)
                    pExt->SetRequired();

                sc = spReqExtn->GetNextExtension(&clsid);
            }
        }

        // Delete extensions that are no longer required
        // Note: Because required extensions are updated when snap-in is first loaded
        //       we don't have to worry about adding/deleting any nodes now.
        pSnapIn->PurgeExtensions();

    } while (FALSE);

Cleanup:
    return HrFromSc(sc);

Error:
    TraceError(TEXT("LoadRequiredExtensions"), sc);
    goto Cleanup;
}


HRESULT CComponentData::Init(HMTNODE hMTNode)
{
    ASSERT(hMTNode != 0);

    if (IsInitialized() == TRUE)
        return S_OK;

    ASSERT(m_spSnapIn != NULL);
    HRESULT hr = S_OK;

    do
    {
        if (m_spIComponentData == NULL)
        {
            if (m_spSnapIn == NULL)
            {
                hr = E_POINTER;
                break;
            }

            IUnknownPtr spUnknown;
            hr = CreateSnapIn(m_spSnapIn->GetSnapInCLSID(), &m_spIComponentData);
            ASSERT(SUCCEEDED(hr));
            ASSERT(m_spIComponentData != NULL);

            if (FAILED(hr))
                break;
            if(m_spIComponentData == NULL)
            {
                hr = E_FAIL;
                break;
            }
        }

        hr = m_spIFramePrivate.CreateInstance(CLSID_NodeInit,
#if _MSC_VER >= 1100
                        NULL,
#endif
                        MMC_CLSCTX_INPROC);

        CHECK_HRESULT(hr);
        BREAK_ON_FAIL(hr);

        Debug_SetNodeInitSnapinName(m_spSnapIn, m_spIFramePrivate.GetInterfacePtr());

        // Init frame.
        ASSERT(m_ComponentID != -1);
        ASSERT(m_spIFramePrivate != NULL);
        ASSERT(m_spSnapIn != NULL);

        if ((m_spIFramePrivate == NULL) || (m_spSnapIn == NULL))
        {
            hr = E_UNEXPECTED;
            CHECK_HRESULT(hr);
            break;
        }

        m_spIFramePrivate->SetComponentID(m_ComponentID);
        m_spIFramePrivate->CreateScopeImageList(m_spSnapIn->GetSnapInCLSID());
        m_spIFramePrivate->SetNode(hMTNode, NULL);

        // Load extensions requested by snap-in and proceed regardless of outcome
        LoadRequiredExtensions(m_spSnapIn, m_spIComponentData);

        hr = m_spIComponentData->Initialize(m_spIFramePrivate);
        CHECK_HRESULT(hr);
        BREAK_ON_FAIL(hr);

    } while (0);

    if (FAILED(hr))
    {
        m_spIComponentData = NULL;
        m_spIFramePrivate = NULL;
    }

    return hr;
}



//############################################################################
//############################################################################
//
//  Implementation of class CMTSnapInNode
//
//############################################################################
//############################################################################

DEBUG_DECLARE_INSTANCE_COUNTER(CMTSnapInNode);

CMTSnapInNode::CMTSnapInNode(Properties* pProps)
      : m_spProps            (pProps),
        m_fCallbackForDisplayName(false)
{
    DEBUG_INCREMENT_INSTANCE_COUNTER(CMTSnapInNode);

    // Open and Closed images
    SetImage(eStockImage_Folder);
    SetOpenImage(eStockImage_OpenFolder);

	m_ePreloadState    = ePreload_Unknown;
    m_bHasBitmaps      = FALSE;
    m_resultImage      = CMTNode::GetImage();


    /*
     * attach this node to it's properties collection
     */
    if (m_spProps != NULL)
    {
        CSnapinProperties* pSIProps = CSnapinProperties::FromInterface (m_spProps);

        if (pSIProps != NULL)
            pSIProps->ScSetSnapInNode (this);
    }
}

CMTSnapInNode::~CMTSnapInNode() throw()
{
    DEBUG_DECREMENT_INSTANCE_COUNTER(CMTSnapInNode);

    for (int i=0; i < m_ComponentDataArray.size(); i++)
        delete m_ComponentDataArray[i];

    // DON'T CHANGE THIS ORDER!!!!!
    m_ComponentStorage.Clear();

    /*
     * detach this node from it's properties collection
     */
    if (m_spProps != NULL)
    {
        CSnapinProperties* pSIProps = CSnapinProperties::FromInterface (m_spProps);

        if (pSIProps != NULL)
            pSIProps->ScSetSnapInNode (NULL);
    }

	/*
	 * clean up the image lists (they aren't self-cleaning!)
	 */
	m_imlSmall.Destroy();
	m_imlLarge.Destroy();
}

HRESULT CMTSnapInNode::Init(void)
{
    DECLARE_SC (sc, _T("CMTSnapInNode::Init"));

    if (IsInitialized() == TRUE)
        return S_FALSE;

    HRESULT hr = CMTNode::Init();
    if (FAILED(hr))
        return hr;

    /*
     * initialize the snap-in with its properties interface
     */
    sc = ScInitProperties ();
    if (sc)
        return (sc.ToHr());

    if (IsPreloadRequired())
    {
        CComponentData* pCCD = GetPrimaryComponentData();
        ASSERT(pCCD != NULL);

        IDataObjectPtr spDataObject;
        hr = pCCD->QueryDataObject(GetUserParam(), CCT_SCOPE, &spDataObject);
        ASSERT(SUCCEEDED(hr));
        if (FAILED(hr))
            return hr;

        HSCOPEITEM hsi = reinterpret_cast<HSCOPEITEM>(this);

        pCCD->Notify(spDataObject, MMCN_PRELOAD, hsi, 0);
    }

    return S_OK;
}


/*+-------------------------------------------------------------------------*
 * CMTSnapInNode::ScInitProperties
 *
 * Initializes the snap-in with its properties interface, if it supports
 * ISnapinProperties.
 *--------------------------------------------------------------------------*/

SC CMTSnapInNode::ScInitProperties ()
{
    DECLARE_SC (sc, _T("CMTSnapInNode::ScInitProperties"));

    /*
     * get the snap-in's IComponentData
     */
    CComponentData* pCCD = GetPrimaryComponentData();
    if (pCCD == NULL)
        return (sc = E_UNEXPECTED);

    IComponentDataPtr spComponentData = pCCD->GetIComponentData();
    if (spComponentData == NULL)
        return (sc = E_UNEXPECTED);

    /*
     * If the snap-in supports ISnapinProperties, give it its Properties
     * interface.
     */
    ISnapinPropertiesPtr spISP = spComponentData;

    if (spISP != NULL)
    {
        /*
         * If we didn't persist properties for this snap-in we won't have
         * a CSnapinProperties object yet; create one now.
         */
        CSnapinProperties* pSIProps = NULL;
        sc = ScCreateSnapinProperties (&pSIProps);
        if (sc)
            return (sc);

        if (pSIProps == NULL)
            return (sc = E_UNEXPECTED);

        /*
         * Initialize the snap-in with the initial properties.
         */
        sc = pSIProps->ScInitialize (spISP, pSIProps, this);
        if (sc)
            return (sc);
    }

    return (sc);
}


/*+-------------------------------------------------------------------------*
 * CMTSnapInNode::ScCreateSnapinProperties
 *
 * Creates the CSnapinProperties object for this node.  It is safe to call
 * this method multiple times; subsequent invocations will short out.
 *--------------------------------------------------------------------------*/

SC CMTSnapInNode::ScCreateSnapinProperties (
    CSnapinProperties** ppSIProps)      /* O:pointer to the CSnapinProperties object (optional) */
{
    DECLARE_SC (sc, _T("CMTSnapInNode::ScCreateSnapinProperties"));

    /*
     * create a CSnapinProperties if we don't already have one
     */
    if (m_spProps == NULL)
    {
        /*
         * create the properties object
         */
        CComObject<CSnapinProperties>* pSIProps;
        sc = CComObject<CSnapinProperties>::CreateInstance (&pSIProps);
        if (sc)
            return (sc);

        if (pSIProps == NULL)
            return (sc = E_UNEXPECTED);

        /*
         * keep a reference to the object
         */
        m_spProps = pSIProps;
    }

    /*
     * return a pointer to the implementing object, if desired
     */
    if (ppSIProps != NULL)
        *ppSIProps = CSnapinProperties::FromInterface (m_spProps);

    return (sc);
}


/*+-------------------------------------------------------------------------*
 *
 * CMTSnapInNode::SetDisplayName
 *
 * PURPOSE: Sets the display name of the node.
 *
 * PARAMETERS:
 *    LPCTSTR  pszName :
 *
 * RETURNS:
 *    void
 *
 *+-------------------------------------------------------------------------*/
void
CMTSnapInNode::SetDisplayName(LPCTSTR pszName)
{
    bool fDisplayCallback = (pszName == (LPCTSTR)MMC_TEXTCALLBACK);

    /*
     * if our callback setting has changed, we're dirty
     */
    if (m_fCallbackForDisplayName != fDisplayCallback)
    {
        m_fCallbackForDisplayName = fDisplayCallback;
        SetDirty();
    }

    /*
     * if we're not now callback, cache the name (if we're callback,
     * the name will be cached the next time GetDisplayName is called)
     */
    if (!m_fCallbackForDisplayName)
        SetCachedDisplayName(pszName);
}

/*+-------------------------------------------------------------------------*
 *
 * CMTSnapInNode::GetDisplayName
 *
 * PURPOSE: Returns the display name of the node.
 *
 * RETURNS:
 *    LPCTSTR
 *
 *+-------------------------------------------------------------------------*/
tstring
CMTSnapInNode::GetDisplayName()
{
    if (m_fCallbackForDisplayName)
        return (CMTNode::GetDisplayName());

    return GetCachedDisplayName();
}

HRESULT CMTSnapInNode::IsExpandable()
{
    // if haven't intiailized the snap-in we have to assume that
    // there could be children
    if (!IsInitialized())
        return S_OK;

    return CMTNode::IsExpandable();
}


void CMTSnapInNode::CompressComponentDataArray()
{
    int nSize = m_ComponentDataArray.size();
    int nSkipped = 0;

    for (int i=0; i<nSize; ++i)
    {
        ASSERT(m_ComponentDataArray[i] != NULL);

        if (m_ComponentDataArray[i]->IsInitialized() == FALSE)
        {
            // if component failed to intialize, delete it
            // and skip over it
            delete m_ComponentDataArray[i];
            ++nSkipped;
        }
        else
        {
            // if components have been skiped, move the good component to the
            // first vacant slot and adjust the component's ID
            if (nSkipped)
            {
                m_ComponentDataArray[i-nSkipped] = m_ComponentDataArray[i];
                m_ComponentDataArray[i-nSkipped]->ResetComponentID(i-nSkipped);
            }
        }
     }

     // reduce array size by number skipped
     if (nSkipped)
        m_ComponentDataArray.resize(nSize - nSkipped);
}

void CMTSnapInNode::AddNode(CNode * pNode)
{
    #ifdef DBG
    {
        POSITION pos = m_NodeList.Find(pNode);
        ASSERT(pos == NULL);
    }
    #endif

    if (!FindNode(pNode->GetViewID()))
        m_NodeList.AddHead(pNode);
}

void CMTSnapInNode::RemoveNode(CNode * pNode)
{
    POSITION pos = m_NodeList.Find(pNode);

    if (pos != NULL)
        m_NodeList.RemoveAt(pos);
}

CSnapInNode* CMTSnapInNode::FindNode(int nViewID)
{
    POSITION pos = m_NodeList.GetHeadPosition();
    while (pos)
    {
        CSnapInNode* pSINode =
            dynamic_cast<CSnapInNode*>(m_NodeList.GetNext(pos));
        ASSERT(pSINode != NULL);

        if (pSINode->GetViewID() == nViewID)
        {
            return pSINode;
        }
    }

    return NULL;
}

UINT CMTSnapInNode::GetResultImage(CNode* pNode, IImageListPrivate* pResultImageList)
{
    if (pResultImageList == NULL)
        return GetImage();
    if ((m_bHasBitmaps == FALSE) && (m_resultImage != MMC_IMAGECALLBACK))
        return GetImage();

    int ret = 0;
    IFramePrivate* pFramePrivate = dynamic_cast<IFramePrivate*>(pResultImageList);
    COMPONENTID id = 0;
    pFramePrivate->GetComponentID (&id);
    COMPONENTID tempID = (COMPONENTID)-GetID(); // use Ravi's negative of ID scheme
    pFramePrivate->SetComponentID (tempID);

    if (m_bHasBitmaps)
	{
		const int nResultImageIndex = 0;

		/*
		 * if we haven't added this node's images to the result image list,
		 * add it now
		 */
		if (FAILED (pResultImageList->MapRsltImage (tempID, nResultImageIndex, &ret)))
		{
			/*
			 * Extract icons from the imagelist dynamically for device independence.
			 * (There ought to be a way to copy images from one imagelist to
			 * another, but there's not.  ImageList_Copy looks like it should
			 * work, but it only supports copying images within the same image
			 * list.)
			 */
			HRESULT hr;
			CSmartIcon icon;

			/*
			 * Set our icon from the small imagelist.  ImageListSetIcon
			 * will also set the large icon by stretching the small, but
			 * we'll fix that below.
			 */
			icon.Attach (m_imlSmall.GetIcon (0));
			hr = pResultImageList->ImageListSetIcon (
							reinterpret_cast<PLONG_PTR>((HICON)icon),
							nResultImageIndex);

			if (hr == S_OK)
			{
				/*
				 * Replace the large icon that ImageListSetIcon generated
				 * by stretching the small icon above, with the large icon
				 * that was created with the correct dimensions.
				 */
				icon.Attach (m_imlLarge.GetIcon (0));
				hr = pResultImageList->ImageListSetIcon (
								reinterpret_cast<PLONG_PTR>((HICON)icon),
								ILSI_LARGE_ICON (nResultImageIndex));
			}

			if (hr == S_OK)
				pResultImageList->MapRsltImage (tempID, nResultImageIndex, &ret);
		}
    }
	else if (m_resultImage == MMC_IMAGECALLBACK)
	{
        // ask snapin
        // first call IComponent::Notify w/ MMCN_ADD_IMAGES;
        CComponent* pComponent = pNode->GetPrimaryComponent ();
        if (pComponent) {
            IDataObjectPtr spDataObject;
            HRESULT hr = pComponent->QueryDataObject (GetUserParam(), CCT_RESULT, &spDataObject);
            if (spDataObject) {
                hr = pComponent->Notify (spDataObject, MMCN_ADD_IMAGES,
                                        (LPARAM)pResultImageList, (LPARAM)this);
                if (hr == S_OK) {
                    RESULTDATAITEM rdi;
                    ZeroMemory (&rdi, sizeof(rdi));
                    rdi.mask   = SDI_IMAGE;
                    rdi.lParam = GetUserParam();
                    rdi.nImage = 0;
                    hr = pComponent->GetDisplayInfo (&rdi);

                    // map user's number to our number
                    pResultImageList->MapRsltImage (tempID, rdi.nImage, &ret);
                }
            }
        }
    }
    pFramePrivate->SetComponentID (id);         // change back
    return (UINT)ret;
}


/*+-------------------------------------------------------------------------*
 * CMTSnapInNode::ScHandleCustomImages
 *
 * Retrieves images from a snap-in's About object and delegates to the
 * overload of this function to assemble the images into their appropriate
 * internal state.
 *--------------------------------------------------------------------------*/

SC CMTSnapInNode::ScHandleCustomImages (const CLSID& clsidSnapin)
{
	DECLARE_SC (sc, _T("CMTSnapInNode::ScHandleCustomImages"));

	m_bHasBitmaps = false;

	/*
	 * open the SnapIns key
	 */
    MMC_ATL::CRegKey keySnapins;
    sc.FromWin32 (keySnapins.Open (HKEY_LOCAL_MACHINE, SNAPINS_KEY, KEY_READ));
	if (sc)
		return (sc);

	OLECHAR szSnapinCLSID[40];
	if (StringFromGUID2 (clsidSnapin, szSnapinCLSID, countof(szSnapinCLSID)) == 0)
		return (sc = E_UNEXPECTED);

	/*
	 * open the key for the requested snap-in
	 */
    USES_CONVERSION;
	MMC_ATL::CRegKey keySnapin;
	sc.FromWin32 (keySnapin.Open (keySnapins, OLE2T(szSnapinCLSID), KEY_READ));
	if (sc)
		return (sc);

    // from snapin clsid, get "about" clsid, if any.
    TCHAR szAboutCLSID[40] = {0};
	DWORD dwCnt = sizeof(szAboutCLSID);
	sc.FromWin32 (keySnapin.QueryValue (szAboutCLSID, _T("About"), &dwCnt));
	if (sc)
		return (sc);

	if (szAboutCLSID[0] == 0)
		return (sc = E_FAIL);

    // create an instance of the About object
    ISnapinAboutPtr spISA;
    sc = spISA.CreateInstance (T2OLE (szAboutCLSID), NULL, MMC_CLSCTX_INPROC);
	if (sc)
		return (sc);

	sc = ScCheckPointers (spISA, E_UNEXPECTED);
	if (sc)
		return (sc);

    // get the images
    // Documentation explicitly states these images are NOT owned by
    // MMC, despite the are out parameters. So we cannot release them,
    // even though most snapins will leak them anyway.
    // see bugs #139613 & #140637
    HBITMAP hbmpSmallImage = NULL;
    HBITMAP hbmpSmallImageOpen = NULL;
    HBITMAP hbmpLargeImage = NULL;
	COLORREF crMask;
    sc = spISA->GetStaticFolderImage (&hbmpSmallImage,
									  &hbmpSmallImageOpen,
									  &hbmpLargeImage,
									  &crMask);
	if (sc)
		return (sc);

	/*
	 * if the snap-in didn't give us a complete set of bitmaps,
	 * use default images but don't fail
	 */
    if (hbmpSmallImage == NULL || hbmpSmallImageOpen == NULL || hbmpLargeImage == NULL)
        return (sc);

	sc = ScHandleCustomImages (hbmpSmallImage, hbmpSmallImageOpen, hbmpLargeImage, crMask);
	if (sc)
		return (sc);

	return (sc);
}


/*+-------------------------------------------------------------------------*
 * CMTSnapInNode::ScHandleCustomImages
 *
 * Takes custom images for this snap-in and adds them to an imagelist for
 * device-independence.
 *--------------------------------------------------------------------------*/

SC CMTSnapInNode::ScHandleCustomImages (
	HBITMAP		hbmSmall,			// I:small image
	HBITMAP		hbmSmallOpen,		// I:small open image
	HBITMAP		hbmLarge,			// I:large image
	COLORREF	crMask)				// I:mask color, common between all bitmaps
{
	DECLARE_SC (sc, _T("CMTSnapInNode::ScHandleCustomImages"));

	/*
	 * validate input
	 */
	sc = ScCheckPointers (hbmSmall, hbmSmallOpen, hbmLarge);
	if (sc)
		return (sc);

	/*
	 * we need to make copies of the input bitmaps because the calls to
	 * ImageList_AddMasked (below) messes up the background color
	 */
    WTL::CBitmap bmpSmallCopy = CopyBitmap (hbmSmall);
	if (bmpSmallCopy.IsNull())
		return (sc.FromLastError());

    WTL::CBitmap bmpSmallOpenCopy = CopyBitmap (hbmSmallOpen);
	if (bmpSmallOpenCopy.IsNull())
		return (sc.FromLastError());

    WTL::CBitmap bmpLargeCopy = CopyBitmap (hbmLarge);
	if (bmpLargeCopy.IsNull())
		return (sc.FromLastError());

	/*
	 * preserve the images in imagelists for device independence
	 */
	ASSERT (m_imlSmall.IsNull());
	if (!m_imlSmall.Create (16, 16, ILC_COLOR8 | ILC_MASK, 2, 1)	||
		(m_imlSmall.Add (bmpSmallCopy,     crMask) == -1)			||
		(m_imlSmall.Add (bmpSmallOpenCopy, crMask) == -1))
	{
		return (sc.FromLastError());
	}

	ASSERT (m_imlLarge.IsNull());
	if (!m_imlLarge.Create (32, 32, ILC_COLOR8 | ILC_MASK, 1, 1)	||
		(m_imlLarge.Add (bmpLargeCopy,     crMask) == -1))
	{
		return (sc.FromLastError());
	}

    m_bHasBitmaps = TRUE;

	sc = ScAddImagesToImageList ();
	if (sc)
		return (sc);

	return (sc);
}


void CMTSnapInNode::SetPrimarySnapIn(CSnapIn * pSI)
{
	DECLARE_SC (sc, _T("CMTSnapInNode::SetPrimarySnapIn"));

    ASSERT(m_ComponentDataArray.size() == 0);
    CComponentData* pCCD = new CComponentData(pSI);
    int nID = AddComponentDataToArray(pCCD);
    ASSERT(nID == 0);
    SetPrimaryComponentData(pCCD);

    if (m_bHasBitmaps == FALSE) {
        sc = ScHandleCustomImages (pSI->GetSnapInCLSID());
		if (sc)
			sc.TraceAndClear();

		if (m_bHasBitmaps)
			SetDirty();
    }
}

/***************************************************************************\
 *
 * METHOD:  CMTSnapInNode::ScInitIComponent
 *
 * PURPOSE: Either loads component (if has a stream/storage)
 *          or initializes with a fresh stream/storage
 *
 * PARAMETERS:
 *    CComponent* pCComponent   [in] component to initialize
 *    int viewID                [in] view id of the component
 *
 * RETURNS:
 *    SC    - result code
 *
\***************************************************************************/
SC CMTSnapInNode::ScInitIComponent(CComponent* pCComponent, int viewID)
{
    DECLARE_SC(sc, TEXT("CMTSnapInNode::ScInitIComponent"));

    // parameter chack
    sc = ScCheckPointers( pCComponent );
    if (sc)
        return sc;

    IComponent* pComponent = pCComponent->GetIComponent();
    sc = ScCheckPointers( pComponent, E_UNEXPECTED );
    if (sc)
        return sc;

    CLSID clsid = pCComponent->GetCLSID();

    // initialize the snapin object
    sc = ScInitComponentOrComponentData(pComponent, &m_ComponentPersistor, viewID, clsid );
    if (sc)
        return sc;

    pCComponent->SetIComponentInitialized();

    return sc;
}

/***************************************************************************\
 *
 * METHOD:  CMTSnapInNode::ScInitIComponentData
 *
 * PURPOSE: Either loads component data (if has a stream/storage)
 *          or initializes with a fresh stream/storage
 *
 * PARAMETERS:
 *    CComponentData* pCComponentData
 *
 * RETURNS:
 *    SC    - result code
 *
\***************************************************************************/
SC CMTSnapInNode::ScInitIComponentData(CComponentData* pCComponentData)
{
    DECLARE_SC(sc, TEXT("CMTSnapInNode::ScInitIComponentData"));

    // parameter check
    sc = ScCheckPointers( pCComponentData );
    if (sc)
        return sc;

    // Get the IComponentData to later obtain IPersist* from
    IComponentData* const pIComponentData = pCComponentData->GetIComponentData();
    sc = ScCheckPointers( pIComponentData, E_UNEXPECTED );
    if (sc)
        return sc;

    const CLSID& clsid = pCComponentData->GetCLSID();

    // initialize the snapin object
    sc = ScInitComponentOrComponentData(pIComponentData, &m_CDPersistor, CDPersistor::VIEW_ID_DOCUMENT, clsid );
    if (sc)
        return sc;

    pCComponentData->SetIComponentDataInitialized();

    return sc;
}

/***************************************************************************\
 *
 * METHOD:  CMTSnapInNode::ScInitComponentOrComponentData
 *
 * PURPOSE: Either loads snapin object (component or component data)
 *          or initializes with a fresh stream/storage
 *
 * PARAMETERS:
 *    IUnknown *pSnapin         [in] - snapin to initialize
 *    CMTSnapinNodeStreamsAndStorages *pStreamsAndStorages
 *                              [in] - collection of streams/storages
 *    int idView                [in] - view id of component
 *    const CLSID& clsid        [in] class is of the snapin
 *
 * RETURNS:
 *    SC    - result code
 *
\***************************************************************************/
SC CMTSnapInNode::ScInitComponentOrComponentData(IUnknown *pSnapin, CMTSnapinNodeStreamsAndStorages *pStreamsAndStorages, int idView, const CLSID& clsid )
{
    DECLARE_SC(sc, TEXT("CMTSnapInNode::ScInitComponentOrComponentData"));

    // parameter check
    sc = ScCheckPointers( pSnapin, pStreamsAndStorages );
    if (sc)
        return sc;

    IPersistStreamPtr       spIPersistStream;
    IPersistStreamInitPtr   spIPersistStreamInit;
    IPersistStoragePtr      spIPersistStorage;

    // determine the interface supported and load/init

    if ( (spIPersistStream = pSnapin) != NULL) // QI first for an IPersistStream
    {
        if ( pStreamsAndStorages->HasStream( idView, clsid ) )
        {
            // load
            IStreamPtr spStream;
            sc = pStreamsAndStorages->ScGetIStream( idView, clsid, &spStream);
            if (sc)
                return sc;

            sc = spIPersistStream->Load( spStream );
            if(sc)
                return sc;
        }
        // for this interface there in no initialization if we have nothing to load from
    }
    else if ( (spIPersistStreamInit = pSnapin) != NULL) // QI for an IPersistStreamInit
    {
        if ( pStreamsAndStorages->HasStream( idView, clsid ) )
        {
            // load
            IStreamPtr spStream;
            sc = pStreamsAndStorages->ScGetIStream( idView, clsid, &spStream);
            if (sc)
                return sc;

            sc = spIPersistStreamInit->Load( spStream );
            if(sc)
                return sc;
        }
        else
        {
            // init new
            sc = spIPersistStreamInit->InitNew();
            if (sc)
                return sc;
        }
    }
    else if ( (spIPersistStorage = pSnapin) != NULL) // QI for an IPersistStorage
    {
        bool bHasStorage = pStreamsAndStorages->HasStorage( idView, clsid );

        IStoragePtr spStorage;
        sc = pStreamsAndStorages->ScGetIStorage( idView, clsid, &spStorage);
        if (sc)
            return sc;

        if ( bHasStorage )
        {
            sc = spIPersistStorage->Load( spStorage );
            if (sc)
                return sc;
        }
        else
        {
            sc = spIPersistStorage->InitNew( spStorage );
            if (sc)
                return sc;
        }
    }

    return sc;
}


/**************************************************************************
// CMTSnapinNode::CloseView
//
// This method does any clean-up that is required before deleting
// a view. For now all we do is close any OCXs assocoiated with the view.
// This is done so the OCX can close before the view is hidden.
***************************************************************************/
HRESULT CMTSnapInNode::CloseView(int idView)
{
    // Locate associated node in specified view
    CNodeList& nodes = GetNodeList();
    ASSERT(&nodes != NULL);
    if (&nodes == NULL)
        return E_FAIL;

    POSITION pos = nodes.GetHeadPosition();
    while (pos)
    {
        CNode* pNode = nodes.GetNext(pos);
        ASSERT(pNode != NULL);
        if (pNode == NULL)
            continue;

        // if match found, tell node to close its controls
        if (pNode->GetViewID() == idView)
        {
            CSnapInNode* pSINode = dynamic_cast<CSnapInNode*>(pNode);
            ASSERT(pSINode != NULL);

            pSINode->CloseControls();
            break;
        }
    }

    HRESULT hr = CMTNode::CloseView(idView);
    ASSERT(hr == S_OK);
    return hr == S_OK ? S_OK : E_FAIL;
}


HRESULT CMTSnapInNode::DeleteView(int idView)
{
    HRESULT hr;

    m_ComponentPersistor.RemoveView(idView);

    hr = CMTNode::DeleteView(idView);
    ASSERT(hr == S_OK);
    return hr == S_OK ? S_OK : E_FAIL;
}

SC CMTSnapInNode::ScLoad()
{
    SC      sc;
    CStream stream;
    CLSID   clsid;

    sc = CMTNode::ScLoad();
    if(sc)
        goto Error;

    stream.Attach(GetTreeStream());

    sc = stream.ScRead(&clsid, sizeof(clsid));
    if(sc)
        goto Error;

    // read bitmaps, if any

    // we are ignoring error here, because we had gaps in the save code
    // in the past and now we have console files to deal with
    // see bug 96402 "Private:  AV in FrontPage Server Extensions & HP ManageX"
	ASSERT (sizeof(m_bHasBitmaps) == sizeof(BOOL));
    sc = stream.ScRead(&m_bHasBitmaps, sizeof(BOOL), true /*bIgnoreErrors*/);
    if(sc)
        goto Error;

    if (m_bHasBitmaps == TRUE)
    {

		WTL::CBitmap bmpSmall;
        sc = ScLoadBitmap (stream, &bmpSmall.m_hBitmap);
        if(sc)
            goto Error;

		WTL::CBitmap bmpSmallOpen;
        sc = ScLoadBitmap (stream, &bmpSmallOpen.m_hBitmap);
        if(sc)
            goto Error;

		WTL::CBitmap bmpLarge;
        sc = ScLoadBitmap (stream, &bmpLarge.m_hBitmap);
        if(sc)
            goto Error;

		COLORREF crMask;
        sc = stream.ScRead(&crMask, sizeof(COLORREF));
        if(sc)
            goto Error;

		sc = ScHandleCustomImages (bmpSmall, bmpSmallOpen, bmpLarge, crMask);
		if (sc)
			goto Error;
    }

    {
        CSnapInsCache* const pCache = theApp.GetSnapInsCache();
        ASSERT(pCache != NULL);
        if (pCache == NULL)
            return E_FAIL;

        CSnapInPtr spSI;
        sc = pCache->ScGetSnapIn(clsid, &spSI);
        if (sc)
            goto Error;
        sc = ScCheckPointers(spSI, E_UNEXPECTED);
        if (sc)
            goto Error;

        SetPrimarySnapIn(spSI);
        pCache->SetDirty(FALSE);
    }

    // see if we have to do the preload thing
	{
		BOOL bPreload = FALSE;
		sc = stream.ScRead(&bPreload, sizeof(BOOL), true /*bIgnoreErrors*/); // the preload bit is optional, do no error out.
		if(sc)
			goto Error;

		SetPreloadRequired (bPreload);
	}

    // read all the streams and storages for this node
    sc = ScReadStreamsAndStoragesFromConsole();
	if(sc)
		goto Error;

Cleanup:
    return sc == S_OK ? S_OK : E_FAIL;
Error:
    TraceError(TEXT("CMTSnapInNode::Load"), sc);
    goto Cleanup;

}

HRESULT CMTSnapInNode::IsDirty()
{
	DECLARE_SC (sc, _T("CMTSnapInNode::IsDirty"));

    HRESULT hr = CMTNode::IsDirty();
    ASSERT(SUCCEEDED(hr));
    if (hr != S_FALSE)
    {
        TraceDirtyFlag(TEXT("CMTSnapinNode"), true);
        return hr;
    }

    hr = AreIComponentDatasDirty();
    ASSERT(hr == S_OK || hr == S_FALSE);
    if (hr == S_OK)
    {
        TraceDirtyFlag(TEXT("CMTSnapinNode"), true);
        return S_OK;
    }
    if (hr != S_FALSE)
    {
        TraceDirtyFlag(TEXT("CMTSnapinNode"), true);
        return E_FAIL;
    }

    hr = AreIComponentsDirty();
    ASSERT(hr == S_OK || hr == S_FALSE);
    if (hr == S_OK)
    {
        TraceDirtyFlag(TEXT("CMTSnapinNode"), true);
        return S_OK;
    }
    if (hr != S_FALSE)
    {
        TraceDirtyFlag(TEXT("CMTSnapinNode"), true);
        return E_FAIL;
    }

	/*
	 * See if "preload" bit changed.  If an error occurred while querying
	 * the snap-in, we'll assume that the preload bit hasn't changed.
	 */
	PreloadState ePreloadState = m_ePreloadState;
	SC scNoTrace = ScQueryPreloadRequired (ePreloadState);

    if (scNoTrace.IsError() || (ePreloadState == m_ePreloadState))
    {
        TraceDirtyFlag(TEXT("CMTSnapinNode"), false);
        return S_FALSE;
    }

    TraceDirtyFlag(TEXT("CMTSnapinNode"), true);
    return S_OK;
}


/*+-------------------------------------------------------------------------*
 * CMTSnapInNode::AreIComponentDatasDirty
 *
 * Returns S_OK if any of the IComponentDatas attached to this snap-in node
 * (i.e. those of this snap-in and its extensions) is dirty, S_FALSE otherwise.
 *--------------------------------------------------------------------------*/

HRESULT CMTSnapInNode::AreIComponentDatasDirty()
{
    CComponentData* const pCCD = GetPrimaryComponentData();

#if 1
    /*
     * we used to check the primary component data explicitly, but that
     * (if it exists) is always the first element in the IComponentData
     * array.  The loop below will handle it in a more generic manner.
     */
    ASSERT ((pCCD == NULL) || (pCCD == m_ComponentDataArray[0]));
#else
    IComponentData* const pICCD = pCCD != NULL ?
                                           pCCD->GetIComponentData() : NULL;

    if ((pICCD != NULL) && (IsIUnknownDirty (pICCD) == S_OK))
        return (S_OK);
#endif

    /*
     * check all of the IComponentDatas attached to this snap-in node
     * to see if any one is dirty
     */
    UINT cComponentDatas = m_ComponentDataArray.size();

    for (UINT i = 0; i < cComponentDatas; i++)
    {
        IComponentData* pICCD = (m_ComponentDataArray[i] != NULL)
                                    ? m_ComponentDataArray[i]->GetIComponentData()
                                    : NULL;

        if ((pICCD != NULL) && (IsIUnknownDirty (pICCD) == S_OK))
            return (S_OK);
    }

    return (S_FALSE);
}


/*+-------------------------------------------------------------------------*
 * CMTSnapInNode::AreIComponentsDirty
 *
 * Returns S_OK if any of the IComponents attached to this snap-in node
 * (in any view) is dirty, S_FALSE otherwise.
 *--------------------------------------------------------------------------*/

HRESULT CMTSnapInNode::AreIComponentsDirty()
{
    CNodeList& nodes = GetNodeList();
    ASSERT(&nodes != NULL);
    if (&nodes == NULL)
        return E_FAIL;

    POSITION pos = nodes.GetHeadPosition();

    while (pos)
    {
        CNode* pNode = nodes.GetNext(pos);
        ASSERT(pNode != NULL);
        if (pNode == NULL)
            return E_FAIL;

        CSnapInNode* pSINode = dynamic_cast<CSnapInNode*>(pNode);
        ASSERT(pSINode != NULL);
        if (pSINode == NULL)
            return E_FAIL;

        const CComponentArray& components = pSINode->GetComponentArray();
        const int end = components.size();
        for (int i = 0; i < end; i++)
        {
            CComponent* pCC = components[i];
            if ((NULL == pCC) || (pCC->IsInitialized() == FALSE) )
                continue;

            IComponent* pComponent = pCC->GetIComponent();
            if (NULL == pComponent)
                continue;

            HRESULT hr = IsIUnknownDirty(pComponent);
            ASSERT(hr == S_OK || hr == S_FALSE);
            if (hr == S_OK)
                return S_OK;
            if (hr != S_FALSE)
                return E_FAIL;
        }
    }

    return S_FALSE;
}


/*+-------------------------------------------------------------------------*
 * CMTSnapInNode::IsIUnknownDirty
 *
 * Checks an IUnknown* for any of the three persistence interfaces
 * (IPersistStream, IPersistStreamInit, and IPersistStorage, in that order)
 * and if any of them is supported, returns the result of that interface's
 * IsDirty method.
 *--------------------------------------------------------------------------*/

HRESULT CMTSnapInNode::IsIUnknownDirty(IUnknown* pUnk)
{
    ASSERT(pUnk != NULL);
    if (pUnk == NULL)
        return E_POINTER;

    // 1. Check for IPersistStream
    IPersistStreamPtr spIPS = pUnk;
    if (spIPS != NULL)
        return spIPS->IsDirty();

    // 2. Check for IPersistStreamInit
    IPersistStreamInitPtr spIPSI = pUnk;
    if (spIPSI != NULL)
        return spIPSI->IsDirty();

    // 3. Check for IPersistStorage
    IPersistStoragePtr spIPStg = pUnk;
    if (spIPStg != NULL)
        return spIPStg->IsDirty();

    return S_FALSE;
}

// local functions
inline long LongScanBytes (long bits)
{
    bits += 31;
    bits /= 8;
    bits &= ~3;
    return bits;
}

SC ScLoadBitmap (CStream &stream, HBITMAP* pBitmap)
{
    DECLARE_SC(sc, TEXT("ScLoadBitmap"));

    // parameter check
    sc = ScCheckPointers(pBitmap);
    if (sc)
        return sc;

	/*
	 * The bitmap we're going to CreateDIBitmap into should be empty.
	 * If it's not, it may indicate a bitmap leak.  If you've investigated
	 * an instance where this assert fails and determined that *pBitmap
	 * isn't being leaked (be very sure!), set *pBitmap to NULL before
	 * calling ScLoadBitmap.  DO NOT remove this assert because you
	 * think it's hyperactive.
	 */
	ASSERT (*pBitmap == NULL);

    // initialization
    *pBitmap = NULL;

    DWORD dwSize;
    sc = stream.ScRead(&dwSize, sizeof(DWORD));
    if(sc)
        return sc;

    CAutoArrayPtr<BYTE> spDib(new BYTE[dwSize]);
    sc = ScCheckPointers(spDib, E_OUTOFMEMORY);
    if (sc)
        return sc;

    // have a typed pointer for member access
    typedef const BITMAPINFOHEADER * const LPCBITMAPINFOHEADER;
    LPCBITMAPINFOHEADER pDib = reinterpret_cast<LPCBITMAPINFOHEADER>(&spDib[0]);

    sc = stream.ScRead(spDib, dwSize);
    if(sc)
        return sc;

    BYTE * bits = (BYTE*) (pDib+1);
    int depth = pDib->biBitCount*pDib->biPlanes;
    if (depth <= 8)
        bits += (1<<depth)*sizeof(RGBQUAD);

    // get a screen dc
    WTL::CClientDC dc(NULL);
    if (dc == NULL)
        return sc.FromLastError(), sc;

    HBITMAP hbitmap = CreateDIBitmap (dc, pDib, CBM_INIT, bits, (BITMAPINFO*)pDib, DIB_RGB_COLORS);
    if (hbitmap == NULL)
        return sc.FromLastError(), sc;

    // return the bitmap
    *pBitmap = hbitmap;

    return sc;
}

/*+-------------------------------------------------------------------------*
 *
 * PersistBitmap
 *
 * PURPOSE:  Saves Bitmap to / loads from XML doc.
 *
 * PARAMETERS:
 *    CPersistor &persistor :
 *    LPCTSTR   name : name attribute of instance in XML
 *    HBITMAP   hBitmap :
 *
 * RETURNS:
 *    void
 *
 *+-------------------------------------------------------------------------*/
void PersistBitmap(CPersistor &persistor, LPCTSTR name, HBITMAP& hBitmap)
{
    DECLARE_SC(sc, TEXT("PersistBitmap"));

    // combined from ScSaveBitmap & ScLoadBitmap

    // get a screen dc
    WTL::CClientDC dc(NULL);
    if (dc == NULL)
        sc.FromLastError(), sc.Throw();

    CXMLAutoBinary binBlock;


    if (persistor.IsStoring())
    {
        // check pointers
        sc = ScCheckPointers(hBitmap);
        if (sc)
            sc.Throw();

        // create memory dc
        WTL::CDC memdc;
        memdc.CreateCompatibleDC(dc);
        if (memdc == NULL)
            sc.FromLastError(), sc.Throw();

        // get bitmap info
        BITMAP bm;
        if (0 == GetObject (hBitmap, sizeof(BITMAP), (LPSTR)&bm))
            sc.FromLastError(), sc.Throw();

        // TODO:  lousy palette stuff

        int depth;
        switch(bm.bmPlanes*bm.bmBitsPixel)
        {
        case 1:
            depth = 1;
            break;
        case 2:
        case 3:
        case 4:
            depth = 4;
            break;
        case 5:
        case 6:
        case 7:
        case 8:
            depth = 8;
            break;
        default:
            depth = 24;
            break;
        }

        DWORD dwSize = sizeof(BITMAPINFOHEADER) + bm.bmHeight*LongScanBytes(depth*bm.bmWidth);
        DWORD colors = 0;
        if(depth  <= 8)
        {
            colors  = 1<<depth;
            dwSize += colors*sizeof(RGBQUAD);
        }

        sc = binBlock.ScAlloc(dwSize);
        if (sc)
            sc.Throw();

        CXMLBinaryLock sLock(binBlock); // will unlock in destructor

        BITMAPINFOHEADER* dib = NULL;
        sc = sLock.ScLock(&dib);
        if (sc)
            sc.Throw();

        sc = ScCheckPointers(dib, E_UNEXPECTED);
        if (sc)
            sc.Throw();

        BYTE * bits = colors*sizeof(RGBQUAD) + (BYTE *)&dib[1];

        dib->biSize          = sizeof(BITMAPINFOHEADER);
        dib->biWidth         = bm.bmWidth;
        dib->biHeight        = bm.bmHeight;
        dib->biPlanes        = 1;
        dib->biBitCount      = (WORD)depth;
        dib->biCompression   = 0;
        dib->biSizeImage     = dwSize; // includes palette and bih ??
        dib->biXPelsPerMeter = 0;
        dib->biYPelsPerMeter = 0;
        dib->biClrUsed       = colors;
        dib->biClrImportant  = colors;

        HBITMAP hold = memdc.SelectBitmap (hBitmap);
        if (hold == NULL)
            sc.FromLastError(), sc.Throw();

        int lines = GetDIBits (memdc, hBitmap, 0, bm.bmHeight, (LPVOID)bits, (BITMAPINFO*)dib, DIB_RGB_COLORS);
        // see if we were successful
        if (!lines)
            sc.FromLastError();
        else if(lines != bm.bmHeight)
            sc = E_UNEXPECTED; // should not happen

        // clean up gdi resources.
        memdc.SelectBitmap(hold);

        if(sc)
            sc.Throw();
    }

    persistor.Persist(binBlock, name);

    if (persistor.IsLoading())
    {
		/*
		 * The bitmap we're going to CreateDIBitmap into should be empty.
		 * If it's not, it may indicate a bitmap leak.  If you've investigated
		 * an instance where this assert fails and determined that hBitmap
		 * isn't being leaked (be very sure!), set hBitmap to NULL before
		 * calling PersistBitmap.  DO NOT remove this assert because you
		 * think it's hyperactive.
		 */
		ASSERT (hBitmap == NULL);
        hBitmap = NULL;

        CXMLBinaryLock sLock(binBlock); // will unlock in destructor

        BITMAPINFOHEADER* dib = NULL;
        sc = sLock.ScLock(&dib);
        if (sc)
            sc.Throw();

        sc = ScCheckPointers(dib, E_UNEXPECTED);
        if (sc)
            sc.Throw();

        BYTE * bits = (BYTE *)&dib[1];
        int depth = dib->biBitCount*dib->biPlanes;
        if (depth <= 8)
            bits += (1<<depth)*sizeof(RGBQUAD);

        HBITMAP hbitmap = CreateDIBitmap (dc,
                                          dib, CBM_INIT,
                                          bits,
                                          (BITMAPINFO*)dib,
                                          DIB_RGB_COLORS);

        if (hbitmap == NULL)
            sc.FromLastError(), sc.Throw();

        hBitmap = hbitmap;
    }
}


/*+-------------------------------------------------------------------------*
 *
 * CMTSnapInNode::Persist
 *
 * PURPOSE:  Persist snapin node
 *
 * PARAMETERS:
 *    CPersistor &persistor :
 *
 * RETURNS:
 *    void
 *
 *+-------------------------------------------------------------------------*/
void CMTSnapInNode::Persist(CPersistor& persistor)
{
    DECLARE_SC(sc, TEXT("CMTSnapInNode::Persist"));

    // save the base class.
    CMTNode::Persist(persistor);

    CLSID clsid;
    ZeroMemory(&clsid,sizeof(clsid));

    if (persistor.IsLoading())
    {
        // check if bitmaps are here
        m_bHasBitmaps = persistor.HasElement(XML_TAG_NODE_BITMAPS, NULL);

        /*
         * load persisted properties, if present
         */
        if (persistor.HasElement (CSnapinProperties::_GetXMLType(), NULL))
        {
            /*
             * create a properties object, since we don't have one yet
             */
            ASSERT (m_spProps == NULL);
            CSnapinProperties* pSIProps = NULL;
            sc = ScCreateSnapinProperties (&pSIProps);
            if (sc)
                sc.Throw();

            if (pSIProps == NULL)
                (sc = E_UNEXPECTED).Throw();

            /*
             * load the properties
             */
            persistor.Persist (*pSIProps);
        }
    }
    else
    {
        clsid = GetPrimarySnapInCLSID();

        /*
         * persist properties, if present
         */
        if (m_spProps != NULL)
        {
            CSnapinProperties* pSIProps = CSnapinProperties::FromInterface(m_spProps);

            if (pSIProps != NULL)
                persistor.Persist (*pSIProps);
        }
    }

    persistor.PersistAttribute(XML_ATTR_MT_NODE_SNAPIN_CLSID, clsid);

    if (m_bHasBitmaps)
    {
        CPersistor persistorBitmaps(persistor, XML_TAG_NODE_BITMAPS);

		/*
		 * Early versions of XML persistence saved device-dependent
		 * bitmaps.  If there's a BinaryData element named "SmallOpen",
		 * this is a console saved by early XML persistence -- read it
		 * in a special manner.
		 */
		if (persistor.IsLoading() &&
			persistorBitmaps.HasElement (XML_TAG_VALUE_BIN_DATA,
										 XML_NAME_NODE_BITMAP_SMALL_OPEN))
		{
			WTL::CBitmap bmpSmall, bmpSmallOpen, bmpLarge;
			std::wstring strMask;

			PersistBitmap(persistorBitmaps, XML_NAME_NODE_BITMAP_SMALL,      bmpSmall.m_hBitmap);
			PersistBitmap(persistorBitmaps, XML_NAME_NODE_BITMAP_SMALL_OPEN, bmpSmallOpen.m_hBitmap);
			PersistBitmap(persistorBitmaps, XML_NAME_NODE_BITMAP_LARGE,      bmpLarge.m_hBitmap);
			persistorBitmaps.PersistAttribute(XML_ATTR_NODE_BITMAPS_MASK, strMask);

			COLORREF crMask = wcstoul(strMask.c_str(), NULL, 16);
			sc = ScHandleCustomImages (bmpSmall, bmpSmallOpen, bmpLarge, crMask);
			if (sc)
				sc.Throw();
		}

		/*
		 * We either writing or reading a modern XML file that has persisted
		 * the images in device-independent imagelist.  Read/write them that way.
		 */
		else
		{
			persistorBitmaps.Persist (m_imlSmall, XML_NAME_NODE_BITMAP_SMALL);
			persistorBitmaps.Persist (m_imlLarge, XML_NAME_NODE_BITMAP_LARGE);

			if (persistor.IsLoading())
			{
				sc = ScAddImagesToImageList();
				if (sc)
					sc.Throw();
			}
		}
    }

    // setup snapins CD
	if (persistor.IsLoading())
	{
        CSnapInsCache* const pCache = theApp.GetSnapInsCache();
        if (pCache == NULL)
            sc.Throw(E_FAIL);

        CSnapInPtr spSI;
        sc = pCache->ScGetSnapIn(clsid, &spSI);
        if (sc)
            sc.Throw();
        if (spSI != NULL)
            SetPrimarySnapIn(spSI);
        else
            sc.Throw(E_UNEXPECTED);
        pCache->SetDirty(FALSE);
    }

    // when storing, ask snapins to save their data first
    if ( persistor.IsStoring() )
    {
        sc = ScSaveIComponentDatas();
        if (sc)
            sc.Throw();

        sc = ScSaveIComponents();
        if (sc)
            sc.Throw();
    }

    persistor.Persist(m_CDPersistor);
    persistor.Persist(m_ComponentPersistor);

	/*
	 * Save/load the preload bit.  Do this last to avoid busting old .msc files.
	 */
	BOOL bPreload = false;
    if (persistor.IsStoring() && IsInitialized())
		bPreload = IsPreloadRequired ();

    persistor.PersistAttribute(XML_ATTR_MT_NODE_PRELOAD, CXMLBoolean(bPreload));

    if (persistor.IsLoading())
		SetPreloadRequired (bPreload);
}


/*+-------------------------------------------------------------------------*
 * CMTSnapInNode::ScAddImagesToImageList
 *
 * Adds the small and small(open) bitmaps for the snap-in to the scope
 * tree's imagelist.
 *--------------------------------------------------------------------------*/

SC CMTSnapInNode::ScAddImagesToImageList()
{
	DECLARE_SC (sc, _T("CMTSnapInNode::ScAddImagesToImageList"));

	/*
	 * get the scope tree's imagelist
	 */
	CScopeTree* pScopeTree = CScopeTree::GetScopeTree();
	sc = ScCheckPointers (pScopeTree, E_UNEXPECTED);
	if (sc)
		return (sc);

	WTL::CImageList imlScopeTree = pScopeTree->GetImageList();
	if (imlScopeTree.IsNull())
		return (sc = E_UNEXPECTED);

	/*
	 * add images to scope tree's imagelist, first closed...
	 */
	CSmartIcon icon;
	icon.Attach (m_imlSmall.GetIcon (0));
	if (icon == NULL)
		return (sc.FromLastError());

    SetImage (imlScopeTree.AddIcon (icon));

	/*
	 * ...then open
	 */
	icon.Attach (m_imlSmall.GetIcon (1));
	if (icon == NULL)
		return (sc.FromLastError());

    SetOpenImage (imlScopeTree.AddIcon (icon));

	return (sc);
}


CComponent* CMTSnapInNode::GetComponent(UINT nViewID, COMPONENTID nID,
                                        CSnapIn* pSnapIn)
{
    CNodeList& nodes = GetNodeList();
    POSITION pos = nodes.GetHeadPosition();
    CNode* pNode = NULL;

    while (pos)
    {
        pNode = nodes.GetNext(pos);
        if (pNode != NULL && pNode->GetViewID() == (int)nViewID)
            break;
    }

    if(pNode == NULL)
        return NULL;

    ASSERT(pNode != NULL);
    ASSERT(pNode->GetViewID() == (int)nViewID);

    if (pNode->GetViewID() != (int)nViewID)
        return NULL;

    CSnapInNode* pSINode = dynamic_cast<CSnapInNode*>(pNode);
    CComponent* pCC = pSINode->GetComponent(nID);

    if (pCC == NULL)
        pCC = pSINode->CreateComponent(pSnapIn, nID);

    return pCC;
}

CNode* CMTSnapInNode::GetNode(CViewData* pViewData, BOOL fRootNode)
{
    /*
     * check for another CSnapInNode that already exists in this view
     */
    CSnapInNode* pExistingNode = FindNode (pViewData->GetViewID());
    CSnapInNode* pNewNode;

    /*
     * if this is the first CSnapInNode for this view, create a unique one
     */
    if (fRootNode || (pExistingNode == NULL))
        pNewNode = new CSnapInNode (this, pViewData, fRootNode);

    /*
     * otherwise, copy the node that's here
     */
    else
        pNewNode = new CSnapInNode (*pExistingNode);

    return (pNewNode);
}


/***************************************************************************\
 *
 * METHOD:  CMTSnapInNode::Reset
 *
 * PURPOSE: Resets the node in order to reload extensions. basically it forces
 *          save-load-init sequence to refresh the snapin node
 *
 * PARAMETERS:
 *
 * RETURNS:
 *    void
 *
\***************************************************************************/
void CMTSnapInNode::Reset()
{
    DECLARE_SC(sc, TEXT("CMTSnapInNode::Reset"));

    CSnapIn * pSnapIn = GetPrimarySnapIn();
    ASSERT(pSnapIn != NULL);

    // we will perform resetting of components and component datas
    // by storing / loading them "the XML way"
    // following that there is nothing what makes this node different
    // from one loaded from XML, so we will change it's type

    sc = ScSaveIComponentDatas();
    if (sc)
        sc.TraceAndClear(); // continue even on error

    sc = ScSaveIComponents();
    if (sc)
        sc.TraceAndClear(); // continue even on error

    // need to reset component XML streams/storage
    sc = m_CDPersistor.ScReset();
    if (sc)
        sc.TraceAndClear(); // continue even on error

    sc = m_ComponentPersistor.ScReset();
    if (sc)
        sc.TraceAndClear(); // continue even on error

    // First Reset all the nodes
    POSITION pos = m_NodeList.GetHeadPosition();
    while (pos)
    {
        CSnapInNode* pSINode =
            dynamic_cast<CSnapInNode*>(m_NodeList.GetNext(pos));
        ASSERT(pSINode != NULL);

        pSINode->Reset();
    }

    for (int i=0; i < m_ComponentDataArray.size(); i++)
        delete m_ComponentDataArray[i];

    m_ComponentDataArray.clear();

    CMTNode::Reset();

    ResetExpandedAtLeastOnce();

    SetPrimarySnapIn(pSnapIn);

    pos = m_NodeList.GetHeadPosition();
    while (pos)
    {
        CSnapInNode* pSINode =
            dynamic_cast<CSnapInNode*>(m_NodeList.GetNext(pos));
        ASSERT(pSINode != NULL);

        CComponent* pCC = new CComponent(pSnapIn);
        pCC->SetComponentID(GetPrimaryComponentID());
        pSINode->AddComponentToArray(pCC);

        pSINode->SetPrimaryComponent(pCC);
    }

    Init();

    pos = m_NodeList.GetHeadPosition();
    while (pos)
    {
        CSnapInNode* pSINode =
            dynamic_cast<CSnapInNode*>(m_NodeList.GetNext(pos));
        ASSERT(pSINode != NULL);
        pSINode->InitComponents();
    }
}



/*+-------------------------------------------------------------------------*
 * class CLegacyNodeConverter
 *
 *
 * PURPOSE: Used to emulate the legacy node snapins' Save routines.
 *
 *+-------------------------------------------------------------------------*/
class CLegacyNodeConverter : public CSerialObjectRW
{

public:
    CLegacyNodeConverter(LPCTSTR szName, LPCTSTR szView)
    : m_strName(szName), m_strView(szView)
    {
    }

    ~CLegacyNodeConverter()
    {
        // must call detach or the strings will be removed from the string table.
        m_strName.Detach();
        m_strView.Detach();
    }



public:
    // CSerialObject methods
    virtual UINT    GetVersion()     {return 1;}
    virtual HRESULT ReadSerialObject (IStream &stm, UINT nVersion) {ASSERT(0 && "Should not come here."); return E_UNEXPECTED;}
    virtual HRESULT WriteSerialObject(IStream &stm);

private: // attributes - persisted
    CStringTableString  m_strName;  // the name of the root node, which is the only node created by the snapin
    CStringTableString  m_strView;  // the view displayed by the node.
};


/*+-------------------------------------------------------------------------*
 *
 * CLegacyNodeConverter::WriteSerialObject
 *
 * PURPOSE: Writes out the name and view strings using the format expected
 *          by the built in snapins.
 *
 * PARAMETERS:
 *    IStream & stm :
 *
 * RETURNS:
 *    HRESULT
 *
 *+-------------------------------------------------------------------------*/
HRESULT
CLegacyNodeConverter::WriteSerialObject(IStream &stm)
{
    stm << m_strName;
    stm << m_strView;

    return S_OK;
}

/*+-------------------------------------------------------------------------*
 *
 * CMTSnapInNode::ScConvertLegacyNode
 *
 * PURPOSE: Reads in an legacy node and converts it to a built-in snapin node.
 *          1) The original tree stream is read and the target URL or OCX is read.
 *          2) The new Data stream with the munged CLSID name is created
 *             and the data required by the snapin is placed there. Because
 *             the bitmap etc is already loaded, and because the original
 *             stream is thrown away, we don't need to emulate the "tree"
 *             stream. Also, because this snapin has no view specific information,
 *             the views storage is not used.
 *
 * PARAMETERS: clsid: The CLSID of the built in snapin.
 *
 * RETURNS:
 *    SC
 *
 *+-------------------------------------------------------------------------*/
SC
CMTSnapInNode::ScConvertLegacyNode(const CLSID &clsid)
{
    USES_CONVERSION;
    SC              sc;
    std::wstring    strView;
    CStream         stream;
    CStream         nodeStream = NULL;
    int             iStorageOrStream=0;
    IStreamPtr      spCDStream;

    bool bIsHTMLNode = (&clsid == &CLSID_HTMLSnapin);
    bool bIsOCXNode  = (&clsid == &CLSID_OCXSnapin);

    // 1. load the base class
    sc = CMTNode::ScLoad();
    if(sc)
        goto Error;

    // get the tree stream.
    stream.Attach(GetTreeStream());

    // 2. read the URL or OCX string as needed.
    if(bIsHTMLNode)
    {
        WCHAR* szView = NULL;

        // get the string length of the label, and read the string.
        unsigned int stringLength;
        sc = stream.ScRead(&stringLength, sizeof(stringLength));
        if(sc)
            goto Error;

        szView = reinterpret_cast<wchar_t*>(alloca((stringLength+1)*sizeof(WCHAR))); // allocates on stack, don't free.
        if (szView == NULL)
            goto PointerError;

        sc = stream.ScRead(szView, stringLength*2);
        if(sc)
            goto Error;

        szView[stringLength] = TEXT('\0'); // null terminate the string.

        strView = szView;
    }
    else if(bIsOCXNode)
    {
        CLSID clsidOCX;

        // Read OCX clsid
        sc = stream.ScRead(&clsidOCX, sizeof(clsidOCX));
        if(sc)
            goto Error;

        {
            WCHAR   szCLSID[40];
            if (0 == StringFromGUID2 (clsidOCX, szCLSID, countof(szCLSID)))
            {
                sc = E_UNEXPECTED;
                goto Error;
            }

            strView = szCLSID;
        }
    }

    // at this point, strView contains either the URL or OCX CLSID.



    // 3. Write node name
    sc = m_CDPersistor.ScGetIStream( clsid, &spCDStream );
    if (sc)
        goto Error;

    nodeStream.Attach( spCDStream );
    if(NULL == nodeStream.Get())
        goto PointerError;

    // 4. Write out the Data stream.
    {
		tstring strName = GetDisplayName();
        CLegacyNodeConverter converter(strName.data(), OLE2CT(strView.data()));

        // call the converter to write out the stream.
        sc = converter.Write(nodeStream);
        if(sc)
            goto Error;
    }

    // at this point, the "data" stream  should be correctly written out.

    // 5. For OCX nodes, convert the view streams and storages
    /*      OLD                                             NEW
            2 (node storage)                                2 (node storage)
                data                                            data
                tree                                            tree
                view                                            view
                    1  <--- streams and storages   ---------        1
                    2  <--- written by OCX, 1 per view --   \           1jvmv2n4y1k471h9ujk86lite7 (OCX snap-in)
                                                         \   -------->      ocx_stream (or ocx_storage)
                                                          \
                                                           ------>  2   1jvmv2n4y1k471h9ujk86lite7 (OCX snap-in)
                                                                            ocx_stream (or ocx_storage)


    */
    if(bIsOCXNode)
    {
        for(iStorageOrStream = 1 /*NOT zero*/; ; iStorageOrStream++)
        {
            // create the name of the storage
            CStr strStorageOrStream;
            strStorageOrStream.Format(TEXT("%d"), iStorageOrStream);

            // at this point strStorageOrStream should contain a number like "1"
            CStorage storageView(GetViewStorage());

            // rename the storage or stream labelled "1" to "temp" under the same parent.
            sc = storageView.ScMoveElementTo(T2COLE(strStorageOrStream), storageView, L"temp", STGMOVE_MOVE);
            if(sc == SC(STG_E_FILENOTFOUND))    // loop end condition - no more streams or storages
            {
                sc.Clear();
                break;
            }

            if(sc)
                goto Error;

            // now we create the storage with the same name, eg "1"
            {
                WCHAR name[MAX_PATH];
                GetComponentStorageName(name, clsid); // the name of the snapin component

                CStorage storageNewView, storageSnapIn;
                sc = storageNewView.ScCreate(storageView, T2COLE(strStorageOrStream),
                                          STGM_WRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
                                          L"\\node\\#\\view\\#\\storage" /*CHANGE*/);
                if(sc)
                    goto Error;

                // create the snapin's storage underneath the view's storage
                sc = storageSnapIn.ScCreate(storageNewView, name,
                                            STGM_WRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
                                            L"\\node\\#\\view\\#\\storage\\#\\snapinStorage");
                if(sc)
                    goto Error;

                // move the "temp" stream or storage to the storage called L"ocx_streamorstorage"
                // (which is what the OCX snapin expects.)

                sc = storageView.ScMoveElementTo(L"temp", storageSnapIn, L"ocx_streamorstorage", STGMOVE_MOVE);
                if(sc)
                    goto Error;
            }

        }
    }


    // 6. now do the same thing that CMTSnapInNode::ScLoad would do.
    {
        CSnapInsCache* const pCache = theApp.GetSnapInsCache();
        ASSERT(pCache != NULL);
        if (pCache == NULL)
            goto FailedError;

        CSnapInPtr spSI;
        sc = pCache->ScGetSnapIn(clsid, &spSI);
        ASSERT(!sc.IsError() && spSI != NULL);

        if (!sc.IsError() && spSI != NULL)
            SetPrimarySnapIn(spSI);

        pCache->SetDirty(FALSE);

        if(sc)
            goto Error;
    }

    // always set the preload bit.
    SetPreloadRequired (true);

    // Some actions (loading bitmaps for example) performed here invalidate the node
    // and set the dirty flag. Since coverting legacy node may be done any time again
    // the converted node should not be assumed as changed.
    ClearDirty();

    // read all the streams and storages for this node
    sc = ScReadStreamsAndStoragesFromConsole();
	if(sc)
		goto Error;

Cleanup:
    return sc;

FailedError:
    sc = E_FAIL;
    goto Error;
PointerError:
    sc = E_POINTER;
Error:
    TraceError(TEXT("CMTSnapInNode::ScConvertLegacyNode"), sc);
    goto Cleanup;
}

HRESULT copyStream(IStream* dest, IStream* src)
{
    ASSERT(dest != NULL);
    ASSERT(src != NULL);
    if (dest == NULL || src == NULL)
        return E_POINTER;

    const LARGE_INTEGER loc = {0,0};
    ULARGE_INTEGER newLoc;
    HRESULT hr = src->Seek(loc, STREAM_SEEK_SET, &newLoc);
    ASSERT(SUCCEEDED(hr));
    if (FAILED(hr))
        return E_FAIL;

    hr = dest->Seek(loc, STREAM_SEEK_SET, &newLoc);
    ASSERT(SUCCEEDED(hr));
    if (FAILED(hr))
        return E_FAIL;

    const ULARGE_INTEGER size = {0,0};
    hr = dest->SetSize(size);
    ASSERT(SUCCEEDED(hr));
    if (FAILED(hr))
        return hr;

    STATSTG statstg;
    hr = src->Stat(&statstg, STATFLAG_NONAME);
    ASSERT(hr == S_OK);
    if (hr != S_OK)
        return E_FAIL;

    ULARGE_INTEGER cr;
    ULARGE_INTEGER cw;
    hr = src->CopyTo(dest, statstg.cbSize, &cr, &cw);
#if 0 // for debugging...
    for (long i = 0; true; i++)
    {
        BYTE b;
        long bytesRead;
        hr = src->Read(&b, sizeof(b), &bytesRead);
        if (hr != S_OK)
            return S_OK;
        long bytesWritten;
        hr = dest->Write(&b, bytesRead, &bytesWritten);
        ASSERT(hr == S_OK);
        ASSERT(bytesWritten == bytesRead);
        if (hr != S_OK || bytesWritten != bytesRead)
            return E_FAIL;
    }
#endif
    return S_OK;
}

//############################################################################
//############################################################################
//
//  Helper functions
//
//############################################################################
//############################################################################

void    DisplayPolicyErrorMessage(const CLSID& clsid, bool bExtension)
{
    CStr strMessage;

    if (bExtension)
        strMessage.LoadString(GetStringModule(), IDS_EXTENSION_NOTALLOWED);
    else
        strMessage.LoadString(GetStringModule(), IDS_SNAPIN_NOTALLOWED);

    // Get the snapin name for the error message.
    CSnapInsCache* pSnapInsCache = theApp.GetSnapInsCache();
    ASSERT(pSnapInsCache != NULL);
    CSnapInPtr spSnapIn;

    SC sc = pSnapInsCache->ScFindSnapIn(clsid, &spSnapIn);
    if (!sc.IsError() && (NULL != spSnapIn))
    {
		WTL::CString strName;
		sc = spSnapIn->ScGetSnapInName (strName);

		if (!sc.IsError())
		{
			strMessage += _T("\n");
			strMessage += strName;
			strMessage += _T(".");
		}
    }

    ::MessageBox(NULL, strMessage, _T("MMC"), MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
}

/***************************************************************************\
 *
 * METHOD:  CMMCSnapIn::get_Vendor
 *
 * PURPOSE: returns vendor info for snapin. Implements OM property SnapIn.Vendor
 *
 * PARAMETERS:
 *    PBSTR pbstrVendor [out] - vendor info
 *
 * RETURNS:
 *    HRESULT    - result code
 *
\***************************************************************************/
STDMETHODIMP CMMCSnapIn::get_Vendor( PBSTR pbstrVendor )
{
    DECLARE_SC(sc, TEXT("CMMCSnapIn::get_Vendor"));

    sc = ScCheckPointers(pbstrVendor);
    if (sc)
        return sc.ToHr();

    // init out parameter
    *pbstrVendor = NULL;

    // get the snapin about
    CSnapinAbout *pSnapinAbout = NULL;
    sc = ScGetSnapinAbout(pSnapinAbout);
    if (sc)
        return sc.ToHr();

    // recheck the pointer
    sc = ScCheckPointers(pSnapinAbout, E_UNEXPECTED);
    if (sc)
        return sc.ToHr();

    *pbstrVendor = ::SysAllocString( pSnapinAbout->GetCompanyName() );

    return sc.ToHr();
}

/***************************************************************************\
 *
 * METHOD:  CMMCSnapIn::get_Version
 *
 * PURPOSE: returns version info for snapin. Implements OM property SnapIn.Version
 *
 * PARAMETERS:
 *    PBSTR pbstrVersion [out] - version info
 *
 * RETURNS:
 *    HRESULT    - result code
 *
\***************************************************************************/
STDMETHODIMP CMMCSnapIn::get_Version( PBSTR pbstrVersion )
{
    DECLARE_SC(sc, TEXT("CMMCSnapIn::get_Version"));

    sc = ScCheckPointers(pbstrVersion);
    if (sc)
        return sc.ToHr();

    // init out parameter
    *pbstrVersion = NULL;

    // get the snapin about
    CSnapinAbout *pSnapinAbout = NULL;
    sc = ScGetSnapinAbout(pSnapinAbout);
    if (sc)
        return sc.ToHr();

    // recheck the pointer
    sc = ScCheckPointers(pSnapinAbout, E_UNEXPECTED);
    if (sc)
        return sc.ToHr();

    *pbstrVersion = ::SysAllocString( pSnapinAbout->GetVersion() );

    return sc.ToHr();
}

/***************************************************************************\
 *
 * METHOD:  CMMCSnapIn::GetMTSnapInNode
 *
 * PURPOSE: helper. returns mtnode for the snapin
 *
 * PARAMETERS:
 *
 * RETURNS:
 *    CMTSnapInNode *    - node
 *
\***************************************************************************/
CMTSnapInNode * CMMCSnapIn::GetMTSnapInNode()
{
    CMTSnapInNode *pMTSnapInNode = NULL;
    SC sc = ScGetTiedObject(pMTSnapInNode);
    if (sc)
        return NULL;

    return pMTSnapInNode;
}

/***************************************************************************\
 *
 * METHOD:  CMMCSnapIn::ScGetSnapinAbout
 *
 * PURPOSE: helper. returns snapins about object
 *
 * PARAMETERS:
 *    CSnapinAbout*& pAbout [out] - snapins about object
 *
 * RETURNS:
 *    SC    - result code
 *
\***************************************************************************/
SC CMMCSnapIn::ScGetSnapinAbout(CSnapinAbout*& pAbout)
{
    DECLARE_SC(sc, TEXT("CMMCSnapIn::ScGetSnapinAbout"));

    // init out param
    pAbout = NULL;

    // If the snapin object is already created just return it.
    if (NULL != (pAbout = m_spSnapinAbout.get()))
        return sc;

    // get snapins clsid
    CLSID clsidSnapin = GUID_NULL;
    sc = GetSnapinClsid(clsidSnapin);
    if (sc)
        return sc;

    CLSID clsidAbout; // get the about class-id.
    sc = ScGetAboutFromSnapinCLSID(clsidSnapin, clsidAbout);
    if (sc)
        return sc;

    if (clsidSnapin == GUID_NULL)
        return sc = E_FAIL;

    // Create about object
    m_spSnapinAbout = SnapinAboutPtr (new CSnapinAbout);
    if (! m_spSnapinAbout.get())
        return sc = E_OUTOFMEMORY;

    // and initialize it.
    if (!m_spSnapinAbout->GetSnapinInformation(clsidAbout))
        return sc = E_FAIL;

    pAbout = m_spSnapinAbout.get();

    return sc;
}


/*+-------------------------------------------------------------------------*
 * CMTSnapInNode::IsPreloadRequired
 *
 * Returns true if the snap-in wants MMCN_PRELOAD notifications, false
 * otherwise.
 *--------------------------------------------------------------------------*/

BOOL CMTSnapInNode::IsPreloadRequired () const
{
	DECLARE_SC (sc, _T("CMTSnapInNode::IsPreloadRequired"));

	/*
	 * if we don't know whether the snap-in wants MMCN_PRELOAD (because
	 * we haven't asked it yet), ask now
	 */
	if (m_ePreloadState == ePreload_Unknown)
	{
		/*
		 * assume preload isn't required
		 */
		m_ePreloadState = ePreload_False;

		sc = ScQueryPreloadRequired (m_ePreloadState);
		if (sc)
			sc.TraceAndClear();
	}

	return (m_ePreloadState == ePreload_True);
}


/*+-------------------------------------------------------------------------*
 * CMTSnapInNode::ScQueryPreloadRequired
 *
 * Asks the snap-in whether it requires preload notification by asking its
 * data object for the CCF_SNAPIN_PRELOADS format.
 *
 * Returns in ePreload:
 *
 * 		ePreload_True	snap-in requires MMCN_PRELOAD
 * 		ePreload_False	snap-in doesn't require MMCN_PRELOAD
 *
 * If anything fails during the process of asking the snap-in for
 * CCF_SNAPIN_PRELOADS, the value of ePreload is unchanged.
 *--------------------------------------------------------------------------*/

SC CMTSnapInNode::ScQueryPreloadRequired (
	PreloadState&	ePreload) const		/* O:preload state for snap-in		*/
{
	DECLARE_SC (sc, _T("CMTSnapInNode::ScQueryPreloadRequired"));

	/*
	 * make sure we have a primary ComponentData
	 */
    CComponentData* pCCD = GetPrimaryComponentData();
	sc = ScCheckPointers (pCCD, E_UNEXPECTED);
	if (sc)
		return (sc);

	/*
	 * get the data object for this node
	 */
	IDataObjectPtr spDataObject;
	sc = pCCD->QueryDataObject(GetUserParam(), CCT_SCOPE, &spDataObject);
	if (sc)
		return (sc);

	sc = ScCheckPointers (spDataObject, E_UNEXPECTED);
	if (sc)
		return (sc);

	/*
	 * CCF_SNAPIN_PRELOADS is an optional clipboard format, so it's not
	 * an error if ExtractData fails.
	 */
	BOOL bPreload = (ePreload == ePreload_True) ? TRUE : FALSE;
	if (SUCCEEDED (ExtractData (spDataObject, GetPreLoadFormat(),
								(BYTE*)&bPreload, sizeof(BOOL))))
	{
		ePreload = (bPreload) ? ePreload_True : ePreload_False;
	}

	return (sc);
}

/***************************************************************************\
 *
 * METHOD:  CMTSnapInNode::ScReadStreamsAndStoragesFromConsole
 *
 * PURPOSE: Enumerates old (structured storage based) console.
 *          Enumerates the streams and storages under the snapin node.
 *          For each stream/storage found adds a copy to m_CDPpersistor
 *          or m_ComponentPersistor, indexed by a hash value (name in storage).
 *          These entries will be recognized and stored by a CLSID when
 *          CLSID is known ( when request by CLSID is made )
 *
 * PARAMETERS:
 *
 * RETURNS:
 *    SC    - result code
 *
\***************************************************************************/
SC CMTSnapInNode::ScReadStreamsAndStoragesFromConsole()
{
    DECLARE_SC(sc, TEXT("CMTSnapInNode::ScReadStreamsAndStoragesFromConsole"));

    IStorage* pNodeCDStorage = GetStorageForCD();
    sc = ScCheckPointers( pNodeCDStorage, E_POINTER );
    if (sc)
        return sc;

    IEnumSTATSTGPtr spEnum;
    sc = pNodeCDStorage->EnumElements( 0, NULL, 0, &spEnum );
    if (sc)
        return sc;

    // recheck pointer
    sc = ScCheckPointers( spEnum, E_POINTER );
    if (sc)
        return sc;

    // reset enumeration
    sc = spEnum->Reset();
    if (sc)
        return sc;

    // enumerate the items ( each entry is for separate component data )
    while (1)
    {
        STATSTG statstg;
        ZeroMemory( &statstg, sizeof(statstg) );

        ULONG cbFetched = 0;
        sc = spEnum->Next( 1, &statstg, &cbFetched );
        if (sc)
            return sc;

        if ( sc != S_OK ) // - done
        {
            sc.Clear();
            break;
        }

        // attach to the out param
        CCoTaskMemPtr<WCHAR> spName( statstg.pwcsName );

        // make a copy of streams and storages
        if ( statstg.type == STGTY_STREAM )
        {
            IStreamPtr spStream;
            sc = OpenDebugStream(pNodeCDStorage, spName, STGM_READ | STGM_SHARE_EXCLUSIVE,
                                 L"\\node\\#\\data\\clsid", &spStream);
            if (sc)
                return sc;

            sc = m_CDPersistor.ScInitIStream( spName, spStream );
            if (sc)
                return sc;
        }
        else if ( statstg.type == STGTY_STORAGE )
        {
            IStoragePtr spStorage;
            sc = OpenDebugStorage(pNodeCDStorage, spName, STGM_READ | STGM_SHARE_EXCLUSIVE,
                                  L"\\node\\#\\data\\clsid", &spStorage);
            if (sc)
                return sc;

            sc = m_CDPersistor.ScInitIStorage( spName, spStorage );
            if (sc)
                return sc;
        }
    }

    // view streams/storages
    IStorage *pNodeComponentStorage = GetViewStorage();
    sc = ScCheckPointers( pNodeComponentStorage, E_POINTER );
    if (sc)
        return sc;

    spEnum = NULL;
    sc = pNodeComponentStorage->EnumElements( 0, NULL, 0, &spEnum );
    if (sc)
        return sc;

    // recheck pointer
    sc = ScCheckPointers( spEnum, E_POINTER );
    if (sc)
        return sc;

    // reset enumeration
    sc = spEnum->Reset();
    if (sc)
        return sc;

    // enumerate the items ( each entry is for separate view )
    while (1)
    {
        STATSTG statstg;
        ZeroMemory( &statstg, sizeof(statstg) );

        ULONG cbFetched = 0;
        sc = spEnum->Next( 1, &statstg, &cbFetched );
        if (sc)
            return sc;

        if ( sc != S_OK ) //  done
        {
            sc.Clear();
            break;
        }

        // attach to the out param
        CCoTaskMemPtr<WCHAR> spName( statstg.pwcsName );

        // read the view storage
        if ( statstg.type == STGTY_STORAGE )
        {
            int idView = CMTNode::GetViewIdFromStorageName(spName);

            IStoragePtr spViewStorage;
            sc = OpenDebugStorage(pNodeComponentStorage, spName, STGM_READ | STGM_SHARE_EXCLUSIVE,
                                  L"\\node\\#\\view\\#", &spViewStorage);
            if (sc)
                return sc;

            // enumerate what's inside a view storage

            IEnumSTATSTGPtr spViewEnum;
            sc = spViewStorage->EnumElements( 0, NULL, 0, &spViewEnum );
            if (sc)
                return sc;

            // recheck pointer
            sc = ScCheckPointers( spViewEnum, E_POINTER );
            if (sc)
                return sc;

            // reset enumeration
            sc = spViewEnum->Reset();
            if (sc)
                return sc;

            // enumerate the items ( each entry is for separate component in a view )
            while (1)
            {
                STATSTG statstg;
                ZeroMemory( &statstg, sizeof(statstg) );

                ULONG cbFetched = 0;
                sc = spViewEnum->Next( 1, &statstg, &cbFetched );
                if (sc)
                    return sc;

                if ( sc != S_OK ) // - done
                {
                    sc.Clear();
                    break;
                }

                // attach to the out param
                CCoTaskMemPtr<WCHAR> spName( statstg.pwcsName );

                // make a copy of streams and storages
                if ( statstg.type == STGTY_STREAM )
                {
                    IStreamPtr spStream;
                    sc = OpenDebugStream(spViewStorage, spName, STGM_READ | STGM_SHARE_EXCLUSIVE,
                                         L"\\node\\#\\view\\#\\clsid", &spStream);
                    if (sc)
                        return sc;

                    sc = m_ComponentPersistor.ScInitIStream( idView, spName, spStream );
                    if (sc)
                        return sc;
                }
                else if ( statstg.type == STGTY_STORAGE )
                {
                    IStoragePtr spStorage;
                    sc = OpenDebugStorage(spViewStorage, spName, STGM_READ | STGM_SHARE_EXCLUSIVE,
                                          L"\\node\\#\\view\\#\\clsid", &spStorage);
                    if (sc)
                        return sc;

                    sc = m_ComponentPersistor.ScInitIStorage( idView, spName, spStorage );
                    if (sc)
                        return sc;
                }
            }
        }
    }

    // by now we should have loaded everything from the console file
    return sc;
}

/***************************************************************************\
 *
 * METHOD:  CMTSnapInNode::ScSaveIComponentDatas
 *
 * PURPOSE: Saves IComponentDatass for all snapins under this static scope node
 *
 * PARAMETERS:
 *
 *
 * RETURNS:
 *    SC    - result code
 *
\***************************************************************************/
SC CMTSnapInNode::ScSaveIComponentDatas( )
{
    DECLARE_SC(sc, TEXT("CMTSnapInNode::ScSaveIComponentDatas"));

    // if node is not initialized ( not expanded ) - nothing to save
    // old data will be persisted.
    if ( !IsInitialized() )
        return sc;

    // go for every component data we have
    for( int i = 0; i< GetNumberOfComponentDatas(); i++ )
    {
        CComponentData* pCD = GetComponentData(i);
        sc = ScCheckPointers(pCD, E_UNEXPECTED);
        if (sc)
            return sc;

        sc = ScSaveIComponentData( pCD );
        if (sc)
            return sc;
    }

    return sc;
}

/***************************************************************************\
 *
 * METHOD:  CMTSnapInNode::ScSaveIComponentData
 *
 * PURPOSE: Determines snapin's IComponentData persistence capabilities (QI for IPersistXXXX)
 *          And asks it to save giving maintained stream/storage as a media.
 *
 * PARAMETERS:
 *    CComponentData* pCD   [in]  component data
 *
 * RETURNS:
 *    SC    - result code
 *
\***************************************************************************/
SC CMTSnapInNode::ScSaveIComponentData( CComponentData* pCD )
{
    DECLARE_SC(sc, TEXT("CMTSnapInNode::ScSaveIComponentData"));

    sc = ScCheckPointers(pCD);
    if (sc)
        return sc;

    // check if the component is initialized
    if ( !pCD->IsIComponentDataInitialized() )
    {
        // compatibility with mmc1.2 - give another chance.
        sc = ScInitIComponentData(pCD);
        if (sc)
            return sc;
    }

    // Check first for an IComponentData
    IComponentData* const pICCD = pCD->GetIComponentData();
    sc = ScCheckPointers( pICCD, E_UNEXPECTED );
    if (sc)
        return sc;

    // Get the snapin name for the error message.
	CSnapInPtr spSnapin = pCD->GetSnapIn();

    // now ask the snapin to save the data
    sc = ScAskSnapinToSaveData( pICCD, &m_CDPersistor, CDPersistor::VIEW_ID_DOCUMENT, pCD->GetCLSID(), spSnapin );
    if (sc)
        return sc;

    return sc;
}

/***************************************************************************\
 *
 * METHOD:  CMTSnapInNode::ScSaveIComponents
 *
 * PURPOSE: Saves IComponents for all snapins under this static scope node
 *
 * PARAMETERS:
 *
 *
 * RETURNS:
 *    SC    - result code
 *
\***************************************************************************/
SC CMTSnapInNode::ScSaveIComponents( )
{
    DECLARE_SC(sc, TEXT("CMTSnapInNode::ScSaveIComponents"));

    // if node is not initialized ( not expanded ) - nothing to save
    // old data will be persisted.
    if ( !IsInitialized() )
        return sc;

    // go for every CNode in every view
    CNodeList& nodes = GetNodeList();
    POSITION pos = nodes.GetHeadPosition();

    while (pos)
    {
        CNode* pNode = nodes.GetNext( pos );
        sc = ScCheckPointers( pNode, E_UNEXPECTED );
        if (sc)
            return sc;

        CSnapInNode* pSINode = dynamic_cast<CSnapInNode*>(pNode);
        sc = ScCheckPointers(pSINode, E_UNEXPECTED);
        if (sc)
            return sc;

        const int viewID = pNode->GetViewID();
        const CComponentArray& components = pSINode->GetComponentArray();
        const int size = components.size();

        for (int i = 0; i < size; i++)
        {
            CComponent* pCC = components[i];
            if ( pCC != NULL )
            {
                sc = ScSaveIComponent( pCC, viewID);
                if (sc)
                    return sc;
            }
        }
    }

    return sc;
}

/***************************************************************************\
 *
 * METHOD:  CMTSnapInNode::ScSaveIComponent
 *
 * PURPOSE: Determines snapin's IComponent persistence capabilities (QI for IPersistXXXX)
 *          And asks it to save giving maintained stream/storage as a media.
 *
 * PARAMETERS:
 *    CComponent* pCComponent [in] component
 *    int viewID              [in] view id for which the component is created
 *
 * RETURNS:
 *    SC    - result code
 *
\***************************************************************************/
SC CMTSnapInNode::ScSaveIComponent( CComponent* pCComponent, int viewID )
{
    DECLARE_SC(sc, TEXT("CMTSnapInNode::ScSaveIComponent"));

    // parameter check
    sc = ScCheckPointers( pCComponent );
    if (sc)
        return sc;

    const CLSID& clsid = pCComponent->GetCLSID();

    // check if the component is initialized (compatibility with mmc 1.2)
    // give another chance to load
    if ( !pCComponent->IsIComponentInitialized() )
    {
        sc = ScInitIComponent(pCComponent, viewID);
        if (sc)
            return sc;
    }

    // get IComponent
    IComponent* pComponent = pCComponent->GetIComponent();
    sc = ScCheckPointers(pComponent, E_UNEXPECTED);
    if (sc)
        return sc;

    // Get the snapin to get name for the error message.
	CSnapInPtr spSnapin = pCComponent->GetSnapIn();

    // now ask the snapin to save the data
    sc = ScAskSnapinToSaveData( pComponent, &m_ComponentPersistor, viewID, clsid, spSnapin );
    if (sc)
        return sc;

    return sc;
}


/***************************************************************************\
 *
 * METHOD:  CMTSnapInNode::ScAskSnapinToSaveData
 *
 * PURPOSE: Determines snapin persistence capabilities (QI for IPersistXXXX)
 *          And asks it to save giving maintained stream/storage as a media.
 *          This method is called to save both Components and ComponentDatas
 *
 * PARAMETERS:
 *    IUnknown *pSnapin         [in] snapin which data needs to be saved
 *    CMTSnapinNodeStreamsAndStorages *pStreamsAndStorages
 *                              [in] collection of streams/storage where to save
 *    int idView                [in] view id - key for saved data
 *    const CLSID& clsid        [in] class id - key for saved data
 *    CSnapIn *pCSnapin         [in] pointer to CSnapin, used for display name on error
 *
 * RETURNS:
 *    SC    - result code
 *
\***************************************************************************/
SC CMTSnapInNode::ScAskSnapinToSaveData( IUnknown *pSnapin,
                                        CMTSnapinNodeStreamsAndStorages *pStreamsAndStorages,
                                        int idView , const CLSID& clsid, CSnapIn *pCSnapin )
{
    DECLARE_SC(sc, TEXT("CMTSnapInNode::ScAskSnapinToSaveData"));

    sc = ScCheckPointers( pSnapin, pStreamsAndStorages );
    if (sc)
        return sc;

    IPersistStreamPtr spIPS;
    IPersistStoragePtr spIPStg;
    IPersistStreamInitPtr spIPSI;

    // QI for IPersistStream
    if ( (spIPS = pSnapin) != NULL)
    {
        // get the object for persistence
        CXML_IStream *pXMLStream = NULL;
        sc = pStreamsAndStorages->ScGetXmlStream( idView, clsid, pXMLStream );
        if (sc)
            return sc;

        // recheck the pointer
        sc = ScCheckPointers( pXMLStream, E_UNEXPECTED );
        if (sc)
            return sc;

        // save data to stream
        sc = pXMLStream->ScRequestSave( spIPS.GetInterfacePtr() );
        if (sc)
            goto DisplaySnapinError;
    }
    else if ( (spIPSI = pSnapin) != NULL) // QI for IPersistStreamInit
    {
        // get the object for persistence
        CXML_IStream *pXMLStream = NULL;
        sc = pStreamsAndStorages->ScGetXmlStream( idView, clsid, pXMLStream );
        if (sc)
            return sc;

        // recheck the pointer
        sc = ScCheckPointers( pXMLStream, E_UNEXPECTED );
        if (sc)
            return sc;

        // save data to stream
        sc = pXMLStream->ScRequestSave( spIPSI.GetInterfacePtr() );
        if (sc)
            goto DisplaySnapinError;
    }
    else if ( (spIPStg = pSnapin) != NULL) // QI for IPersistStorage
    {
        // get the object for persistence
        CXML_IStorage *pXMLStorage = NULL;
        sc = pStreamsAndStorages->ScGetXmlStorage( idView, clsid, pXMLStorage );
        if (sc)
            return sc;

        // recheck the pointer
        sc = ScCheckPointers( pXMLStorage, E_UNEXPECTED );
        if (sc)
            return sc;

        // save data to storage
        sc = pXMLStorage->ScRequestSave( spIPStg.GetInterfacePtr() );
        if (sc)
            goto DisplaySnapinError;
    }

   return sc;

// display snapin failure
DisplaySnapinError:

    // need to inform the world...

    CStr strMessage;
    strMessage.LoadString(GetStringModule(), IDS_SNAPIN_SAVE_FAILED);

	if (pCSnapin != NULL)
	{
		WTL::CString strName;
		if (!pCSnapin->ScGetSnapInName(strName).IsError())
		{
			strMessage += _T("\n");
			strMessage += strName;
			strMessage += _T(".");
		}
	}

    ::MessageBox(NULL, strMessage, _T("Error"), MB_OK | MB_ICONEXCLAMATION);

    return sc;
}