// This is a part of the Microsoft Management Console. // Copyright (C) Microsoft Corporation, 1995 - 1999 // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Management Console and related // electronic documentation provided with the interfaces. #ifndef _TREEDATA_H #define _TREEDATA_H ///////////////////////////////////////////////////////////////////////////// // Miscellanea extern LPCWSTR g_lpszNullString; ///////////////////////////////////////////////////////////////////////////// // Generic Helper functions template inline void SAFE_RELEASE(TYPE*& pObj) { if (pObj != NULL) { pObj->Release(); pObj = NULL; } else { TRACE(_T("Release called on NULL interface ptr")); } } /////////////////////////////////////////////////////////////////// // Context Menu data structures and macros #define MAX_CONTEXT_MENU_STRLEN 128 struct MENUDATARES { WCHAR szBuffer[MAX_CONTEXT_MENU_STRLEN*2]; UINT uResID; }; struct MENUMAP { MENUDATARES* dataRes; CONTEXTMENUITEM2* ctxMenu; }; #define DECLARE_MENU(theClass) \ class theClass \ { \ public: \ static LPCONTEXTMENUITEM2 GetContextMenuItem() { return GetMenuMap()->ctxMenu; }; \ static MENUMAP* GetMenuMap(); \ }; #define BEGIN_MENU(theClass) \ MENUMAP* theClass::GetMenuMap() { #define BEGIN_CTX static CONTEXTMENUITEM2 ctx[] = { #define CTX_ENTRY_TOP(cmdID, languageIndependantStringID) { L"",L"", cmdID, CCM_INSERTIONPOINTID_PRIMARY_TOP, 0, 0, languageIndependantStringID}, #define CTX_ENTRY_NEW(cmdID, languageIndependantStringID) { L"",L"", cmdID, CCM_INSERTIONPOINTID_PRIMARY_NEW, 0, 0, languageIndependantStringID}, #define CTX_ENTRY_TASK(cmdID, languageIndependantStringID) { L"",L"", cmdID, CCM_INSERTIONPOINTID_PRIMARY_TASK, 0, 0, languageIndependantStringID}, #define CTX_ENTRY_VIEW(cmdID, languageIndependantStringID) { L"",L"", cmdID, CCM_INSERTIONPOINTID_PRIMARY_VIEW, 0, 0, languageIndependantStringID}, #define END_CTX { NULL, NULL, 0, 0, 0, 0} }; #define BEGIN_RES static MENUDATARES dataRes[] = { #define RES_ENTRY(resID) {L"", resID }, #define END_RES { NULL, 0 } }; #define END_MENU \ static MENUMAP menuMap = { dataRes, ctx }; \ return &menuMap; } BOOL LoadContextMenuResources(MENUMAP* pMenuMap); // // Toolbar macros // #define DECLARE_TOOLBAR_MAP() \ public: \ virtual HRESULT ToolbarNotify(int event, \ CComponentDataObject* pComponentData, \ CNodeList* pNodeList); #define BEGIN_TOOLBAR_MAP(theClass) \ HRESULT theClass::ToolbarNotify(int event, \ CComponentDataObject* pComponentData, \ CNodeList* pNodeList) \ { \ HRESULT hr = S_OK; \ event; \ pComponentData; \ pNodeList; #define TOOLBAR_EVENT(toolbar_event, function) \ if (event == toolbar_event) \ { \ hr = function(pComponentData, pNodeList); \ } #define END_TOOLBAR_MAP() \ return hr; \ } #define DECLARE_TOOLBAR_EVENT(toolbar_event, value) \ static const int toolbar_event = value; //////////////////////////////////////////////////////////// // header control resources data structures #define MAX_RESULT_HEADER_STRLEN 128 struct RESULT_HEADERMAP { WCHAR szBuffer[MAX_RESULT_HEADER_STRLEN]; UINT uResID; int nFormat; int nWidth; }; BOOL LoadResultHeaderResources(RESULT_HEADERMAP* pHeaderMap, int nCols); //////////////////////////////////////////////////////////// // bitmap strips resources data structures template class CBitmapHolder : public CBitmap { public: BOOL LoadBitmap() { return CBitmap::LoadBitmap(nResID);} }; /////////////////////////////////////////////////////////////////////////////// // FORWARD DECLARATIONS class CComponentDataObject; class CContainerNode; class CMTContainerNode; class CLeafNode; class CPropertyPageHolderBase; class CBackgroundThread; class CQueryObj; ///////////////////////////////////////////////////////////////////// // CObjBase // base class for all objects relying on RTTI and class type info class CObjBase { public: CObjBase() {} virtual ~CObjBase() {} }; ///////////////////////////////////////////////////////////////////// // CTreeNode // cannot construct objects of this class, have to derive from it #define DECLARE_NODE_GUID() \ static const GUID NodeTypeGUID; \ virtual const GUID* GetNodeType() { return &NodeTypeGUID;} // use the HIWORD for generic flags and leave the LOWORD for application specific data #define TN_FLAG_HIDDEN (0x00010000) // does not appear in the UI #define TN_FLAG_NO_WRITE (0x00020000) // cannot edit or create #define TN_FLAG_NO_DELETE (0x00040000) // cannot delete #define TN_FLAG_HAS_SHEET (0x00080000) // this node or a child has a property sheet up #define TN_FLAG_CONTAINER (0x00100000) // container (i.e. not leaf) #define TN_FLAG_CONTAINER_ENUM (0x00200000) // container node has been enumerated (back end) #define TN_FLAG_CONTAINER_EXP (0x00400000) // container node has been expanded (UI node) class CTreeNode : public CObjBase { public: virtual ~CTreeNode() {} CContainerNode* GetContainer() { return m_pContainer; } void SetContainer(CContainerNode* pContainer) { m_pContainer = pContainer; } BOOL HasContainer(CContainerNode* pContainerNode); virtual LPCWSTR GetDisplayName() { return m_szDisplayName; } virtual void SetDisplayName(LPCWSTR lpszDisplayName) { m_szDisplayName = lpszDisplayName;} // // Data Object related data // virtual const GUID* GetNodeType() { return NULL;} virtual HRESULT GetDataHere(CLIPFORMAT, LPSTGMEDIUM, CDataObject*) { return DV_E_CLIPFORMAT;} virtual HRESULT GetData(CLIPFORMAT, LPSTGMEDIUM, CDataObject*) { return DV_E_CLIPFORMAT;} virtual HRESULT GetResultViewType(CComponentDataObject* pComponentData, LPOLESTR* ppViewType, long* pViewOptions); virtual HRESULT OnShow(LPCONSOLE) { return S_OK; } // // flag manipulation API's // BOOL IsContainer() { return (m_dwNodeFlags & TN_FLAG_CONTAINER) ? TRUE : FALSE;} BOOL IsVisible() { return (m_dwNodeFlags & TN_FLAG_HIDDEN) ? FALSE : TRUE;} BOOL CanDelete() { return (m_dwNodeFlags & TN_FLAG_NO_DELETE) ? FALSE : TRUE;} virtual void SetFlagsDown(DWORD dwNodeFlags, BOOL bSet); void SetFlagsUp(DWORD dwNodeFlags, BOOL bSet); DWORD GetFlags() { return m_dwNodeFlags;} virtual BOOL CanExpandSync() { return FALSE; } virtual void Show(BOOL bShow, CComponentDataObject* pComponentData); // // Verb handlers // virtual HRESULT OnRename(CComponentDataObject*, LPWSTR) { return S_FALSE; } virtual void OnDelete(CComponentDataObject* pComponentData, CNodeList* pNodeList) = 0; virtual BOOL OnRefresh(CComponentDataObject*, CNodeList*) { return FALSE; } virtual HRESULT OnCommand(long, DATA_OBJECT_TYPES, CComponentDataObject*, CNodeList*) { return S_OK; }; virtual HRESULT OnAddMenuItems(IContextMenuCallback2* pContextMenuCallback2, DATA_OBJECT_TYPES type, long *pInsertionAllowed, CNodeList* pNodeList); virtual HRESULT OnAddMenuItemsMultipleSelect(IContextMenuCallback2*, DATA_OBJECT_TYPES, long*, CNodeList*) { return S_OK; } virtual MMC_CONSOLE_VERB GetDefaultVerb(DATA_OBJECT_TYPES type, CNodeList* pNodeList); virtual void OnSetVerbState(LPCONSOLEVERB pConsoleVerb, DATA_OBJECT_TYPES type, CNodeList* pNodeList); virtual HRESULT OnSetToolbarVerbState(IToolbar* pToolbar, CNodeList* pNodeList); virtual BOOL OnSetRenameVerbState(DATA_OBJECT_TYPES type, BOOL* pbHide, CNodeList* pNodeList); virtual BOOL OnSetDeleteVerbState(DATA_OBJECT_TYPES type, BOOL* pbHide, CNodeList* pNodeList); virtual BOOL OnSetRefreshVerbState(DATA_OBJECT_TYPES type, BOOL* pbHide, CNodeList* pNodeList); virtual BOOL OnSetCutVerbState(DATA_OBJECT_TYPES type, BOOL* pbHide, CNodeList* pNodeList); virtual BOOL OnSetCopyVerbState(DATA_OBJECT_TYPES type, BOOL* pbHide, CNodeList* pNodeList); virtual BOOL OnSetPasteVerbState(DATA_OBJECT_TYPES type, BOOL* pbHide, CNodeList* pNodeList); virtual BOOL OnSetPrintVerbState(DATA_OBJECT_TYPES type, BOOL* pbHide, CNodeList* pNodeList); // // Property Page methods // virtual BOOL DelegatesPPToContainer() { return FALSE; } virtual void ShowPageForNode(CComponentDataObject* pComponentDataObject); virtual BOOL HasPropertyPages(DATA_OBJECT_TYPES type, BOOL* pbHideVerb, CNodeList* pNodeList); virtual HRESULT CreatePropertyPages(LPPROPERTYSHEETCALLBACK, LONG_PTR, CNodeList*) { return E_FAIL; } virtual void OnPropertyChange(CComponentDataObject* pComponentData, BOOL bScopePane,long changeMask); virtual BOOL CanCloseSheets() { return TRUE;} void OnCreateSheet(); void OnDeleteSheet(); BOOL HasSheet() { return (m_dwNodeFlags & TN_FLAG_HAS_SHEET) ? TRUE : FALSE;} BOOL GetSheetCount() { return m_nSheetCount;} virtual void IncrementSheetLockCount(); virtual void DecrementSheetLockCount(); BOOL IsSheetLocked() { return m_nSheetLockCount > 0;} BOOL IsNodeForPropSheet(){ return m_bNodeForPropSheet;} void NodeForPropSheet(){ m_bNodeForPropSheet = TRUE;} // // Misc. // virtual LPWSTR GetDescriptionBarText() { return L""; } virtual LPCWSTR GetString(int nCol) = 0; virtual int GetImageIndex(BOOL bOpenImage) = 0; virtual void Trace() { TRACE(_T("Name %s "), (LPCTSTR)m_szDisplayName);} void DeleteHelper(CComponentDataObject* pComponentData); protected: CString m_szDisplayName; // name of the item CContainerNode* m_pContainer; // back pointer to the container the node is in DWORD m_dwNodeFlags; LONG m_nSheetLockCount; // keeps track if a node has been locked by a property sheet LONG m_nSheetCount; // keeps track of the # of sheets the node has up BOOL m_bNodeForPropSheet; // This node is created for property sheet display only // Once the propertysheet is destroyed, node should be deleted. CTreeNode() { m_pContainer = NULL; m_nSheetLockCount = 0; m_dwNodeFlags = 0x0; //m_dwNodeFlags |= TN_FLAG_HIDDEN; m_nSheetCount = 0; m_bNodeForPropSheet = FALSE; } virtual LPCONTEXTMENUITEM2 OnGetContextMenuItemTable() { return NULL;} virtual BOOL OnAddMenuItem(LPCONTEXTMENUITEM2, long*) { return TRUE;} friend class CContainerNode; // to get access to the m_pContainer member // // Provides a default implementation for toolbar support // DECLARE_TOOLBAR_MAP() }; /////////////////////////////////////////////////////////////////////// // CNodeList // collection of nodes typedef CList CNodeListBase; class CNodeList : public CNodeListBase { public: BOOL RemoveNode(CTreeNode* p) { POSITION pos = Find(p); if (pos == NULL) return FALSE; RemoveAt(pos); return TRUE; } void RemoveAllNodes() { while (!IsEmpty()) delete RemoveTail(); } BOOL HasNode(CTreeNode* p) { return NULL != Find(p); } INT_PTR GetVisibleCount(); }; //////////////////////////////////////////////////////////////////////// // CContainerNode // node that can be a container of other nodes class CContainerNode : public CTreeNode { public: CContainerNode() { m_ID = 0; m_dwNodeFlags |= TN_FLAG_CONTAINER; m_nState = -1; m_dwErr = 0x0; m_nThreadLockCount = 0; } virtual ~CContainerNode() { ASSERT(m_nSheetLockCount == 0); RemoveAllChildrenFromList(); } CContainerNode* GetRootContainer() { return (m_pContainer != NULL) ? m_pContainer->GetRootContainer() : this; } // // Thread Helpers // void IncrementThreadLockCount(); void DecrementThreadLockCount(); BOOL IsThreadLocked() { return m_nThreadLockCount > 0;} virtual BOOL OnEnumerate(CComponentDataObject*, BOOL bAsync = TRUE) { bAsync; return TRUE;} // TRUE = add children in the list to UI // // Node state helpers // BOOL HasChildren() { return !m_containerChildList.IsEmpty() || !m_leafChildList.IsEmpty(); } void ForceEnumeration(CComponentDataObject* pComponentData); void MarkEnumerated(BOOL bEnum = TRUE); BOOL IsEnumerated() { ASSERT(IsContainer()); return (m_dwNodeFlags & TN_FLAG_CONTAINER_ENUM) ? TRUE : FALSE;} void MarkExpanded() { ASSERT(IsContainer()); m_dwNodeFlags |= TN_FLAG_CONTAINER_EXP; } BOOL IsExpanded() { ASSERT(IsContainer()); return (m_dwNodeFlags & TN_FLAG_CONTAINER_EXP) ? TRUE : FALSE;} void MarkEnumeratedAndLoaded(CComponentDataObject* pComponentData); void SetScopeID(HSCOPEITEM ID) { m_ID = ID;} HSCOPEITEM GetScopeID() { return m_ID;} BOOL AddedToScopePane() { return GetScopeID() != 0;} virtual CColumnSet* GetColumnSet() = 0; virtual LPCWSTR GetColumnID() = 0; virtual void SetFlagsDown(DWORD dwNodeFlags, BOOL bSet); void SetFlagsOnNonContainers(DWORD dwNodeFlags,BOOL bSet); // // child list mainpulation API's // CNodeList* GetContainerChildList() { return &m_containerChildList; } CNodeList* GetLeafChildList() { return &m_leafChildList; } BOOL AddChildToList(CTreeNode* p); BOOL AddChildToListSorted(CTreeNode* p, CComponentDataObject* pComponentData); BOOL RemoveChildFromList(CTreeNode* p); void RemoveAllChildrenFromList(); void RemoveAllContainersFromList() { m_containerChildList.RemoveAllNodes(); } void RemoveAllLeavesFromList() { m_leafChildList.RemoveAllNodes(); } // // given a node, it searches for it recursively and if successful it returns the // container the node is in // BOOL FindChild(CTreeNode* pNode, CTreeNode** ppContainer); BOOL AddChildToListAndUI(CTreeNode* pChildToAdd, CComponentDataObject* pComponentData); BOOL AddChildToListAndUISorted(CTreeNode* pChildToAdd, CComponentDataObject* pComponentData); virtual int Compare(CTreeNode* pNodeA, CTreeNode* pNodeB, int nCol, LPARAM lUserParam); virtual HRESULT CreatePropertyPagesHelper(LPPROPERTYSHEETCALLBACK, LONG_PTR, long) { return E_FAIL;} virtual BOOL OnRefresh(CComponentDataObject* pComponentData, CNodeList* pNodeList); virtual void OnColumnsChanged(int*, int) {} void RemoveAllChildrenHelper(CComponentDataObject* pComponentData); protected: virtual void OnChangeState(CComponentDataObject*) {} void AddCurrentChildrenToUI(CComponentDataObject* pComponentData); LONG m_nThreadLockCount; CNodeList m_leafChildList; // leaf contents of the node CNodeList m_containerChildList; // container contents of the node HSCOPEITEM m_ID; // ID when the item is inserted in the master tree int m_nState; // for general purpose finite state machine implementation DWORD m_dwErr; // for general purpose error handling }; //////////////////////////////////////////////////////////////////////// // CLeafNode // node that is not a container of other nodes class CLeafNode : public CTreeNode { public: }; /////////////////////////////////////////////////////////////////// // data nodes // the root, with folders in it class CRootData : public CContainerNode { public: CRootData(CComponentDataObject* pComponentData) { ASSERT(pComponentData != NULL); m_pComponentData = pComponentData; m_bDirty = FALSE; } virtual LPCWSTR GetString(int nCol) { if (nCol == 0) return GetDisplayName(); return g_lpszNullString; } CComponentDataObject* GetComponentDataObject(){ return m_pComponentData;} CTreeNode* GetNodeFromCookie(MMC_COOKIE cookie) { // cookie == 0 means root to enumerate if (cookie == NULL) { return (CTreeNode*)this; } else { CTreeNode* pNode = (CTreeNode*)cookie; CTreeNode* pContainer; if (FindChild(pNode,&pContainer)) { return pNode; } } return NULL; } // IStream manipulation helpers virtual HRESULT IsDirty() { return m_bDirty ? S_OK : S_FALSE; } virtual HRESULT Load(IStream*) { return S_OK; } virtual HRESULT Save(IStream*, BOOL) { return S_OK; } void SetDirtyFlag(BOOL bDirty) { m_bDirty = bDirty ;} private: CComponentDataObject* m_pComponentData; BOOL m_bDirty; CString m_szSnapinType; // constant part of the name loaded from resources }; ////////////////////////////////////////////////////////////////////// // CBackgroundThread class CBackgroundThread : public CWinThread { public: CBackgroundThread(); virtual ~CBackgroundThread(); void SetQueryObj(CQueryObj* pQueryObj); BOOL Start(CMTContainerNode* pNode, CComponentDataObject* pComponentData); virtual BOOL InitInstance() { return TRUE; } // MFC override virtual int Run(); // MFC override // REVIEWED-2002/03/08-JeffJon-There appears to be no danger // of getting stuck in the critical section void Lock() { ::EnterCriticalSection(&m_cs); } void Unlock() { ::LeaveCriticalSection(&m_cs); } void Abandon(); BOOL IsAbandoned(); BOOL OnAddToQueue(INT_PTR nCount); CObjBase* RemoveFromQueue(); BOOL IsQueueEmpty(); BOOL PostHaveData(); BOOL PostError(DWORD dwErr); BOOL PostExiting(); void AcknowledgeExiting() { VERIFY(0 != ::SetEvent(m_hEventHandle));} private: // communication with ComponentData object BOOL PostMessageToComponentDataRaw(UINT Msg, WPARAM wParam, LPARAM lParam); void WaitForExitAcknowledge(); CRITICAL_SECTION m_cs; // critical section to sync access to data HANDLE m_hEventHandle; // syncronization handle for shutdown notification CMTContainerNode* m_pContNode; // back pointer to node the thread is executing for CQueryObj* m_pQueryObj; // query object the thread is executing INT_PTR m_nQueueCountMax; // max size of the queue HWND m_hHiddenWnd; // handle to window to post messages BOOL m_bAbandoned; }; ////////////////////////////////////////////////////////////////////// // CQueryObj typedef CList CObjBaseList; class CQueryObj { public: CQueryObj() { m_dwErr = 0; m_pThread = NULL;} virtual ~CQueryObj() { while (!m_objQueue.IsEmpty()) delete m_objQueue.RemoveTail(); }; void SetThread(CBackgroundThread* pThread) { ASSERT(pThread != NULL); m_pThread = pThread; } CBackgroundThread* GetThread() {return m_pThread;} virtual BOOL Enumerate() { return FALSE;} virtual BOOL AddQueryResult(CObjBase* pObj) { BOOL bRes = FALSE; if (m_pThread != NULL) { BOOL bPostedHaveDataMessage = FALSE; m_pThread->Lock(); bRes = NULL != m_objQueue.AddTail(pObj); bPostedHaveDataMessage = m_pThread->OnAddToQueue(m_objQueue.GetCount()); m_pThread->Unlock(); // wait for the queue length to go down to zero if (bPostedHaveDataMessage) { INT_PTR nQueueCount = 0; do { m_pThread->Lock(); nQueueCount = m_objQueue.GetCount(); m_pThread->Unlock(); if (m_pThread->IsAbandoned()) { break; } if (nQueueCount > 0) { ::Sleep(100); } } while (nQueueCount > 0); } // if } else { bRes = NULL != m_objQueue.AddTail(pObj); } ASSERT(bRes); return bRes; } virtual void OnError(DWORD dwErr) { if (m_pThread != NULL) { m_pThread->Lock(); m_dwErr = dwErr; m_pThread->Unlock(); m_pThread->PostError(dwErr); } else { m_dwErr = dwErr; } } CObjBaseList* GetQueue() { return &m_objQueue;} DWORD GetError() { if (m_pThread != NULL) { m_pThread->Lock(); DWORD dwErr = m_dwErr; m_pThread->Unlock(); return dwErr; } else { return m_dwErr; } } private: CBackgroundThread* m_pThread; // back pointer, if in the context of a thread CObjBaseList m_objQueue; // queue for results DWORD m_dwErr; // error code, if any }; //////////////////////////////////////////////////////////////////////// // CMTContainerNode // container that can do operations from a secondary thread class CMTContainerNode : public CContainerNode { public: CMTContainerNode() { m_pThread = NULL; } virtual ~CMTContainerNode(); virtual BOOL OnEnumerate(CComponentDataObject* pComponentData, BOOL bAsync = TRUE); virtual BOOL OnRefresh(CComponentDataObject* pComponentData, CNodeList* pNodeList); protected: // thread creation virtual CBackgroundThread* CreateThreadObject() { return new CBackgroundThread(); // override if need derived tipe of object } // query creation virtual CQueryObj* OnCreateQuery() // override to create a user defined query object { return new CQueryObj(); // return a do-nothing query } // main message handler for thread messages virtual void OnThreadHaveDataNotification(CComponentDataObject* pComponentDataObject); virtual void OnThreadErrorNotification(DWORD dwErr, CComponentDataObject* pComponentDataObject); virtual void OnThreadExitingNotification(CComponentDataObject* pComponentDataObject); virtual void OnHaveData(CObjBase*, CComponentDataObject*) {} virtual void OnError(DWORD dwErr) { m_dwErr = dwErr; } BOOL StartBackgroundThread(CComponentDataObject* pComponentData, BOOL bAsync = TRUE); CBackgroundThread* GetThread() { ASSERT(m_pThread != NULL); return m_pThread;} void AbandonThread(CComponentDataObject* pComponentData); private: CBackgroundThread* m_pThread; // pointer to thread object executing the code friend class CHiddenWnd; // to get OnThreadNotification() friend class CRunningThreadTable; // to get AbandonThread() }; #endif // _TREEDATA_H