718 lines
22 KiB
C++
718 lines
22 KiB
C++
// 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<class TYPE>
|
|
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 <UINT nResID> 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;}
|
|
|
|
//
|
|
// 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
|
|
|
|
CTreeNode()
|
|
{
|
|
m_pContainer = NULL;
|
|
m_nSheetLockCount = 0;
|
|
m_dwNodeFlags = 0x0; //m_dwNodeFlags |= TN_FLAG_HIDDEN;
|
|
m_nSheetCount = 0;
|
|
}
|
|
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<CTreeNode*,CTreeNode*> 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);
|
|
}
|
|
};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// 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
|
|
|
|
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<CObjBase*,CObjBase*> 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
|