/////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 1996-2000 Microsoft Corporation
//
//  Module Name:
//      ClusDoc.cpp
//
//  Abstract:
//      Implementation of the CClusterDoc class.
//
//  Author:
//      David Potter (davidp)   May 1, 1996
//
//  Revision History:
//
//  Notes:
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <ClAdmWiz.h>
#include "CluAdmin.h"
#include "ConstDef.h"
#include "ClusDoc.h"
#include "Cluster.h"
#include "ExcOper.h"
#include "Notify.h"
#include "TraceTag.h"
#include "ListView.h"
#include "TreeView.h"
#include "GrpWiz.h"
#include "ResWiz.h"
#include "SplitFrm.h"
#include "YesToAll.h"
#include "ActGrp.h"

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

/////////////////////////////////////////////////////////////////////////////
// Global Variables
/////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
CTraceTag   g_tagDoc(_T("Document"), _T("DOC"), 0);
CTraceTag   g_tagDocMenu(_T("Menu"), _T("DOC"), 0);
CTraceTag   g_tagDocNotify(_T("Notify"), _T("DOC NOTIFY"), 0);
CTraceTag   g_tagDocRegNotify(_T("Notify"), _T("DOC REG NOTIFY"), 0);
CTraceTag   g_tagDocRefresh(_T("Document"), _T("REFRESH"), 0);
#endif

/////////////////////////////////////////////////////////////////////////////
// CClusterDoc
/////////////////////////////////////////////////////////////////////////////

IMPLEMENT_DYNCREATE(CClusterDoc, CDocument)

/////////////////////////////////////////////////////////////////////////////
// Message Maps

BEGIN_MESSAGE_MAP(CClusterDoc, CDocument)
    //{{AFX_MSG_MAP(CClusterDoc)
    ON_COMMAND(ID_FILE_NEW_GROUP, OnCmdNewGroup)
    ON_COMMAND(ID_FILE_NEW_RESOURCE, OnCmdNewResource)
    ON_COMMAND(ID_FILE_NEW_NODE, OnCmdNewNode)
    ON_COMMAND(ID_FILE_CONFIG_APP, OnCmdConfigApp)
    ON_COMMAND(ID_VIEW_REFRESH, OnCmdRefresh)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::CClusterDoc
//
//  Routine Description:
//      Default constructor.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
CClusterDoc::CClusterDoc(void)
{
    m_hcluster = NULL;
    m_hkeyCluster = NULL;
    m_pciCluster = NULL;
    m_ptiCluster = NULL;

    m_hmenuCluster = NULL;
    m_hmenuNode = NULL;
    m_hmenuGroup = NULL;
    m_hmenuResource = NULL;
    m_hmenuResType = NULL;
    m_hmenuNetwork = NULL;
    m_hmenuNetIFace = NULL;
    m_hmenuCurrent = NULL;
    m_idmCurrentMenu = 0;

    m_bUpdateFrameNumber = TRUE;
    m_bInitializing = TRUE;
    m_bIgnoreErrors = FALSE;

    m_bClusterAvailable = FALSE;

    EnableAutomation();

}  //*** CClusterDoc::CClusterDoc()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::~CClusterDoc
//
//  Routine Description:
//      Destructor.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
CClusterDoc::~CClusterDoc(void)
{
    // Destroy any menus we loaded.
    if (m_hmenuCluster != NULL)
        DestroyMenu(m_hmenuCluster);
    if (m_hmenuNode != NULL)
        DestroyMenu(m_hmenuNode);
    if (m_hmenuGroup != NULL)
        DestroyMenu(m_hmenuGroup);
    if (m_hmenuResource != NULL)
        DestroyMenu(m_hmenuResource);
    if (m_hmenuResType != NULL)
        DestroyMenu(m_hmenuResType);
    if (m_hmenuNetwork != NULL)
        DestroyMenu(m_hmenuNetwork);
    if (m_hmenuNetIFace != NULL)
        DestroyMenu(m_hmenuNetIFace);

    delete m_pciCluster;

}  //*** CClusterDoc::~CClusterDoc()

/////////////////////////////////////////////////////////////////////////////
// CClusterDoc diagnostics

#ifdef _DEBUG
void CClusterDoc::AssertValid(void) const
{
    CDocument::AssertValid();
}

void CClusterDoc::Dump(CDumpContext& dc) const
{
    CDocument::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::OnOpenDocument
//
//  Routine Description:
//      Open a cluster.
//
//  Arguments:
//      lpszPathName    [IN] Name of the cluster to open.
//
//  Return Value:
//      TRUE            Cluster opened successfully.
//      FALSE           Failed to open the cluster.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CClusterDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
    BOOL        bSuccess    = TRUE;
    CWaitCursor wc;

    ASSERT(Hcluster() == NULL);
    ASSERT(HkeyCluster() == NULL);

    // There better be a cluster name.
    ASSERT(lpszPathName != NULL);
    ASSERT(*lpszPathName != _T('\0'));

    // Display a message on the status bar.
    {
        CString     strStatusBarText;
        strStatusBarText.FormatMessage(IDS_SB_OPENING_CONNECTION, lpszPathName);
        PframeMain()->SetMessageText(strStatusBarText);
        PframeMain()->UpdateWindow();
    }  // Display a message on the status bar

    // If the application is minimized, don't display message boxes
    // on errors.
    m_bIgnoreErrors = AfxGetMainWnd()->IsIconic() == TRUE;

    try
    {
        OnOpenDocumentWorker(lpszPathName);
    }  // try
    catch (CException * pe)
    {
        if (!m_bIgnoreErrors)
            pe->ReportError();
        pe->Delete();
        if (HkeyCluster() != NULL)
        {
            ClusterRegCloseKey(HkeyCluster());
            m_hkeyCluster = NULL;
        }  // if:  cluster registry key is open
        if ((Hcluster() != NULL) && (Hcluster() != GetClusterAdminApp()->HOpenedCluster()))
        {
            CloseCluster(Hcluster());
            m_hcluster = NULL;
        }  // if:  cluster is open
        m_bClusterAvailable = FALSE;
        bSuccess = FALSE;
    }  // catch:  CException

    // Reset the message on the status bar.
    PframeMain()->SetMessageText(AFX_IDS_IDLEMESSAGE);
    PframeMain()->UpdateWindow();

    m_bInitializing = FALSE;

    return bSuccess;

}  //*** CClusterDoc::OnOpenDocument()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::OnOpenDocumentWorker
//
//  Routine Description:
//      Worker function for opening a cluster.
//
//  Arguments:
//      lpszPathName    [IN] Name of the cluster to open.
//
//  Return Value:
//      None.
//
//  Exceptions Thrown:
//      Any exceptions thrown by CString::operator=(), CCluster::new(),
//      CCluster::Init(), BuildBaseHierarchy(), or CollectClusterItems().
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::OnOpenDocumentWorker(LPCTSTR lpszPathName)
{
    // Set the node name to the path name.
    m_strNode = lpszPathName;

    // Delete the contents to start out with an empty document.
    DeleteContents();

    m_bClusterAvailable = TRUE;

    // Create a new cluster object.
    m_pciCluster = new CCluster;
    if ( m_pciCluster == NULL )
    {
        AfxThrowMemoryException();
    } // if: error allocating the cluster object
    PciCluster()->AddRef();
    PciCluster()->Init(this, lpszPathName, GetClusterAdminApp()->HOpenedCluster());

    // Build the base hierarchy.
    BuildBaseHierarchy();

    // Collect the items in the cluster and build the hierarchy.
    CollectClusterItems();

    // Collect network priority list.
    PciCluster()->CollectNetworkPriority(NULL);

    // Open new windows if there were more open when we exited.
    {
        int         iwin;
        int         cwin;
        CString     strSection;

        strSection = REGPARAM_CONNECTIONS _T("\\") + StrNode();
        cwin = AfxGetApp()->GetProfileInt(strSection, REGPARAM_WINDOW_COUNT, 1);
        for (iwin = 1 ; iwin < cwin ; iwin++)
            AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_WINDOW_NEW, NULL);
    }  // Open new windows if there were more open when we exited

    // Initialize the frame window.
    {
        POSITION            pos;
        CView *             pview;
        CSplitterFrame *    pframe;

        pos = GetFirstViewPosition();
        pview = GetNextView(pos);
        ASSERT_VALID(pview);
        pframe = (CSplitterFrame *) pview->GetParentFrame();
        ASSERT_KINDOF(CSplitterFrame, pframe);
        pframe->InitFrame(this);
    }  // Initialize the frame window

}  //*** CClusterDoc::OnOpenDocumentWorker()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::OnCloseDocument
//
//  Routine Description:
//      Close a cluster.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::OnCloseDocument(void)
{
    TraceMenu(g_tagDocMenu, AfxGetMainWnd()->GetMenu(), _T("OnCloseDocument menu: "));
    m_bUpdateFrameNumber = FALSE;
    CDocument::OnCloseDocument();
    TraceMenu(g_tagDocMenu, AfxGetMainWnd()->GetMenu(), _T("Post-OnCloseDocument menu: "));

}  //*** CClusterDoc::OnCloseDocument()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::SaveSettings
//
//  Routine Description:
//      Save settings so they can be restored later.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::SaveSettings(void)
{
    int         cwin = 0;
    POSITION    pos;
    CView *     pview;
    CString     strSection;

    try
    {
        // Save the number of windows open on this document.
        strSection = REGPARAM_CONNECTIONS _T("\\") + StrNode();
        pos = GetFirstViewPosition();
        while (pos != NULL)
        {
            pview = GetNextView(pos);
            ASSERT_VALID(pview);
            if (pview->IsKindOf(RUNTIME_CLASS(CClusterTreeView)))
                cwin++;
        }  // while:  more views in the list
        AfxGetApp()->WriteProfileInt(strSection, REGPARAM_WINDOW_COUNT, cwin);
    }  // try
    catch (CException * pe)
    {
        pe->Delete();
    }  // catch:  CException

}  //*** CClusterDoc::SaveSettings()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::BuildBaseHierarchy
//
//  Routine Description:
//      Build the base hierarchy.  This hierarchy consists of tree items
//      for the hierarchy and list items for what is displayed in the list
//      view but does not contain any items for specific objects, other
//      than the cluster itself.
//
//  Arguments:
//      None.
//
//  Return Value:
//      dwStatus    Status of the operation: 0 if successful, !0 otherwise.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::BuildBaseHierarchy(void)
{
    ASSERT_VALID(PciCluster());
    ASSERT(PtiCluster() == NULL);

    // Create the root cluster item.
    {
        ASSERT_VALID(PciCluster());
        PciCluster()->ReadItem();
        m_ptiCluster = new CTreeItem(NULL, PciCluster());
        if ( m_ptiCluster == NULL )
        {
            AfxThrowMemoryException();
        } // if: error allocating tree item
        m_ptiCluster->AddRef();
        ASSERT_VALID(PtiCluster());
        PciCluster()->AddTreeItem(PtiCluster());
        PtiCluster()->Init();
        PtiCluster()->PcoliAddColumn(IDS_COLTEXT_NAME, COLI_WIDTH_NAME);
//      PtiCluster()->PcoliAddColumn(IDS_COLTEXT_TYPE, COLI_WIDTH_TYPE);
        PtiCluster()->PcoliAddColumn(IDS_COLTEXT_STATE, COLI_WIDTH_STATE);
        PtiCluster()->PcoliAddColumn(IDS_COLTEXT_DESCRIPTION, COLI_WIDTH_DESCRIPTION);
    }  // Create the root cluster item

    // Add the Groups container item under the cluster.
    {
        CTreeItem * ptiGroups;

        // Create the Groups container item.
        ptiGroups = PtiCluster()->PtiAddChild(IDS_TREEITEM_GROUPS);
        ASSERT_VALID(ptiGroups);
        ptiGroups->PcoliAddColumn(IDS_COLTEXT_NAME, COLI_WIDTH_NAME);
//      ptiGroups->PcoliAddColumn(IDS_COLTEXT_TYPE, COLI_WIDTH_TYPE);
        ptiGroups->PcoliAddColumn(IDS_COLTEXT_STATE, COLI_WIDTH_STATE);
        ptiGroups->PcoliAddColumn(IDS_COLTEXT_OWNER, COLI_WIDTH_OWNER);
        ptiGroups->PcoliAddColumn(IDS_COLTEXT_DESCRIPTION, COLI_WIDTH_DESCRIPTION);

    }  // Add the Groups container item under the cluster

    // Add the Resources container item under the cluster.
    {
        CTreeItem * ptiResources;

        // Create the Resources container item.
        ptiResources = PtiCluster()->PtiAddChild(IDS_TREEITEM_RESOURCES);
        ASSERT_VALID(ptiResources);
        ptiResources->PcoliAddColumn(IDS_COLTEXT_NAME, COLI_WIDTH_NAME);
//      ptiResources->PcoliAddColumn(IDS_COLTEXT_TYPE, COLI_WIDTH_TYPE);
        ptiResources->PcoliAddColumn(IDS_COLTEXT_STATE, COLI_WIDTH_STATE);
        ptiResources->PcoliAddColumn(IDS_COLTEXT_OWNER, COLI_WIDTH_OWNER);
        ptiResources->PcoliAddColumn(IDS_COLTEXT_GROUP, COLI_WIDTH_GROUP);
        ptiResources->PcoliAddColumn(IDS_COLTEXT_RESTYPE, COLI_WIDTH_RESTYPE);
        ptiResources->PcoliAddColumn(IDS_COLTEXT_DESCRIPTION, COLI_WIDTH_DESCRIPTION);

    }  // Add the Resources container item under the cluster

    // Add the Cluster Configuration container item under the cluster.
    {
        CTreeItem * ptiClusCfg;

        // Create the Cluster Configuration container item.
        ptiClusCfg = PtiCluster()->PtiAddChild(IDS_TREEITEM_CLUSTER_CONFIG);
        ASSERT_VALID(ptiClusCfg);
        ptiClusCfg->PcoliAddColumn(IDS_COLTEXT_NAME, COLI_WIDTH_NAME);
//      ptiClusCfg->PcoliAddColumn(IDS_COLTEXT_TYPE, COLI_WIDTH_TYPE);
        ptiClusCfg->PcoliAddColumn(IDS_COLTEXT_DESCRIPTION, COLI_WIDTH_DESCRIPTION);

        // Add the Resources Types container item under the Cluster Configuration container.
        {
            CTreeItem * ptiResTypes;

            // Create the Resources Types container item.
            ptiResTypes = ptiClusCfg->PtiAddChild(IDS_TREEITEM_RESTYPES);
            ASSERT_VALID(ptiResTypes);
            ptiResTypes->PcoliAddColumn(IDS_COLTEXT_DISPLAY_NAME, COLI_WIDTH_DISPLAY_NAME);
//          ptiResTypes->PcoliAddColumn(IDS_COLTEXT_NAME, COLI_WIDTH_NAME);
//          ptiResTypes->PcoliAddColumn(IDS_COLTEXT_TYPE, COLI_WIDTH_TYPE);
            ptiResTypes->PcoliAddColumn(IDS_COLTEXT_RESDLL, COLI_WIDTH_RESDLL);
            ptiResTypes->PcoliAddColumn(IDS_COLTEXT_DESCRIPTION, COLI_WIDTH_DESCRIPTION);

        }  // Add the Resources Types container item under the Cluster Configuration container

        // Add the Networks container item under the Cluster Configuration container.
        {
            CTreeItem * ptiNetworks;

            // Create the Networks container item.
            ptiNetworks = ptiClusCfg->PtiAddChild(IDS_TREEITEM_NETWORKS);
            ASSERT_VALID(ptiNetworks);
            ptiNetworks->PcoliAddColumn(IDS_COLTEXT_NAME, COLI_WIDTH_NAME);
//          ptiNetworks->PcoliAddColumn(IDS_COLTEXT_TYPE, COLI_WIDTH_TYPE);
            ptiNetworks->PcoliAddColumn(IDS_COLTEXT_STATE, COLI_WIDTH_STATE);
            ptiNetworks->PcoliAddColumn(IDS_COLTEXT_ROLE, COLI_WIDTH_NET_ROLE);
//          ptiNetworks->PcoliAddColumn(IDS_COLTEXT_ADDRESS, COLI_WIDTH_NET_ADDRESS);
            ptiNetworks->PcoliAddColumn(IDS_COLTEXT_MASK, COLI_WIDTH_NET_MASK);
            ptiNetworks->PcoliAddColumn(IDS_COLTEXT_DESCRIPTION, COLI_WIDTH_DESCRIPTION);

        }  // Add the Networks container item under the Cluster Configuration container

        // Add the Network Interfaces container item under the Cluster Configuration container.
        {
            CTreeItem * ptiNetworkInterfacess;

            // Create the Network Interfaces container item.
            ptiNetworkInterfacess = ptiClusCfg->PtiAddChild(IDS_TREEITEM_NETIFACES);
            ASSERT_VALID(ptiNetworkInterfacess);
            ptiNetworkInterfacess->PcoliAddColumn(IDS_COLTEXT_NODE, COLI_WIDTH_NODE);
            ptiNetworkInterfacess->PcoliAddColumn(IDS_COLTEXT_NETWORK, COLI_WIDTH_NETWORK);
//          ptiNetworkInterfacess->PcoliAddColumn(IDS_COLTEXT_TYPE, COLI_WIDTH_TYPE);
            ptiNetworkInterfacess->PcoliAddColumn(IDS_COLTEXT_STATE, COLI_WIDTH_STATE);
            ptiNetworkInterfacess->PcoliAddColumn(IDS_COLTEXT_ADAPTER, COLI_WIDTH_NET_ADAPTER);
            ptiNetworkInterfacess->PcoliAddColumn(IDS_COLTEXT_ADDRESS, COLI_WIDTH_NET_ADDRESS);
            ptiNetworkInterfacess->PcoliAddColumn(IDS_COLTEXT_DESCRIPTION, COLI_WIDTH_DESCRIPTION);

        }  // Add the Network Interfaces container item under the Cluster Configuration container

    }  // Add the Cluster Configuration container item under the cluster

}  //*** CClusterDoc::BuildBaseHierarchy()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::CollectClusterItems
//
//  Routine Description:
//      Collect items in the cluster.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//  Exceptions Thrown:
//      CNTException        Status from ClusterOpenEnum or ClusterEnum.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::CollectClusterItems(void)
{
    DWORD           dwStatus;
    HCLUSENUM       hclusenum;
    ClusEnumType    cet;
    int             ienum;
    LPWSTR          pwszName = NULL;
    DWORD           cchName;
    DWORD           cchmacName;

    // Open the enumeration.
    hclusenum = ClusterOpenEnum(
                        Hcluster(),
                        ( CLUSTER_ENUM_NODE
                        | CLUSTER_ENUM_GROUP
                        | CLUSTER_ENUM_RESOURCE
                        | CLUSTER_ENUM_RESTYPE
                        | CLUSTER_ENUM_NETWORK
                        | CLUSTER_ENUM_NETINTERFACE
                        )
                        );
    if (hclusenum == NULL)
        ThrowStaticException(GetLastError(), IDS_OPEN_CLUSTER_ENUM_ERROR, StrName());

    try
    {
        // Allocate a buffer for object names.
        cchmacName = 128;
        pwszName = new WCHAR[cchmacName];
        if ( pwszName == NULL )
        {
            AfxThrowMemoryException();
        } // if: error allocating the name buffer

        // Loop through the enumeration and add each item to the appropriate list.
        for (ienum = 0 ; ; ienum++)
        {
            cchName = cchmacName;
            dwStatus = ClusterEnum(hclusenum, ienum, &cet, pwszName, &cchName);
            if (dwStatus == ERROR_MORE_DATA)
            {
                Trace(g_tagDoc, _T("OnOpenDocument() - name buffer too small.  Expanding from %d to %d"), cchmacName, cchName);
                delete [] pwszName;
                pwszName = NULL;
                cchmacName = cchName + 1;
                pwszName = new WCHAR[cchmacName];
                if ( pwszName == NULL )
                {
                    AfxThrowMemoryException();
                } // if: error allocating the name buffer
                cchName = cchmacName;
                dwStatus = ClusterEnum(hclusenum, ienum, &cet, pwszName, &cchName);
            }  // if:  name buffer was too small
            if (dwStatus == ERROR_NO_MORE_ITEMS)
                break;
            else if (dwStatus != ERROR_SUCCESS)
                ThrowStaticException(dwStatus, IDS_ENUM_CLUSTER_ERROR, StrName());

            switch (cet)
            {
                case CLUSTER_ENUM_NODE:
                    PciAddNewNode(pwszName);
                    break;

                case CLUSTER_ENUM_GROUP:
                    PciAddNewGroup(pwszName);
                    break;

                case CLUSTER_ENUM_RESOURCE:
                    PciAddNewResource(pwszName);
                    break;

                case CLUSTER_ENUM_RESTYPE:
                    PciAddNewResourceType(pwszName);
                    break;

                case CLUSTER_ENUM_NETWORK:
                    PciAddNewNetwork(pwszName);
                    break;

                case CLUSTER_ENUM_NETINTERFACE:
                    PciAddNewNetInterface(pwszName);
                    break;

                default:
                    Trace(g_tagDoc, _T("OnOpenDocument() - Unknown cluster enumeration type '%d'"), cet);
                    ASSERT(0);
                    break;

            }  // switch:  cet
        }  // for:  each item enumerated

        // Initialize all the cluster items.
        InitNodes();
        InitGroups();
        InitResourceTypes();
        InitResources();
        InitNetworks();
        InitNetInterfaces();

        // Deallocate our name buffer.
        delete [] pwszName;

        // Close the enumerator.
        ClusterCloseEnum(hclusenum);

    }  // try
    catch (CException *)
    {
        delete [] pwszName;
        ClusterCloseEnum(hclusenum);
        throw;
    }  // catch:  any exception

}  //*** CClusterDoc::CollectClusterItems()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::PciAddNewNode
//
//  Routine Description:
//      Add a new node to the list of nodes.
//
//  Arguments:
//      pszName     [IN] Name of the node.
//
//  Return Value:
//      pci         Cluster item for the new node.
//
//--
/////////////////////////////////////////////////////////////////////////////
CClusterNode * CClusterDoc::PciAddNewNode(IN LPCTSTR pszName)
{
    CClusterNode *  pciNewNode = NULL;
    CClusterNode *  pciRetNode = NULL;
    CClusterNode *  pciOldNode = NULL;
    CActiveGroups * pciActiveGroups = NULL;
    CTreeItem *     ptiActiveGroups = NULL;

    ASSERT(pszName != NULL);
    ASSERT(*pszName != NULL);
    ASSERT_VALID(PtiCluster());
    ASSERT(LpciNodes().PciNodeFromName(pszName) == NULL);

    // Display a message on the status bar.
    {
        CString     strStatusBarText;
        strStatusBarText.FormatMessage(IDS_SB_ADDING_NODE, pszName, StrNode());
        PframeMain()->SetMessageText(strStatusBarText);
        PframeMain()->UpdateWindow();
    }  // Display a message on the status bar

    try
    {
        // If there is an item with that name, delete it.
        pciOldNode = LpciNodes().PciNodeFromName(pszName);
        if (pciOldNode != NULL)
        {
            pciOldNode->Delete();
            pciOldNode = NULL;
        }  // if:  already an item with that name

        // Allocate a new node.
        pciNewNode = new CClusterNode;
        if ( pciNewNode == NULL )
        {
            AfxThrowMemoryException();
        } // if: error allocating the node

        // Add a reference while we are working on it to prevent a delete
        // notification from taking us out.
        pciNewNode->AddRef();

        // Initialize the node.
        pciNewNode->Init(this, pszName);
    }  // try
    catch (CNTException * pnte)
    {
        if (pnte->Sc() == RPC_S_CALL_FAILED)
        {
            if (!m_bIgnoreErrors)
                pnte->ReportError();
            delete pciNewNode;
            throw;
        }  // if:  RPC call failed error
        else if (pnte->Sc() != ERROR_FILE_NOT_FOUND)
        {
            ID id = IdProcessNewObjectError(pnte);
            if (id == IDNO)
            {
                delete pciNewNode;
                throw;
            }  // if:  user doesn't want to ignore error
        }  // else if:  error is NOT node not found
        else
        {
            delete pciNewNode;
            pnte->Delete();
            return NULL;
        }  // else:  object not found
        pnte->Delete();
    }  // catch:  CNTException
    catch (CException * pe)
    {
        ID id = IdProcessNewObjectError(pe);
        if (id == IDNO)
        {
            delete pciNewNode;
            throw;
        }  // if:  user doesn't want to ignore error
        pe->Delete();
        if (pciNewNode == NULL)
            return NULL;
    }  // catch:  CException

    try
    {
        // Add the node to the list.
        {
            POSITION        posPci;
            POSITION        posCurPci;

            posPci = LpciNodes().GetHeadPosition();
            while (posPci != NULL)
            {
                posCurPci = posPci;
                pciOldNode = (CClusterNode *) LpciNodes().GetNext(posPci);
                ASSERT_VALID(pciOldNode);
                if (pciOldNode->StrName().CompareNoCase(pszName) > 0)
                {
                    LpciNodes().InsertBefore(posCurPci, pciNewNode);
                    break;
                }  // if:  new node before this node
                pciOldNode = NULL;
            }  // while:  more items in the list
            if (pciOldNode == NULL)
                LpciNodes().AddTail(pciNewNode);
        }  // Add the node to the list

        // Save this node as a return value now that we have added it to the list
        pciRetNode = pciNewNode;
        pciNewNode = NULL;

        // Insert the item in the tree.
        {
            CTreeItem *     ptiNode;
            CTreeItem *     ptiChild;

            ptiNode = PtiCluster()->PtiAddChildBefore(pciOldNode, pciRetNode);
            ASSERT_VALID(ptiNode);
            ptiNode->PcoliAddColumn(IDS_COLTEXT_NAME, COLI_WIDTH_NAME);

            // Add the Active Groups container under the node.
            {
                CString     strName;

                // Create the Active Groups container cluster item.
                strName.LoadString(IDS_TREEITEM_ACTIVEGROUPS);
                pciActiveGroups = new CActiveGroups;
                if ( pciActiveGroups == NULL )
                {
                    AfxThrowMemoryException();
                } // if: Error allocating the active groups objct
                pciActiveGroups->Init(this, strName, pciRetNode);

                // Add the tree item for the container.
                ptiActiveGroups = ptiNode->PtiAddChild(pciActiveGroups, TRUE /*bTakeOwnership*/);
                ASSERT_VALID(ptiActiveGroups);
                ptiActiveGroups->PcoliAddColumn(IDS_COLTEXT_NAME, COLI_WIDTH_NAME);
//              ptiActiveGroups->PcoliAddColumn(IDS_COLTEXT_TYPE, COLI_WIDTH_TYPE);
                ptiActiveGroups->PcoliAddColumn(IDS_COLTEXT_STATE, COLI_WIDTH_STATE);
                ptiActiveGroups->PcoliAddColumn(IDS_COLTEXT_OWNER, COLI_WIDTH_OWNER);
                ptiActiveGroups->PcoliAddColumn(IDS_COLTEXT_DESCRIPTION, COLI_WIDTH_DESCRIPTION);

            }  // Add the Active Groups container under the node.

            // Add the Active Resources container under the node.
            {
                ptiChild = ptiNode->PtiAddChild(IDS_TREEITEM_ACTIVERESOURCES);
                ASSERT_VALID(ptiChild);
                ptiChild->PcoliAddColumn(IDS_COLTEXT_NAME, COLI_WIDTH_NAME);
//              ptiChild->PcoliAddColumn(IDS_COLTEXT_TYPE, COLI_WIDTH_TYPE);
                ptiChild->PcoliAddColumn(IDS_COLTEXT_STATE, COLI_WIDTH_STATE);
                ptiChild->PcoliAddColumn(IDS_COLTEXT_OWNER, COLI_WIDTH_OWNER);
                ptiChild->PcoliAddColumn(IDS_COLTEXT_GROUP, COLI_WIDTH_GROUP);
                ptiChild->PcoliAddColumn(IDS_COLTEXT_RESTYPE, COLI_WIDTH_RESTYPE);
                ptiChild->PcoliAddColumn(IDS_COLTEXT_DESCRIPTION, COLI_WIDTH_DESCRIPTION);

            }  // Add the Active Resources container under the node.

            // Add the Network Interfaces container under the node.
            {
                ptiChild = ptiNode->PtiAddChild(IDS_TREEITEM_NETIFACES);
                ASSERT_VALID(ptiChild);
                ptiChild->PcoliAddColumn(IDS_COLTEXT_NODE, COLI_WIDTH_NODE);
                ptiChild->PcoliAddColumn(IDS_COLTEXT_NETWORK, COLI_WIDTH_NETWORK);
//              ptiChild->PcoliAddColumn(IDS_COLTEXT_TYPE, COLI_WIDTH_TYPE);
                ptiChild->PcoliAddColumn(IDS_COLTEXT_STATE, COLI_WIDTH_STATE);
                ptiChild->PcoliAddColumn(IDS_COLTEXT_ADAPTER, COLI_WIDTH_NET_ADAPTER);
                ptiChild->PcoliAddColumn(IDS_COLTEXT_ADDRESS, COLI_WIDTH_NET_ADDRESS);
                ptiChild->PcoliAddColumn(IDS_COLTEXT_DESCRIPTION, COLI_WIDTH_DESCRIPTION);
            }  // Add the Network Interfaces container under the node

            // Add the Physical Devices container under the node.
            {
//              ptiChild = ptiNode->PtiAddChild(IDS_TREEITEM_PHYSDEVS);
//              ASSERT_VALID(ptiChild);
//              AddDefaultColumns(ptiChild);

            }  // Add the Physical Devices container under the node.
        }  // Insert the item in the tree
    }  // try
    catch (CException * pe)
    {
        // If the Active Groups container has been created, clean up the
        // reference to the node object we are creating.  If the tree
        // item hasn't been created yet, we still own the cluster item,
        // so delete that as well.
        if (pciActiveGroups != NULL)
        {
            pciActiveGroups->Cleanup();
            if (ptiActiveGroups == NULL)
                delete pciActiveGroups;
        }  // if:  Active Groups container created
        delete pciNewNode;
        ID id = IdProcessNewObjectError(pe);
        if (id == IDNO)
            throw;
        pe->Delete();
    }  // catch:  CException

    if (pciRetNode != NULL)
        pciRetNode->Release();

    return pciRetNode;

}  //*** CClusterDoc::PciAddNewNode()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::PciAddNewGroup
//
//  Routine Description:
//      Add a new group to the list of groups.
//
//  Arguments:
//      pszName     [IN] Name of the group.
//
//  Return Value:
//      pci         Cluster item for the new group.
//
//--
/////////////////////////////////////////////////////////////////////////////
CGroup * CClusterDoc::PciAddNewGroup(IN LPCTSTR pszName)
{
    CGroup *    pciNewGroup = NULL;
    CGroup *    pciRetGroup = NULL;
    CGroup *    pciOldGroup = NULL;

    ASSERT(pszName != NULL);
    ASSERT(*pszName != NULL);

    // Display a message on the status bar.
    {
        CString     strStatusBarText;
        strStatusBarText.FormatMessage(IDS_SB_ADDING_GROUP, pszName, StrNode());
        PframeMain()->SetMessageText(strStatusBarText);
        PframeMain()->UpdateWindow();
    }  // Display a message on the status bar

    try
    {
        // If there is an item with that name, delete it.
        pciOldGroup = LpciGroups().PciGroupFromName(pszName);
        if (pciOldGroup != NULL)
        {
            Trace(g_tagGroup, _T("Deleting existing group '%s' (%x) before adding new instance"), pszName, pciOldGroup);
            pciOldGroup->Delete();
            pciOldGroup = NULL;
        }  // if:  already an item with that name

        // Allocate a new group.
        pciNewGroup = new CGroup;
        if ( pciNewGroup == NULL )
        {
            AfxThrowMemoryException();
        } // if: error allocating the group object

        // Add a reference while we are working on it to prevent a delete
        // notification from taking us out.
        pciNewGroup->AddRef();

        // Initialize the group and add it to the list.
        pciNewGroup->Init(this, pszName);
    }  // try
    catch (CNTException * pnte)
    {
        if (pnte->Sc() == RPC_S_CALL_FAILED)
        {
            if (!m_bIgnoreErrors)
                pnte->ReportError();
            delete pciNewGroup;
            throw;
        }  // if:  RPC call failed error
        else if (pnte->Sc() != ERROR_GROUP_NOT_FOUND)
        {
            ID id = IdProcessNewObjectError(pnte);
            if (id == IDNO)
            {
                delete pciNewGroup;
                throw;
            }  // if:  user doesn't want to ignore error
        }  // else if:  error is NOT group not found
        else
        {
            delete pciNewGroup;
            pnte->Delete();
            return NULL;
        }  // else:  object not found
        pnte->Delete();
    }  // catch:  CNTException
    catch (CException * pe)
    {
        ID id = IdProcessNewObjectError(pe);
        if (id == IDNO)
        {
            delete pciNewGroup;
            throw;
        }  // if:  user doesn't want to ignore error
        pe->Delete();
        if (pciNewGroup == NULL)
            return NULL;
    }  // catch:  CException

    try
    {
        // Add the group to the list.
        {
            POSITION    posPci;
            POSITION    posCurPci;

            posPci = LpciGroups().GetHeadPosition();
            while (posPci != NULL)
            {
                posCurPci = posPci;
                pciOldGroup = (CGroup *) LpciGroups().GetNext(posPci);
                ASSERT_VALID(pciOldGroup);
                if (pciOldGroup->StrName().CompareNoCase(pszName) > 0)
                {
                    LpciGroups().InsertBefore(posCurPci, pciNewGroup);
                    break;
                }  // if:  new group before this group
                pciOldGroup = NULL;
            }  // while:  more items in the list
            if (pciOldGroup == NULL)
                LpciGroups().AddTail(pciNewGroup);
        }  // Add the group to the list

        // Save this group as a return value now that we have added it to the list
        pciRetGroup = pciNewGroup;
        pciNewGroup = NULL;

        // Insert the item in the tree.
        {
            CTreeItem *     ptiGroups;
            CTreeItem *     ptiGroup;

            // Find the Groups container tree item.
            ptiGroups = PtiCluster()->PtiChildFromName(IDS_TREEITEM_GROUPS);
            ASSERT_VALID(ptiGroups);

            // Add the item before the found item we inserted it into in the groups list.
            ptiGroup = ptiGroups->PtiAddChildBefore(pciOldGroup, pciRetGroup);
            ASSERT_VALID(ptiGroup);
            ptiGroup->PcoliAddColumn(IDS_COLTEXT_NAME, COLI_WIDTH_NAME);
//          ptiGroup->PcoliAddColumn(IDS_COLTEXT_TYPE, COLI_WIDTH_TYPE);
            ptiGroup->PcoliAddColumn(IDS_COLTEXT_STATE, COLI_WIDTH_STATE);
            ptiGroup->PcoliAddColumn(IDS_COLTEXT_OWNER, COLI_WIDTH_OWNER);
            ptiGroup->PcoliAddColumn(IDS_COLTEXT_RESTYPE, COLI_WIDTH_RESTYPE);
            ptiGroup->PcoliAddColumn(IDS_COLTEXT_DESCRIPTION, COLI_WIDTH_DESCRIPTION);

        }  // Insert the item in the tree
    }  // try
    catch (CException * pe)
    {
        delete pciNewGroup;
        ID id = IdProcessNewObjectError(pe);
        if (id == IDNO)
            throw;
        pe->Delete();
    }  // catch:  CException

    if (pciRetGroup != NULL)
        pciRetGroup->Release();

    return pciRetGroup;

}  //*** CClusterDoc::PciAddNewGroup()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::PciAddNewResource
//
//  Routine Description:
//      Add a new resource to the list of groups.
//
//  Arguments:
//      pszName     [IN] Name of the resource.
//
//  Return Value:
//      pci         Cluster item for the new resource.
//
//--
/////////////////////////////////////////////////////////////////////////////
CResource * CClusterDoc::PciAddNewResource(IN LPCTSTR pszName)
{
    CResource * pciNewRes = NULL;
    CResource * pciRetRes = NULL;
    CResource * pciOldRes;

    ASSERT(pszName != NULL);
    ASSERT(*pszName != NULL);

    // Display a message on the status bar.
    {
        CString     strStatusBarText;
        strStatusBarText.FormatMessage(IDS_SB_ADDING_RESOURCE, pszName, StrNode());
        PframeMain()->SetMessageText(strStatusBarText);
        PframeMain()->UpdateWindow();
    }  // Display a message on the status bar

    try
    {
        // If there is an item with that name, delete it.
        pciOldRes = LpciResources().PciResFromName(pszName);
        if (pciOldRes != NULL)
        {
            if (pciOldRes->BInitializing())
                return pciOldRes;
            Trace(g_tagResource, _T("Deleting existing resource '%s' (%x) before adding new instance"), pszName, pciOldRes);
            pciOldRes->Delete();
            pciOldRes = NULL;
        }  // if:  already an item with that name

        // Allocate a new resource.
        pciNewRes = new CResource;
        if ( pciNewRes == NULL )
        {
            AfxThrowMemoryException();
        } // if: error allocating the resource object

        // Add a reference while we are working on it to prevent a delete
        // notification from taking us out.
        pciNewRes->AddRef();

        // Initialize the resource and add it to the list.
        pciNewRes->Init(this, pszName);
    }  // try
    catch (CNTException * pnte)
    {
        //DebugBreak();
        if (pnte->Sc() == RPC_S_CALL_FAILED)
        {
            if (!m_bIgnoreErrors)
                pnte->ReportError();
            delete pciNewRes;
            throw;
        }  // if:  RPC call failed error
        else if (pnte->Sc() != ERROR_RESOURCE_NOT_FOUND)
        {
            ID id = IdProcessNewObjectError(pnte);
            if (id == IDNO)
            {
                delete pciNewRes;
                throw;
            }  // if:  user doesn't want to ignore error
        }  // else if:  error is NOT resource not found
        else
        {
            delete pciNewRes;
            pnte->Delete();
            return NULL;
        }  // else:  object not found
        pnte->Delete();
    }  // catch:  CNTException
    catch (CException * pe)
    {
        //DebugBreak();
        ID id = IdProcessNewObjectError(pe);
        if (id == IDNO)
        {
            delete pciNewRes;
            throw;
        }  // if:  user doesn't want to ignore error
        pe->Delete();
        if (pciNewRes == NULL)
            return NULL;
    }  // catch:  CException

    try
    {
        // Add the resource to the list.
        {
            POSITION    posPci;
            POSITION    posCurPci;
            CResource * pciOldRes   = NULL;

            posPci = LpciResources().GetHeadPosition();
            while (posPci != NULL)
            {
                posCurPci = posPci;
                pciOldRes = (CResource *) LpciResources().GetNext(posPci);
                ASSERT_VALID(pciOldRes);
                if (pciOldRes->StrName().CompareNoCase(pszName) > 0)
                {
                    LpciResources().InsertBefore(posCurPci, pciNewRes);
                    break;
                }  // if:  new resource before this resource
                pciOldRes = NULL;
            }  // while:  more items in the list
            if (pciOldRes == NULL)
                LpciResources().AddTail(pciNewRes);
        }  // Add the resource to the list

        // Save this resource as a return value now that we have added it to the list
        pciRetRes = pciNewRes;
        pciNewRes = NULL;

        // Insert the item in the tree.
        {
            CTreeItem *     ptiResources;

            // Find the Resources container tree item.
            ptiResources = PtiCluster()->PtiChildFromName(IDS_TREEITEM_RESOURCES);
            ASSERT_VALID(ptiResources);

            // Add the item to the list of children.
            VERIFY(ptiResources->PliAddChild(pciRetRes) != NULL);

        }  // Insert the item in the tree
    }  // try
    catch (CException * pe)
    {
        delete pciNewRes;
        ID id = IdProcessNewObjectError(pe);
        if (id == IDNO)
            throw;
        pe->Delete();
    }  // catch:  CException

    if (pciRetRes != NULL)
        pciRetRes->Release();

    return pciRetRes;

}  //*** CClusterDoc::PciAddNewResource()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::PciAddNewResourceType
//
//  Routine Description:
//      Add a new resource type to the list of groups.
//
//  Arguments:
//      pszName     [IN] Name of the resource type.
//
//  Return Value:
//      pci         Cluster item for the new resource type.
//
//--
/////////////////////////////////////////////////////////////////////////////
CResourceType * CClusterDoc::PciAddNewResourceType(IN LPCTSTR pszName)
{
    CResourceType * pciNewResType = NULL;
    CResourceType * pciRetResType = NULL;
    CResourceType * pciOldResType;

    ASSERT(pszName != NULL);
    ASSERT(*pszName != NULL);
    ASSERT(LpciResourceTypes().PciResTypeFromName(pszName) == NULL);

    // Display a message on the status bar.
    {
        CString     strStatusBarText;
        strStatusBarText.FormatMessage(IDS_SB_ADDING_RESTYPE, pszName, StrNode());
        PframeMain()->SetMessageText(strStatusBarText);
        PframeMain()->UpdateWindow();
    }  // Display a message on the status bar

    try
    {
        // If there is an item with that name, delete it.
        pciOldResType = LpciResourceTypes().PciResTypeFromName(pszName);
        if (pciOldResType != NULL)
        {
            pciOldResType->Delete();
            pciOldResType = NULL;
        }  // if:  already an item with that name

        // Allocate a new resource type.
        pciNewResType = new CResourceType;
        if ( pciNewResType == NULL )
        {
            AfxThrowMemoryException();
        } // if: error allocating the resource type object

        // Add a reference while we are working on it to prevent a delete
        // notification from taking us out.
        pciNewResType->AddRef();

        // Initialize the resource type and add it to the list.
        pciNewResType->Init(this, pszName);
    }  // try
    catch (CNTException * pnte)
    {
        //DebugBreak();
        if (pnte->Sc() == RPC_S_CALL_FAILED)
        {
            if (!m_bIgnoreErrors)
                pnte->ReportError();
            delete pciNewResType;
            throw;
        }  // if:  RPC call failed error
        else if (pnte->Sc() != ERROR_FILE_NOT_FOUND)
        {
            ID id = IdProcessNewObjectError(pnte);
            if (id == IDNO)
            {
                delete pciNewResType;
                throw;
            }  // if:  user doesn't want to ignore error
        }  // else if:  error is NOT resource type not found
        else
        {
            delete pciNewResType;
            pnte->Delete();
            return NULL;
        }  // else:  object not found
        pnte->Delete();
    }  // catch:  CNTException
    catch (CException * pe)
    {
        //DebugBreak();

        ID id = IdProcessNewObjectError(pe);
        if (id == IDNO)
        {
            delete pciNewResType;
            throw;
        }  // if:  user doesn't want to ignore error
        pe->Delete();
        if (pciNewResType == NULL)
            return NULL;
    }  // catch:  CException

    try
    {
        // Add the resource type to the list.
        {
            POSITION        posPci;
            POSITION        posCurPci;
            CResourceType * pciOldResType   = NULL;

            posPci = LpciResourceTypes().GetHeadPosition();
            while (posPci != NULL)
            {
                posCurPci = posPci;
                pciOldResType = (CResourceType *) LpciResourceTypes().GetNext(posPci);
                ASSERT_VALID(pciOldResType);
                if (pciOldResType->StrName().CompareNoCase(pszName) > 0)
                {
                    LpciResourceTypes().InsertBefore(posCurPci, pciNewResType);
                    break;
                }  // if:  new resource type before this resource type
                pciOldResType = NULL;
            }  // while:  more items in the list
            if (pciOldResType == NULL)
                LpciResourceTypes().AddTail(pciNewResType);
        }  // Add the resource type to the list

        // Save this resource type as a return value now that we have added it to the list
        pciRetResType = pciNewResType;
        pciNewResType = NULL;

        // Insert the item in the tree.
        {
            CTreeItem *     ptiClusCfg;
            CTreeItem *     ptiResTypes;

            // Find the Resource Types container tree item.
            ptiClusCfg = PtiCluster()->PtiChildFromName(IDS_TREEITEM_CLUSTER_CONFIG);
            ASSERT_VALID(ptiClusCfg);
            ptiResTypes = ptiClusCfg->PtiChildFromName(IDS_TREEITEM_RESTYPES);
            ASSERT_VALID(ptiResTypes);

            // Add the item to the list of children.
            VERIFY(ptiResTypes->PliAddChild(pciRetResType) != NULL);

        }  // Insert the item in the tree
    }  // try
    catch (CException * pe)
    {
        delete pciNewResType;
        ID id = IdProcessNewObjectError(pe);
        if (id == IDNO)
            throw;
        pe->Delete();
    }  // catch:  CException

    if (pciRetResType != NULL)
        pciRetResType->Release();

    return pciRetResType;

}  //*** CClusterDoc::PciAddNewResourceType()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::PciAddNewNetwork
//
//  Routine Description:
//      Add a new network to the list of networks.
//
//  Arguments:
//      pszName     [IN] Name of the networks.
//
//  Return Value:
//      pci         Cluster item for the new network.
//
//--
/////////////////////////////////////////////////////////////////////////////
CNetwork * CClusterDoc::PciAddNewNetwork(IN LPCTSTR pszName)
{
    CNetwork *  pciNewNetwork = NULL;
    CNetwork *  pciRetNetwork = NULL;
    CNetwork *  pciOldNetwork = NULL;

    ASSERT(pszName != NULL);
    ASSERT(*pszName != NULL);
    ASSERT(LpciNetworks().PciNetworkFromName(pszName) == NULL);

    // Display a message on the status bar.
    {
        CString     strStatusBarText;
        strStatusBarText.FormatMessage(IDS_SB_ADDING_NETWORK, pszName, StrNode());
        PframeMain()->SetMessageText(strStatusBarText);
        PframeMain()->UpdateWindow();
    }  // Display a message on the status bar

    try
    {
        // If there is an item with that name, delete it.
        pciOldNetwork = LpciNetworks().PciNetworkFromName(pszName);
        if (pciOldNetwork != NULL)
        {
            Trace(g_tagNetwork, _T("Deleting existing network '%s' (%x) before adding new instance"), pszName, pciOldNetwork);
            pciOldNetwork->Delete();
            pciOldNetwork = NULL;
        }  // if:  already an item with that name

        // Allocate a new network.
        pciNewNetwork = new CNetwork;
        if ( pciNewNetwork == NULL )
        {
            AfxThrowMemoryException();
        } // if: error allocating the network object

        // Add a reference while we are working on it to prevent a delete
        // notification from taking us out.
        pciNewNetwork->AddRef();

        // Initialize the network.
        pciNewNetwork->Init(this, pszName);
    }  // try
    catch (CNTException * pnte)
    {
        if (pnte->Sc() == RPC_S_CALL_FAILED)
        {
            if (!m_bIgnoreErrors)
                pnte->ReportError();
            delete pciNewNetwork;
            throw;
        }  // if:  RPC call failed error
        ID id = IdProcessNewObjectError(pnte);
        if (id == IDNO)
        {
            delete pciNewNetwork;
            throw;
        }  // if:  user doesn't want to ignore error
        pnte->Delete();
    }  // catch:  CNTException
    catch (CException * pe)
    {
        ID id = IdProcessNewObjectError(pe);
        if (id == IDNO)
        {
            delete pciNewNetwork;
            throw;
        }  // if:  user doesn't want to ignore error
        pe->Delete();
        if (pciNewNetwork == NULL)
            return NULL;
    }  // catch:  CException

    try
    {
        // Add the network to the list.
        {
            POSITION    posPci;
            POSITION    posCurPci;

            posPci = LpciNetworks().GetHeadPosition();
            while (posPci != NULL)
            {
                posCurPci = posPci;
                pciOldNetwork = (CNetwork *) LpciNetworks().GetNext(posPci);
                ASSERT_VALID(pciOldNetwork);
                if (pciOldNetwork->StrName().CompareNoCase(pszName) > 0)
                {
                    LpciNetworks().InsertBefore(posCurPci, pciNewNetwork);
                    break;
                }  // if:  new network before this network
                pciOldNetwork = NULL;
            }  // while:  more items in the list
            if (pciOldNetwork == NULL)
                LpciNetworks().AddTail(pciNewNetwork);
        }  // Add the network to the list

        // Save this network as a return value now that we have added it to the list
        pciRetNetwork = pciNewNetwork;
        pciNewNetwork = NULL;

        // Insert the item in the tree.
        {
            CTreeItem *     ptiClusCfg;
            CTreeItem *     ptiNetworks;
            CTreeItem *     ptiNetwork;

            // Find the Networks container tree item.
            ptiClusCfg = PtiCluster()->PtiChildFromName(IDS_TREEITEM_CLUSTER_CONFIG);
            ASSERT_VALID(ptiClusCfg);
            ptiNetworks = ptiClusCfg->PtiChildFromName(IDS_TREEITEM_NETWORKS);
            ASSERT_VALID(ptiNetworks);

            // Add the item before the found item we inserted it into in the networks list.
            ptiNetwork = ptiNetworks->PtiAddChildBefore(pciOldNetwork, pciRetNetwork);
            ASSERT_VALID(ptiNetwork);
            ptiNetwork->PcoliAddColumn(IDS_COLTEXT_NODE, COLI_WIDTH_NODE);
            ptiNetwork->PcoliAddColumn(IDS_COLTEXT_NETWORK, COLI_WIDTH_NETWORK);
//          ptiNetwork->PcoliAddColumn(IDS_COLTEXT_TYPE, COLI_WIDTH_TYPE);
            ptiNetwork->PcoliAddColumn(IDS_COLTEXT_STATE, COLI_WIDTH_STATE);
            ptiNetwork->PcoliAddColumn(IDS_COLTEXT_ADAPTER, COLI_WIDTH_NET_ADAPTER);
            ptiNetwork->PcoliAddColumn(IDS_COLTEXT_ADDRESS, COLI_WIDTH_NET_ADDRESS);
            ptiNetwork->PcoliAddColumn(IDS_COLTEXT_DESCRIPTION, COLI_WIDTH_DESCRIPTION);

        }  // Insert the item in the tree
    }  // try
    catch (CException * pe)
    {
        delete pciNewNetwork;
        ID id = IdProcessNewObjectError(pe);
        if (id == IDNO)
            throw;
        pe->Delete();
    }  // catch:  CException

    if (pciRetNetwork != NULL)
        pciRetNetwork->Release();

    return pciRetNetwork;

}  //*** CClusterDoc::PciAddNewNetwork()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::PciAddNewNetInterface
//
//  Routine Description:
//      Add a new network interfaces to the list of network interfaces.
//
//  Arguments:
//      pszName     [IN] Name of the network interface.
//
//  Return Value:
//      pci         Cluster item for the new network interface.
//
//--
/////////////////////////////////////////////////////////////////////////////
CNetInterface * CClusterDoc::PciAddNewNetInterface(IN LPCTSTR pszName)
{
    CNetInterface * pciNewNetIFace = NULL;
    CNetInterface * pciRetNetIFace = NULL;
    CNetInterface * pciOldNetIFace;

    ASSERT(pszName != NULL);
    ASSERT(*pszName != NULL);
    ASSERT(LpciNetInterfaces().PciNetInterfaceFromName(pszName) == NULL);

    // Display a message on the status bar.
    {
        CString     strStatusBarText;
        strStatusBarText.FormatMessage(IDS_SB_ADDING_NETIFACE, pszName, StrNode());
        PframeMain()->SetMessageText(strStatusBarText);
        PframeMain()->UpdateWindow();
    }  // Display a message on the status bar

    try
    {
        // If there is an item with that name, delete it.
        pciOldNetIFace = LpciNetInterfaces().PciNetInterfaceFromName(pszName);
        if (pciOldNetIFace != NULL)
        {
            pciOldNetIFace->Delete();
            pciOldNetIFace = NULL;
        }  // if:  already an item with that name

        // Allocate a new network interface.
        pciNewNetIFace = new CNetInterface;
        if ( pciNewNetIFace == NULL )
        {
            AfxThrowMemoryException();
        } // if: error allocating the net interface object

        // Add a reference while we are working on it to prevent a delete
        // notification from taking us out.
        pciNewNetIFace->AddRef();

        // Initialize the network interface.
        pciNewNetIFace->Init(this, pszName);
    }  // try
    catch (CNTException * pnte)
    {
        if (pnte->Sc() == RPC_S_CALL_FAILED)
        {
            if (!m_bIgnoreErrors)
                pnte->ReportError();
            delete pciNewNetIFace;
            throw;
        }  // if:  RPC call failed error
        ID id = IdProcessNewObjectError(pnte);
        if (id == IDNO)
        {
            delete pciNewNetIFace;
            throw;
        }  // if:  user doesn't want to ignore error
        pnte->Delete();
    }  // catch:  CNTException
    catch (CException * pe)
    {
        ID id = IdProcessNewObjectError(pe);
        if (id == IDNO)
        {
            delete pciNewNetIFace;
            throw;
        }  // if:  user doesn't want to ignore error
        pe->Delete();
        if (pciNewNetIFace == NULL)
            return NULL;
    }  // catch:  CException

    try
    {
        // Add the network interface to the list.
        {
            POSITION        posPci;
            POSITION        posCurPci;
            CNetInterface * pciOldNetIFace  = NULL;

            posPci = LpciNetInterfaces().GetHeadPosition();
            while (posPci != NULL)
            {
                posCurPci = posPci;
                pciOldNetIFace = (CNetInterface *) LpciNetInterfaces().GetNext(posPci);
                ASSERT_VALID(pciOldNetIFace);
                if (pciOldNetIFace->StrName().CompareNoCase(pszName) > 0)
                {
                    LpciNetInterfaces().InsertBefore(posCurPci, pciNewNetIFace);
                    break;
                }  // if:  new network interfaces before this network interface
                pciOldNetIFace = NULL;
            }  // while:  more items in the list
            if (pciOldNetIFace == NULL)
                LpciNetInterfaces().AddTail(pciNewNetIFace);
        }  // Add the network interface to the list

        // Save this network interface as a return value now that we have added it to the list
        pciRetNetIFace = pciNewNetIFace;
        pciNewNetIFace = NULL;

        // Insert the item in the tree.
        {
            CTreeItem *     ptiClusCfg;
            CTreeItem *     ptiNetIFaces;

            // Find the Network Interfaces container tree item.
            ptiClusCfg = PtiCluster()->PtiChildFromName(IDS_TREEITEM_CLUSTER_CONFIG);
            ASSERT_VALID(ptiClusCfg);
            ptiNetIFaces = ptiClusCfg->PtiChildFromName(IDS_TREEITEM_NETIFACES);
            ASSERT_VALID(ptiNetIFaces);

            // Add the item to the list of children.
            VERIFY(ptiNetIFaces->PliAddChild(pciRetNetIFace) != NULL);

        }  // Insert the item in the tree
    }  // try
    catch (CException * pe)
    {
        delete pciNewNetIFace;
        ID id = IdProcessNewObjectError(pe);
        if (id == IDNO)
            throw;
        pe->Delete();
    }  // catch:  CException

    if (pciRetNetIFace != NULL)
        pciRetNetIFace->Release();

    return pciRetNetIFace;

}  //*** CClusterDoc::PciAddNewNetInterface()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::InitNodes
//
//  Routine Description:
//      Read item data.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::InitNodes(void)
{
    POSITION        pos;
    CClusterNode *  pci;
    CNodeList &     rlpci = LpciNodes();
    CString         strStatusBarText;

    pos = rlpci.GetHeadPosition();
    while (pos != NULL)
    {
        pci = (CClusterNode *) rlpci.GetNext(pos);
        pci->AddRef();
        try
        {
            // Display a message on the status bar.
            {
                strStatusBarText.FormatMessage(IDS_SB_READING_NODE, pci->StrName(), StrNode());
                PframeMain()->SetMessageText(strStatusBarText);
                PframeMain()->UpdateWindow();
            }  // Display a message on the status bar

            pci->ReadItem();
        }  // try
        catch (CNTException * pnte)
        {
            strStatusBarText.Empty();
            if (pnte->Sc() == RPC_S_CALL_FAILED)
            {
                if (!m_bIgnoreErrors)
                    pnte->ReportError();
                pci->Release();
                throw;
            }  // if:  RPC call failed
            ID id = IdProcessNewObjectError(pnte);
            if (id == IDNO)
            {
                pci->Release();
                throw;
            }  // if:  don't ignore the error
            pnte->Delete();
        }  // catch:  CNTException
        catch (CException * pe)
        {
            strStatusBarText.Empty();
            ID id = IdProcessNewObjectError(pe);
            if (id == IDNO)
            {
                pci->Release();
                throw;
            }  // if:  don't ignore the error
            pe->Delete();
        }  // catch:  CException
        pci->Release();
    }  // while:  more items in the list

}  //*** CClusterDoc::InitNodes()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::InitGroups
//
//  Routine Description:
//      Read item data.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::InitGroups(void)
{
    POSITION        pos;
    CGroup *        pci;
    CGroupList &    rlpci = LpciGroups();
    CString         strStatusBarText;

    pos = rlpci.GetHeadPosition();
    while (pos != NULL)
    {
        pci = (CGroup *) rlpci.GetNext(pos);
        pci->AddRef();
        try
        {
            // Display a message on the status bar.
            {
                strStatusBarText.FormatMessage(IDS_SB_READING_GROUP, pci->StrName(), StrNode());
                PframeMain()->SetMessageText(strStatusBarText);
                PframeMain()->UpdateWindow();
            }  // Display a message on the status bar

            pci->ReadItem();
        }  // try
        catch (CNTException * pnte)
        {
            strStatusBarText.Empty();
            if (pnte->Sc() == RPC_S_CALL_FAILED)
            {
                if (!m_bIgnoreErrors)
                    pnte->ReportError();
                pci->Release();
                throw;
            }  // if:  RPC call failed
            ID id = IdProcessNewObjectError(pnte);
            if (id == IDNO)
            {
                pci->Release();
                throw;
            }  // if:  don't ignore the error
            pnte->Delete();
        }  // catch:  CNTException
        catch (CException * pe)
        {
            strStatusBarText.Empty();
            ID id = IdProcessNewObjectError(pe);
            if (id == IDNO)
            {
                pci->Release();
                throw;
            }  // if:  don't ignore the error
            pe->Delete();
        }  // catch:  CException
        pci->Release();
    }  // while:  more items in the list

}  //*** CClusterDoc::InitGroups()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::InitResources
//
//  Routine Description:
//      Read item data.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::InitResources(void)
{
    POSITION        pos;
    CResource *     pci;
    CResourceList & rlpci = LpciResources();
    CString         strStatusBarText;

    pos = rlpci.GetHeadPosition();
    while (pos != NULL)
    {
        pci = (CResource *) rlpci.GetNext(pos);
        pci->AddRef();
        try
        {
            // Display a message on the status bar.
            {
                CString     strStatusBarText;
                strStatusBarText.FormatMessage(IDS_SB_READING_RESOURCE, pci->StrName(), StrNode());
                PframeMain()->SetMessageText(strStatusBarText);
                PframeMain()->UpdateWindow();
            }  // Display a message on the status bar

            pci->ReadItem();
        }  // try
        catch (CNTException * pnte)
        {
            strStatusBarText.Empty();
            if (pnte->Sc() == RPC_S_CALL_FAILED)
            {
                if (!m_bIgnoreErrors)
                    pnte->ReportError();
                pci->Release();
                throw;
            }  // if:  RPC call failed
            ID id = IdProcessNewObjectError(pnte);
            if (id == IDNO)
            {
                pci->Release();
                throw;
            }  // if:  don't ignore the error
            pnte->Delete();
        }  // catch:  CNTException
        catch (CException * pe)
        {
            strStatusBarText.Empty();
            ID id = IdProcessNewObjectError(pe);
            if (id == IDNO)
            {
                pci->Release();
                throw;
            }  // if:  don't ignore the error
            pe->Delete();
        }  // catch:  CException
        pci->Release();
    }  // while:  more items in the list

}  //*** CClusterDoc::InitResources()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::InitResourceTypes
//
//  Routine Description:
//      Read item data.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::InitResourceTypes(void)
{
    POSITION            pos;
    CResourceType *     pci;
    CResourceTypeList & rlpci = LpciResourceTypes();
    CString             strStatusBarText;

    pos = rlpci.GetHeadPosition();
    while (pos != NULL)
    {
        pci = (CResourceType *) rlpci.GetNext(pos);
        pci->AddRef();
        try
        {
            // Display a message on the status bar.
            {
                CString     strStatusBarText;
                strStatusBarText.FormatMessage(IDS_SB_READING_RESTYPE, pci->StrName(), StrNode());
                PframeMain()->SetMessageText(strStatusBarText);
                PframeMain()->UpdateWindow();
            }  // Display a message on the status bar

            pci->ReadItem();
        }  // try
        catch (CNTException * pnte)
        {
            strStatusBarText.Empty();
            if (pnte->Sc() == RPC_S_CALL_FAILED)
            {
                if (!m_bIgnoreErrors)
                    pnte->ReportError();
                pci->Release();
                throw;
            }  // if:  RPC call failed
            ID id = IdProcessNewObjectError(pnte);
            if (id == IDNO)
            {
                pci->Release();
                throw;
            }  // if:  don't ignore the error
            pnte->Delete();
        }  // catch:  CNTException
        catch (CException * pe)
        {
            strStatusBarText.Empty();
            ID id = IdProcessNewObjectError(pe);
            if (id == IDNO)
            {
                pci->Release();
                throw;
            }  // if:  don't ignore the error
            pe->Delete();
        }  // catch:  CException
        pci->Release();
    }  // while:  more items in the list

}  //*** CClusterDoc::InitResourceTypes()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::InitNetworks
//
//  Routine Description:
//      Read item data.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::InitNetworks(void)
{
    POSITION        pos;
    CNetwork *      pci;
    CNetworkList &  rlpci = LpciNetworks();
    CString         strStatusBarText;

    pos = rlpci.GetHeadPosition();
    while (pos != NULL)
    {
        pci = (CNetwork *) rlpci.GetNext(pos);
        pci->AddRef();
        try
        {
            // Display a message on the status bar.
            {
                CString     strStatusBarText;
                strStatusBarText.FormatMessage(IDS_SB_READING_NETWORK, pci->StrName(), StrNode());
                PframeMain()->SetMessageText(strStatusBarText);
                PframeMain()->UpdateWindow();
            }  // Display a message on the status bar

            pci->ReadItem();
        }  // try
        catch (CNTException * pnte)
        {
            strStatusBarText.Empty();
            if (pnte->Sc() == RPC_S_CALL_FAILED)
            {
                if (!m_bIgnoreErrors)
                    pnte->ReportError();
                pci->Release();
                throw;
            }  // if:  RPC call failed
            ID id = IdProcessNewObjectError(pnte);
            if (id == IDNO)
            {
                pci->Release();
                throw;
            }  // if:  don't ignore the error
            pnte->Delete();
        }  // catch:  CNTException
        catch (CException * pe)
        {
            strStatusBarText.Empty();
            ID id = IdProcessNewObjectError(pe);
            if (id == IDNO)
            {
                pci->Release();
                throw;
            }  // if:  don't ignore the error
            pe->Delete();
        }  // catch:  CException
        pci->Release();
    }  // while:  more items in the list

}  //*** CClusterDoc::InitNetworks()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::InitNetInterfaces
//
//  Routine Description:
//      Read item data.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::InitNetInterfaces(void)
{
    POSITION            pos;
    CNetInterface *     pci;
    CNetInterfaceList & rlpci = LpciNetInterfaces();
    CString             strStatusBarText;

    pos = rlpci.GetHeadPosition();
    while (pos != NULL)
    {
        pci = (CNetInterface *) rlpci.GetNext(pos);
        pci->AddRef();
        try
        {
            // Display a message on the status bar.
            {
                CString     strStatusBarText;
                strStatusBarText.FormatMessage(IDS_SB_READING_NETIFACE, pci->StrName(), StrNode());
                PframeMain()->SetMessageText(strStatusBarText);
                PframeMain()->UpdateWindow();
            }  // Display a message on the status bar

            pci->ReadItem();
        }  // try
        catch (CNTException * pnte)
        {
            strStatusBarText.Empty();
            if (pnte->Sc() == RPC_S_CALL_FAILED)
            {
                if (!m_bIgnoreErrors)
                    pnte->ReportError();
                pci->Release();
                throw;
            }  // if:  RPC call failed
            ID id = IdProcessNewObjectError(pnte);
            if (id == IDNO)
            {
                pci->Release();
                throw;
            }  // if:  don't ignore the error
            pnte->Delete();
        }  // catch:  CNTException
        catch (CException * pe)
        {
            strStatusBarText.Empty();
            ID id = IdProcessNewObjectError(pe);
            if (id == IDNO)
            {
                pci->Release();
                throw;
            }  // if:  don't ignore the error
            pe->Delete();
        }  // catch:  CException
        pci->Release();
    }  // while:  more items in the list

}  //*** CClusterDoc::InitNetInterfaces()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::IdProcessNewObjectError
//
//  Routine Description:
//      Processes errors that occur when adding a new object.  If this
//      occurs during initialization and errors have not already been set
//      to be ignored, display the YesToAll dialog.  If not during
//      initialization, add it to the error message queue to be displayed
//      later.
//
//  Arguments:
//      pe          [IN OUT] Exception object to process.
//
//  Return Value:
//      IDYES               Ignore error.
//      IDNO                Cancel the object creation.
//      IDC_YTA_YESTOALL    Ignore this error and all succeeding ones.
//
//--
/////////////////////////////////////////////////////////////////////////////
ID CClusterDoc::IdProcessNewObjectError(IN OUT CException * pe)
{
    ID id = IDYES;

    ASSERT(pe != NULL);

    if (m_bInitializing)
    {
        if (!m_bIgnoreErrors)
        {
            TCHAR   szErrorMsg[2048];

            CYesToAllDialog dlg(szErrorMsg);
            pe->GetErrorMessage(szErrorMsg, sizeof(szErrorMsg) / sizeof(TCHAR));
            id =  (ID)dlg.DoModal();
            if (id == IDC_YTA_YESTOALL)
                m_bIgnoreErrors = TRUE;
        }  // if:  not ignoring errors
    }  // if:  initializing the connection
    else
    {
        if (!m_bIgnoreErrors)
            pe->ReportError();
    }  // else:  called for a notification

    return id;

}  //*** CClusterDoc::IdProcessNewObjectError()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::AddDefaultColumns
//
//  Routine Description:
//      Add default columns to the item.
//
//  Arguments:
//      pti         [IN OUT] Pointer to the item to add the columns to.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::AddDefaultColumns(IN OUT CTreeItem * pti)
{
    ASSERT_VALID(pti);

    pti->DeleteAllColumns();
    pti->PcoliAddColumn(IDS_COLTEXT_NAME, COLI_WIDTH_NAME);
//  pti->PcoliAddColumn(IDS_COLTEXT_TYPE, COLI_WIDTH_TYPE);
    pti->PcoliAddColumn(IDS_COLTEXT_DESCRIPTION, COLI_WIDTH_DESCRIPTION);

}  //*** CClusterDoc::AddDefaultColumns()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::DeleteContents
//
//  Routine Description:
//      Delete the contents of the document.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::DeleteContents(void)
{
    // Select the root item in all views.
    // This is done so that selection is done right up front when all data
    // is still available.
    if (PtiCluster() != NULL)
        PtiCluster()->SelectInAllViews();

    // Delete the tree hierarchy.
    if (m_ptiCluster != NULL)
    {
        // Delete the tree.
        m_ptiCluster->Delete();
        m_ptiCluster->Release();
        m_ptiCluster = NULL;
    }  // if:  there is a hierarchy

    // Delete all the lists.
    DeleteAllItemData(LpciResources());
    DeleteAllItemData(LpciGroups());
    DeleteAllItemData(LpciNetInterfaces());
    DeleteAllItemData(LpciNetworks());
    DeleteAllItemData(LpciNodes());
    DeleteAllItemData(LpciResourceTypes());
    LpciResources().RemoveAll();
    LpciGroups().RemoveAll();
    LpciNetInterfaces().RemoveAll();
    LpciNetworks().RemoveAll();
    LpciNodes().RemoveAll();
    LpciResourceTypes().RemoveAll();

    // Delete the top cluster item.
    if (m_pciCluster != NULL)
    {
        m_pciCluster->Delete();
        m_pciCluster->Release();
        m_pciCluster = NULL;
    }  // if:  there is a cluster item

    // Close the cluster registry key.
    if (HkeyCluster() != NULL)
    {
        ClusterRegCloseKey(HkeyCluster());
        m_hkeyCluster = NULL;
    }  // if:  cluster registry key is open

    // Close the cluster if it is open.
    if ((Hcluster() != NULL) && (Hcluster() != GetClusterAdminApp()->HOpenedCluster()))
    {
        CloseCluster(Hcluster());
        m_hcluster = NULL;
    }  // if:  cluster is open

    CDocument::DeleteContents();

    UpdateAllViews(NULL);

    // If there are any items left to be deleted, let's delete them now.
    {
        POSITION        pos;
        POSITION        posBeingChecked;
        CClusterItem *  pci;

        pos = LpciToBeDeleted().GetHeadPosition();
        while (pos != NULL)
        {
            posBeingChecked = pos;
            pci = LpciToBeDeleted().GetNext(pos);
            ASSERT_VALID(pci);

            ASSERT(pci->NReferenceCount() == 1);
            if (pci->NReferenceCount() == 1)
                LpciToBeDeleted().RemoveAt(posBeingChecked);
        }  // while:  more items in the list
        ASSERT(LpciToBeDeleted().GetCount() == 0);
    }  // Delete items in To Be Deleted list

}  //*** CClusterDoc::DeleteContents()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::SetPathName
//
//  Routine Description:
//      Set the name of the document.
//
//  Arguments:
//      lpszPathName    [IN] Name of the cluster.
//      bAddToMRU       [IN] TRUE = add to Most Recently Used list.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::SetPathName(IN LPCTSTR lpszPathName, IN BOOL bAddToMRU)
{
    CString     strTitle;

    m_strPathName = lpszPathName;
    ASSERT(!m_strPathName.IsEmpty());       // must be set to something
    m_bEmbedded = FALSE;
    ASSERT_VALID(this);

    // Set the document title to the cluster name.
    strTitle.FormatMessage(IDS_WINDOW_TITLE_FORMAT, m_strName, lpszPathName);
    SetTitle(strTitle);

    // add it to the file MRU list
    if (bAddToMRU)
        AfxGetApp()->AddToRecentFileList(m_strPathName);

    // Set the node name to the path name.
    m_strNode = lpszPathName;

    ASSERT_VALID(this);

}  //*** CClusterDoc::SetPathName()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::UpdateTitle
//
//  Routine Description:
//      Update the title of the document.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::UpdateTitle(void)
{
    CString     strTitle;

    ASSERT_VALID(PciCluster());
    ASSERT_VALID(this);

    // Set the document title to the cluster name.
    m_strName = PciCluster()->StrName();
    strTitle.FormatMessage(IDS_WINDOW_TITLE_FORMAT, m_strName, m_strPathName);
    SetTitle(strTitle);

}  //*** CClusterDoc::UpdateTitle()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::OnChangedViewList
//
//  Routine Description:
//      Called when the list of view changes by either having a view added
//      or removed.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::OnChangedViewList(void)
{
    ASSERT_VALID(this);

    // Notify all frames to re-calculate their frame number.
    if (m_bUpdateFrameNumber)
    {
        POSITION            pos;
        CView *             pview;
        CSplitterFrame *    pframe;

        pos = GetFirstViewPosition();
        while (pos != NULL)
        {
            pview = GetNextView(pos);
            ASSERT_VALID(pview);
            if (pview->IsKindOf(RUNTIME_CLASS(CClusterTreeView)))
            {
                pframe = (CSplitterFrame *) pview->GetParentFrame();
                ASSERT_VALID(pframe);
                pframe->CalculateFrameNumber();
            }  // if:  tree view
        }  // while:  more views on the document
    }  // if:  updating frame numbers

    // Call the base class method.
    CDocument::OnChangedViewList();

}  //*** CClusterDoc::OnChangedViewList()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::OnSelChanged
//
//  Routine Description:
//      Called by one of the cluster views when selection changes.
//      Changes the menu if the object type changed.
//
//  Arguments:
//      pciSelected     [IN] Currently selected item.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::OnSelChanged(IN CClusterItem * pciSelected)
{
    IDM     idmNewMenu;
    HMENU * phmenu;
    IDS     idsType;

    // Get the type of object being selected
    if (pciSelected == NULL)
        idsType = 0;
    else
    {
        ASSERT_VALID(pciSelected);
        idsType = pciSelected->IdsType();
    }  // else:  an item was selected

    // Get the ID of the menu required by the selected item.
    switch (idsType)
    {
        case IDS_ITEMTYPE_CLUSTER:
            idmNewMenu = IDM_CLUSTER;
            phmenu = &m_hmenuCluster;
            break;

        case IDS_ITEMTYPE_NODE:
            idmNewMenu = IDM_NODE;
            phmenu = &m_hmenuNode;
            break;

        case IDS_ITEMTYPE_GROUP:
            idmNewMenu = IDM_GROUP;
            phmenu = &m_hmenuGroup;
            break;

        case IDS_ITEMTYPE_RESOURCE:
            idmNewMenu = IDM_RESOURCE;
            phmenu = &m_hmenuResource;
            break;

        case IDS_ITEMTYPE_RESTYPE:
            idmNewMenu = IDM_RESTYPE;
            phmenu = &m_hmenuResType;
            break;

        case IDS_ITEMTYPE_NETWORK:
            idmNewMenu = IDM_NETWORK;
            phmenu = &m_hmenuNetwork;
            break;

        case IDS_ITEMTYPE_NETIFACE:
            idmNewMenu = IDM_NETIFACE;
            phmenu = &m_hmenuNetIFace;
            break;

        default:
            idmNewMenu = 0;
            phmenu = NULL;
            break;

    }  // switch:  pciSelected->IdsType()

    // If the menu ID changed, load the new one.
    if (m_idmCurrentMenu != idmNewMenu)
    {
        if (idmNewMenu == 0)
            m_hmenuCurrent = NULL;
        else
        {
            if (*phmenu == NULL)
                *phmenu = ::LoadMenu(AfxGetResourceHandle(), MAKEINTRESOURCE(idmNewMenu));
            m_hmenuCurrent = *phmenu;
        }  // else:  special menu required by item

        m_idmCurrentMenu = idmNewMenu;
    }  // if:  menu ID changed

    // Update the menu bar and redisplay it.
    if (((CMDIFrameWnd *) AfxGetMainWnd())->MDIGetActive() != NULL)
    {
#ifdef _DEBUG
        if (g_tagDocMenu.BAny())
        {
            TraceMenu(g_tagDocMenu, AfxGetMainWnd()->GetMenu(), _T("OnSelChanged menu: "));
            {
                CMDIFrameWnd *  pFrame = (CMDIFrameWnd *) AfxGetMainWnd();
                CMenu           menuDefault;

                menuDefault.Attach(pFrame->m_hMenuDefault);
                TraceMenu(g_tagDocMenu, &menuDefault, _T("Frame default menu before OnSelChanged: "));
                menuDefault.Detach();
            }  // trace default menu
        }  // if:  tag is active
#endif

        ((CFrameWnd *) AfxGetMainWnd())->OnUpdateFrameMenu(NULL);
        AfxGetMainWnd()->DrawMenuBar();

        TraceMenu(g_tagDocMenu, AfxGetMainWnd()->GetMenu(), _T("Post-OnSelChanged menu: "));
    }  // if:  active window present

}  //*** CClusterDoc::OnSelChanged()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::GetDefaultMenu
//
//  Routine Description:
//      Returns the menu to use.  Overridden to allow us to use multiple menus
//      with the same type of document.
//
//  Arguments:
//      None.
//
//  Return Value:
//      hmenu       The currently selected menu, or NULL for no default.
//
//--
/////////////////////////////////////////////////////////////////////////////
HMENU CClusterDoc::GetDefaultMenu(void)
{
    return m_hmenuCurrent;

}  //*** CClusterDoc::GetDefaultMenu()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::OnCmdRefresh
//
//  Routine Description:
//      Processes the ID_VIEW_REFRESH menu command.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::OnCmdRefresh(void)
{
    CWaitCursor     wc;

    try
    {
        Trace(g_tagDocRefresh, _T("(%s) Deleting old contents"), StrNode());

        {
            POSITION            pos;
            CSplitterFrame *    pframe;
            CView *             pview;

            // Get the active child frame window.
            pframe = (CSplitterFrame *) ((CFrameWnd *) AfxGetMainWnd())->GetActiveFrame();
            if ((pframe->IsKindOf(RUNTIME_CLASS(CSplitterFrame)))
                    && (pframe->PviewList()->PtiParent() != NULL))
            {
                // Tell the view to save its column information.
                pframe->PviewList()->SaveColumns();
            }  //  if:  MDI window exists

            pos = GetFirstViewPosition();
            while (pos != NULL)
            {
                pview = GetNextView(pos);
                if (pview->IsKindOf(RUNTIME_CLASS(CClusterTreeView)))
                {
                    // Save the current selection
                    ((CClusterTreeView *) pview)->SaveCurrentSelection();
                }  // if:  this is a tree view
            }  // while:  more views
        }  // Save the column information

        DeleteContents();

        Trace(g_tagDocRefresh, _T("(%s) %d items still to be deleted"), StrNode(), LpciToBeDeleted().GetCount());

        Trace(g_tagDocRefresh, _T("(%s) Creating new cluster object"), StrNode());

        m_bClusterAvailable = TRUE;
        m_bInitializing = TRUE;

        // Create a new cluster object.
        m_pciCluster = new CCluster;
        if ( m_pciCluster == NULL )
        {
            AfxThrowMemoryException();
        } // if: error allocating the cluster object
        PciCluster()->AddRef();
        PciCluster()->Init(this, GetPathName());

        Trace(g_tagDocRefresh, _T("(%s) Building base hierarchy"), StrNode());

        // Build the base hierarchy.
        BuildBaseHierarchy();

        Trace(g_tagDocRefresh, _T("(%s) Collecting cluster items"), StrNode());

        // Collect the items in the cluster and build the hierarchy.
        CollectClusterItems();
        PciCluster()->CollectNetworkPriority(NULL);

        Trace(g_tagDocRefresh, _T("(%s) Re-initializing the views"), StrNode());

        // Re-initialize the views.
        {
            POSITION    pos;
            CView *     pview;

            pos = GetFirstViewPosition();
            while (pos != NULL)
            {
                pview = GetNextView(pos);
                ASSERT_VALID(pview);
                pview->OnInitialUpdate();
            }  // while:  more items in the list
        }  // Re-initialize the views
    }  // try
    catch (CException * pe)
    {
        if (!m_bIgnoreErrors)
            pe->ReportError();
        pe->Delete();

        if (HkeyCluster() != NULL)
        {
            ClusterRegCloseKey(HkeyCluster());
            m_hkeyCluster = NULL;
        }  // if:  cluster registry key is open
        if (Hcluster() != NULL)
        {
            CloseCluster(Hcluster());
            m_hcluster = NULL;
        }  // if:  cluster is open
        m_bClusterAvailable = FALSE;
    }  // catch:  CException

    // Reset the message on the status bar.
    PframeMain()->SetMessageText(AFX_IDS_IDLEMESSAGE);
    PframeMain()->UpdateWindow();

    m_bInitializing = FALSE;

#ifdef GC_DEBUG
    gcCollect();
#endif

}  //*** CClusterDoc::OnCmdRefresh()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::OnCmdNewGroup
//
//  Routine Description:
//      Processes the ID_FILE_NEW_GROUP menu command.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::OnCmdNewGroup(void)
{
    CCreateGroupWizard  wiz(this, AfxGetMainWnd());

    if (wiz.BInit())
    {
        if (wiz.DoModal() == ID_WIZFINISH)
        {
            CString     strMsg;

            strMsg.FormatMessage(IDS_CREATED_GROUP, wiz.StrName());
            AfxMessageBox(strMsg, MB_ICONINFORMATION);
        }  // if:  user pressed the FINISH button
    }  // if:  wizard initialized successfully

}  //*** CClusterDoc::OnCmdNewGroup()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::OnCmdNewResource
//
//  Routine Description:
//      Processes the ID_FILE_NEW_RESOURCE menu command.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::OnCmdNewResource(void)
{
    CCreateResourceWizard   wiz(this, AfxGetMainWnd());

    if (wiz.BInit())
    {
        if (wiz.DoModal() == ID_WIZFINISH)
        {
            CString     strMsg;

            strMsg.FormatMessage(IDS_CREATED_RESOURCE, wiz.StrName());
            AfxMessageBox(strMsg, MB_ICONINFORMATION);
        }  // if:  user pressed the FINISH button
    }  // if:  wizard initialized successfully

}  //*** CClusterDoc::OnCmdNewResource()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::OnCmdNewNode
//
//  Routine Description:
//      Processes the ID_FILE_NEW_NODE menu command.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::OnCmdNewNode( void )
{
    NewNodeWizard( StrName(), m_bIgnoreErrors );
#if 0
    HRESULT             hr;
    IClusCfgWizard *    pccwWiz;
    BSTR                bstrConnectName = NULL;
    BOOL                fCommitted = FALSE;

    // Make sure the ClusCfg client has been loaded.
    GetClusterAdminApp()->LoadClusCfgClient();

    // Get an interface pointer for the wizard.
    hr = CoCreateInstance(
            CLSID_ClusCfgWizard,
            NULL,
            CLSCTX_INPROC_SERVER,
            IID_IClusCfgWizard,
            (void **) &pccwWiz
            );
    if ( FAILED( hr ) )
    {
        CNTException nte( hr, IDS_CREATE_CLUSCFGWIZ_OBJ_ERROR, NULL, NULL, FALSE /*bAutoDelete*/ );
        if ( ! m_bIgnoreErrors )
        {
            nte.ReportError();
        }
        return;
    } // if: error getting the interface pointer

    // Specify the name of the cluster we are going to add a node to.
    bstrConnectName = SysAllocString( PciCluster()->StrFQDN() );
    if ( bstrConnectName == NULL )
    {
        AfxThrowMemoryException();
    }
    hr = pccwWiz->put_ClusterName( bstrConnectName );
    if ( FAILED( hr ) )
    {
        CNTException nte( hr, IDS_ADD_NODES_TO_CLUSTER_ERROR, bstrConnectName, NULL, FALSE /*bAutoDelete*/ );
        if ( ! m_bIgnoreErrors )
        {
            nte.ReportError();
        }
    } // if: error setting the cluster name

    // Display the wizard.
    hr = pccwWiz->AddClusterNodes(
                    AfxGetMainWnd()->m_hWnd,
                    &fCommitted
                    );
    if ( FAILED( hr ) )
    {
        CNTException nte( hr, IDS_ADD_NODES_TO_CLUSTER_ERROR, bstrConnectName, NULL, FALSE /*bAutoDelete*/ );
        if ( ! m_bIgnoreErrors )
        {
            nte.ReportError();
        }
    } // if: error adding cluster nodes

    SysFreeString( bstrConnectName );
    pccwWiz->Release();
#endif

}  //*** CClusterDoc::OnCmdNewNode()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::OnCmdConfigApp
//
//  Routine Description:
//      Processes the ID_FILE_CONFIG_APP menu command.
//
//  Arguments:
//      None.
//
//  Return Value:
//      None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::OnCmdConfigApp(void)
{
    HRESULT                     hr;
    IClusterApplicationWizard * piWiz;

    // Get an interface pointer for the wizard.
    hr = CoCreateInstance(
            __uuidof(ClusAppWiz),
            NULL,
            CLSCTX_INPROC_SERVER,
            __uuidof(IClusterApplicationWizard),
            (LPVOID *) &piWiz
            );
    if (FAILED(hr))
    {
        CNTException nte(hr, (IDS) 0);
        if (!m_bIgnoreErrors)
            nte.ReportError();
        return;
    }  // if:  error getting the interface pointer

    // Display the wizard.
    hr = piWiz->DoModalWizard(AfxGetMainWnd()->m_hWnd,
                              (ULONG_PTR)Hcluster(),
                              NULL);
    piWiz->Release();

    // Handle any errors.
    if (FAILED(hr))
    {
        CNTException nte(hr, (IDS) 0);
        if (!m_bIgnoreErrors)
            nte.ReportError();
    }  // if:  error from the wizard

}  //*** CClusterDoc::OnCmdConfigApp()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::OnClusterNotify
//
//  Routine Description:
//      Handler for the WM_CAM_CLUSTER_NOTIFY message.
//      Processes cluster notifications.
//
//  Arguments:
//      pnotify     [IN OUT] Object describing the notification.
//
//  Return Value:
//      Value returned from the application method.
//
//--
/////////////////////////////////////////////////////////////////////////////
LRESULT CClusterDoc::OnClusterNotify( IN OUT CClusterNotify * pnotify )
{
    ASSERT( pnotify != NULL );

    BOOL            bOldIgnoreErrors = m_bIgnoreErrors;
    CClusterItem *  pciClusterItemPtr = NULL;

    m_bIgnoreErrors = TRUE;

    try
    {
        switch ( pnotify->m_dwFilterType )
        {
            case CLUSTER_CHANGE_CLUSTER_STATE:
                {
                    m_bClusterAvailable = FALSE;

                    // Update the state of all objects in the cluster.
                    ASSERT_VALID( PtiCluster() );
                    PtiCluster()->UpdateAllStatesInTree();
                    try
                    {
                        CString     strMsg;
                        ASSERT( pnotify->m_strName.GetLength() > 0 );
                        strMsg.FormatMessage( IDS_CLUSTER_NOT_AVAILABLE, pnotify->m_strName );
                        AfxMessageBox( strMsg, MB_ICONINFORMATION );
                    }  // try
                    catch ( CException * pe )
                    {
                        pe->Delete();
                    }  // catch:  CException
                    break;
                }

            case CLUSTER_CHANGE_CLUSTER_PROPERTY:
                {
                    ASSERT_VALID( PciCluster() );
                    Trace( g_tagDocNotify, _T("(%s) - Cluster properties changed - new name is '%s'"), StrNode(), pnotify->m_strName );
                    PciCluster()->ReadItem();
                    PciCluster()->CollectNetworkPriority( NULL );
                    break;
                }

            case CLUSTER_CHANGE_NODE_ADDED:
                {
                    CClusterNode *  pciNode;
                    Trace( g_tagNodeNotify, _T("(%s) - Adding node '%s'"), m_strPathName, pnotify->m_strName );
                    pciNode = PciAddNewNode( pnotify->m_strName );
                    if ( pciNode != NULL )
                    {
                        ASSERT_VALID( pciNode );
                        pciNode->AddRef();
                        // For calling Release later. This is done so that
                        // release is called even if ReadItem below throws an exception.
                        pciClusterItemPtr = pciNode;
                        pciNode->ReadItem();
                    }  // if:  node was added
                    break;
                }

            case CLUSTER_CHANGE_GROUP_ADDED:
                {
                    CGroup *    pciGroup;
                    Trace( g_tagGroupNotify, _T("(%s) - Adding group '%s'"), m_strPathName, pnotify->m_strName );
                    pciGroup = PciAddNewGroup( pnotify->m_strName );
                    if ( pciGroup != NULL )
                    {
                        ASSERT_VALID( pciGroup );
                        pciGroup->AddRef();
                        // For calling Release later. This is done so that
                        // release is called even if ReadItem below throws an exception.
                        pciClusterItemPtr = pciGroup;
                        pciGroup->ReadItem();
                    } // if:  group was added
                    break;
                }

            case CLUSTER_CHANGE_RESOURCE_ADDED:
                {
                    CResource * pciRes;
                    Trace( g_tagResNotify, _T("(%s) - Adding resource '%s'"), m_strPathName, pnotify->m_strName );
                    pciRes = PciAddNewResource( pnotify->m_strName );
                    if (pciRes != NULL)
                    {
                        ASSERT_VALID( pciRes );
                        pciRes->AddRef();
                        // For calling Release later. This is done so that
                        // release is called even if ReadItem below throws an exception.
                        pciClusterItemPtr = pciRes;
                        if ( ! pciRes->BInitializing() )
                        {
                            pciRes->ReadItem();
                        } // if: not initializing the resource
                    } // if:  resource was added
                    break;
                }

            case CLUSTER_CHANGE_RESOURCE_TYPE_ADDED:
                {
                    CResourceType * pciResType;
                    Trace( g_tagResTypeNotify, _T("(%s) - Adding resource Type '%s'"), m_strPathName, pnotify->m_strName );
                    pciResType = PciAddNewResourceType( pnotify->m_strName );
                    if ( pciResType != NULL )
                    {
                        ASSERT_VALID( pciResType );
                        pciResType->AddRef();
                        // For calling Release later. This is done so that
                        // release is called even if ReadItem below throws an exception.
                        pciClusterItemPtr = pciResType;
                        pciResType->ReadItem();
                    } // if:  resource type was added
                    break;
                }

            case CLUSTER_CHANGE_RESOURCE_TYPE_DELETED:
                {
                    ASSERT( pnotify->m_strName.GetLength() > 0 );
                    CResourceType * pciResType = LpciResourceTypes().PciResTypeFromName( pnotify->m_strName );
                    if ( pciResType != NULL )
                    {
                        ASSERT_VALID( pciResType );
                        Trace( g_tagResTypeNotify, _T("(%s) - Resource Type '%s' deleted"), m_strPathName, pnotify->m_strName );
                        pciResType->Delete();
                    }  // if:  resource type was found
                    else
                    {
                        Trace( g_tagDocNotify, _T("(%s) - Resource Type '%s' deleted (NOT FOUND)"), m_strPathName, pnotify->m_strName );
                    } // else: resource type not found
                    break;
                }

            case CLUSTER_CHANGE_RESOURCE_TYPE_PROPERTY:
                {
                    ASSERT( pnotify->m_strName.GetLength() > 0 );
                    CResourceType * pciResType = LpciResourceTypes().PciResTypeFromName( pnotify->m_strName );
                    if ( pciResType != NULL )
                    {
                        ASSERT_VALID( pciResType );
                        Trace( g_tagResTypeNotify, _T("(%s) - Resource Type '%s' property change"), m_strPathName, pnotify->m_strName );
                        pciResType->ReadItem();
                    } // if:  resource type was found
                    else
                    {
                        Trace( g_tagDocNotify, _T("(%s) - Resource Type '%s' deleted (NOT FOUND)"), m_strPathName, pnotify->m_strName );
                    } // else: resource type not found
                    break;
                }

            case CLUSTER_CHANGE_NETWORK_ADDED:
                {
                    CNetwork *  pciNetwork;
                    Trace( g_tagNetNotify, _T("(%s) - Adding network '%s'"), m_strPathName, pnotify->m_strName );
                    pciNetwork = PciAddNewNetwork( pnotify->m_strName );
                    if ( pciNetwork != NULL )
                    {
                        ASSERT_VALID( pciNetwork );
                        pciNetwork->AddRef();
                        // For calling Release later. This is done so that
                        // release is called even if ReadItem below throws an exception.
                        pciClusterItemPtr = pciNetwork;
                        pciNetwork->ReadItem();
                    } // if:  network was added
                    break;
                }

            case CLUSTER_CHANGE_NETINTERFACE_ADDED:
                {
                    CNetInterface * pciNetIFace;
                    Trace( g_tagNetIFaceNotify, _T("(%s) - Adding network interface '%s'"), m_strPathName, pnotify->m_strName );
                    pciNetIFace = PciAddNewNetInterface( pnotify->m_strName );
                    if ( pciNetIFace != NULL )
                    {
                        ASSERT_VALID( pciNetIFace );
                        pciNetIFace->AddRef();
                        // For calling Release later. This is done so that
                        // release is called even if ReadItem below throws an exception.
                        pciClusterItemPtr = pciNetIFace;
                        pciNetIFace->ReadItem();
                    } // if:  network interface was added
                    break;
                }

            case CLUSTER_CHANGE_QUORUM_STATE:
                Trace( g_tagDocNotify, _T("(%s) - Quorum state changed (%s)"), m_strPathName, pnotify->m_strName );
                break;

            case CLUSTER_CHANGE_REGISTRY_NAME:
                Trace( g_tagDocRegNotify, _T("(%s) - Registry namespace '%s' changed"), m_strPathName, pnotify->m_strName );
                ProcessRegNotification( pnotify );
                break;

            case CLUSTER_CHANGE_REGISTRY_ATTRIBUTES:
                Trace( g_tagDocRegNotify, _T("(%s) - Registry atributes for '%s' changed"), m_strPathName, pnotify->m_strName );
                ProcessRegNotification( pnotify );
                break;

            case CLUSTER_CHANGE_REGISTRY_VALUE:
                Trace( g_tagDocRegNotify, _T("(%s) - Registry value '%s' changed"), m_strPathName, pnotify->m_strName );
                ProcessRegNotification( pnotify );
                break;

            default:
                Trace( g_tagDocNotify, _T("(%s) - Unknown notification (%x) for '%s'"), m_strPathName, pnotify->m_dwFilterType, pnotify->m_strName );
        } // switch:  dwFilterType
    } // try
    catch ( CException * pe )
    {
        // Don't display anything on notification errors.
        // If it's really a problem, the user will see it when
        // refreshing the view.
        if ( ! m_bIgnoreErrors )
        {
            pe->ReportError();
        } // if: not ignoring errors
        pe->Delete();
    } // catch:  CException

    if ( pciClusterItemPtr != NULL )
    {
        pciClusterItemPtr->Release();
    } // if: cluster item pointer not released yet

    m_bIgnoreErrors = bOldIgnoreErrors;

    // Reset the message on the status bar.
    {
        CFrameWnd * pframe = PframeMain( );
        if ( pframe != NULL )
        {
            pframe->SetMessageText(AFX_IDS_IDLEMESSAGE);
            pframe->UpdateWindow();
        } // if: main frame window is available
    }

    delete pnotify;
    return 0;

} //*** CClusterDoc::OnClusterNotify()

/////////////////////////////////////////////////////////////////////////////
//++
//
//  CClusterDoc::ProcessRegNotification
//
//  Routine Description:
//      Process registry notifications for the document.
//
//  Arguments:
//      pnotify     [IN] Object describing the notification.
//
//  Return Value:
//      pci         Cluster item that cares about the notification.
//      NULL        Unknown object.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CClusterDoc::ProcessRegNotification(IN const CClusterNotify * pnotify)
{
    CCluster *  pci             = NULL;
    HKEY        hkey            = NULL;
    CString     strRootKeyName;

#define RESTYPE_KEY_NAME_PREFIX CLUSREG_KEYNAME_RESOURCE_TYPES _T("\\")

    try
    {
        // If there is no key name, update the cluster item.
        if (pnotify->m_strName.GetLength() == 0)
            pci = PciCluster();
        else
        {
            // Find the root key name.
            strRootKeyName = pnotify->m_strName.SpanExcluding(_T("\\"));

            // If the root key name is the same as the notification name
            // and it is for one of the object type keys, reread the lists
            // of extensions for that one type of object.
            if (strRootKeyName == pnotify->m_strName)
            {
                POSITION    pos;

                // Find the object based on its type.
                if (strRootKeyName == CLUSREG_KEYNAME_NODES)
                {
                    PciCluster()->ReadNodeExtensions();
                    pos = LpciNodes().GetHeadPosition();
                    while (pos != NULL)
                        ((CClusterNode *) LpciNodes().GetNext(pos))->ReadExtensions();
                }  // if:  node registry notification
                else if (strRootKeyName == CLUSREG_KEYNAME_GROUPS)
                {
                    PciCluster()->ReadGroupExtensions();
                    pos = LpciGroups().GetHeadPosition();
                    while (pos != NULL)
                        ((CGroup *) LpciGroups().GetNext(pos))->ReadExtensions();
                }  // else if:  group registry notification
                else if (strRootKeyName == CLUSREG_KEYNAME_RESOURCES)
                {
                    PciCluster()->ReadResourceExtensions();
                    pos = LpciResources().GetHeadPosition();
                    while (pos != NULL)
                        ((CResource *) LpciResources().GetNext(pos))->ReadExtensions();
                }  // else if:  resource registry notification
                else if (strRootKeyName == CLUSREG_KEYNAME_RESOURCE_TYPES)
                {
                    PciCluster()->ReadResTypeExtensions();
                    pos = LpciResourceTypes().GetHeadPosition();
                    while (pos != NULL)
                        ((CResourceType *) LpciResourceTypes().GetNext(pos))->ReadExtensions();
                    pos = LpciResources().GetHeadPosition();
                    while (pos != NULL)
                        ((CResource *) LpciResources().GetNext(pos))->ReadExtensions();
                }  // else if:  resource type registry notification
                else if (strRootKeyName == CLUSREG_KEYNAME_NETWORKS)
                {
                    PciCluster()->ReadNetworkExtensions();
                    pos = LpciNetworks().GetHeadPosition();
                    while (pos != NULL)
                        ((CNetwork *) LpciNetworks().GetNext(pos))->ReadExtensions();
                }  // else if:  network registry notification
                else if (strRootKeyName == CLUSREG_KEYNAME_NETINTERFACES)
                {
                    PciCluster()->ReadNetInterfaceExtensions();
                    pos = LpciNetInterfaces().GetHeadPosition();
                    while (pos != NULL)
                        ((CNetInterface *) LpciNetInterfaces().GetNext(pos))->ReadExtensions();
                }  // else if:  network interface registry notification
            }  // if:  root name and full name are the same
            else if (_tcsnicmp(pnotify->m_strName, RESTYPE_KEY_NAME_PREFIX, lstrlen(RESTYPE_KEY_NAME_PREFIX)) == 0)
            {
                int             idxSlash = pnotify->m_strName.Find(_T('\\'));
                CString         strResTypeName;
                CResource *     pciRes;
                CResourceType * pciResType;
                POSITION        pos;

                strResTypeName = pnotify->m_strName.Mid(idxSlash + 1, lstrlen(pnotify->m_strName) - lstrlen(RESTYPE_KEY_NAME_PREFIX));

                // Re-read the resource type extensions.
                pos = LpciResourceTypes().GetHeadPosition();
                while (pos != NULL)
                {
                    pciResType = (CResourceType *) LpciResourceTypes().GetNext(pos);
                    if (pciResType->StrName().CompareNoCase(strResTypeName) == 0)
                    {
                        pciResType->ReadExtensions();
                        break;
                    } // if: found the resource type
                } // while: more resource types

                // Re-read the resource extensions.
                pos = LpciResources().GetHeadPosition();
                while (pos != NULL)
                {
                    pciRes = (CResource *) LpciResources().GetNext(pos);
                    if (pciRes->StrResourceType() == strResTypeName)
                    {
                        pciRes->ReadExtensions();
                    } // if: found a resource of that type
                } // while: more resources
            } // else if: single resource type changed

            pci = PciCluster();
        } // else:  not the cluster object

        // If the cluster object can process it, have it re-read its info
        if (pci != NULL)
        {
            pci->MarkAsChanged();
            pci->ReadClusterExtensions();
        }  // if:  cluster object changed
    }  // try
    catch (...)
    {
    }

    if (hkey != NULL)
        ::ClusterRegCloseKey(hkey);

}  //*** CClusterDoc::ProcessRegNotification()