//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1998 - 1999 // // File: treedat_.cpp // //-------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////////////////// // Miscellanea LPCWSTR g_lpszNullString = L"\0"; /////////////////////////////////////////////////////////////////////// // Global Helper functions BOOL LoadContextMenuResources(MENUMAP* pMenuMap) { HINSTANCE hInstance = _Module.GetModuleInstance(); for (int i = 0; pMenuMap->ctxMenu[i].strName; i++) { if (0 == ::LoadString(hInstance, pMenuMap->dataRes[i].uResID, pMenuMap->dataRes[i].szBuffer, MAX_CONTEXT_MENU_STRLEN*2)) return FALSE; pMenuMap->ctxMenu[i].strName = pMenuMap->dataRes[i].szBuffer; for (WCHAR* pCh = pMenuMap->dataRes[i].szBuffer; (*pCh) != NULL; pCh++) { if ( (*pCh) == L'\n') { pMenuMap->ctxMenu[i].strStatusBarText = (pCh+1); (*pCh) = NULL; break; } } } return TRUE; } BOOL LoadResultHeaderResources(RESULT_HEADERMAP* pHeaderMap, int nCols) { HINSTANCE hInstance = _Module.GetModuleInstance(); for ( int i = 0; i < nCols ; i++) { if ( 0 == ::LoadString(hInstance, pHeaderMap[i].uResID, pHeaderMap[i].szBuffer, MAX_RESULT_HEADER_STRLEN)) return TRUE; } return TRUE; } //////////////////////////////////////////////////////////////////////// // CTreeNode BEGIN_TOOLBAR_MAP(CTreeNode) END_TOOLBAR_MAP() BOOL CTreeNode::HasContainer(CContainerNode* pContainerNode) { if (m_pContainer == NULL) return FALSE; // root if (m_pContainer == pContainerNode) return TRUE; // got it return m_pContainer->HasContainer(pContainerNode); } HRESULT CTreeNode::GetResultViewType(CComponentDataObject* pComponentData, LPOLESTR* ppViewType, long* pViewOptions) { if (pComponentData->IsMultiSelect()) { *pViewOptions = MMC_VIEW_OPTIONS_MULTISELECT; } else { *pViewOptions = MMC_VIEW_OPTIONS_NONE; } *ppViewType = NULL; return S_FALSE; } void CTreeNode::Show(BOOL bShow, CComponentDataObject* pComponentData) { if (bShow) { ASSERT(m_dwNodeFlags & TN_FLAG_HIDDEN); // must be currently hidden SetFlagsDown(TN_FLAG_HIDDEN,FALSE); // mark it visible VERIFY(SUCCEEDED(pComponentData->AddNode(this))); } else { ASSERT(!(m_dwNodeFlags & TN_FLAG_HIDDEN)); // must be currently visible SetFlagsDown(TN_FLAG_HIDDEN,TRUE); // mark it hidden VERIFY(SUCCEEDED(pComponentData->DeleteNode(this))); if (IsContainer()) { ((CContainerNode*)this)->RemoveAllChildrenFromList(); ((CContainerNode*)this)->MarkEnumerated(FALSE); } } } void CTreeNode::SetFlagsDown(DWORD dwNodeFlags, BOOL bSet) { if (bSet) m_dwNodeFlags |= dwNodeFlags; else m_dwNodeFlags &= ~dwNodeFlags; } void CTreeNode::SetFlagsUp(DWORD dwNodeFlags, BOOL bSet) { if (bSet) m_dwNodeFlags |= dwNodeFlags; else m_dwNodeFlags &= ~dwNodeFlags; if (m_pContainer != NULL) { ASSERT(m_pContainer != this); m_pContainer->SetFlagsUp(dwNodeFlags, bSet); } } // // Property Page methods // void CTreeNode::ShowPageForNode(CComponentDataObject* pComponentDataObject) { ASSERT(pComponentDataObject != NULL); pComponentDataObject->GetPropertyPageHolderTable()->BroadcastSelectPage(this, -1); } BOOL CTreeNode::HasPropertyPages(DATA_OBJECT_TYPES, BOOL* pbHideVerb, CNodeList*) { *pbHideVerb = TRUE; return FALSE; } // // Menu Item methods // HRESULT CTreeNode::OnAddMenuItems(IContextMenuCallback2* pContextMenuCallback2, DATA_OBJECT_TYPES type, long *pInsertionAllowed, CNodeList* pNodeList) { HRESULT hr = S_OK; LPCONTEXTMENUITEM2 pContextMenuItem = NULL; if (pNodeList->GetCount() == 1) // single selection { pContextMenuItem = OnGetContextMenuItemTable(); if (pContextMenuItem == NULL) return hr; // // Loop through and add each of the menu items // for (LPCONTEXTMENUITEM2 m = pContextMenuItem; m->strName; m++) { if ( ( (*pInsertionAllowed & CCM_INSERTIONALLOWED_NEW) && (m->lInsertionPointID == CCM_INSERTIONPOINTID_PRIMARY_NEW) ) || ( (*pInsertionAllowed & CCM_INSERTIONALLOWED_TASK) && (m->lInsertionPointID == CCM_INSERTIONPOINTID_PRIMARY_TASK) ) || ( (*pInsertionAllowed & CCM_INSERTIONALLOWED_VIEW) && (m->lInsertionPointID == CCM_INSERTIONPOINTID_PRIMARY_VIEW) ) || ( (*pInsertionAllowed & CCM_INSERTIONALLOWED_TOP) && (m->lInsertionPointID == CCM_INSERTIONPOINTID_PRIMARY_TOP) ) ) { // make a temporary copy that can be modified CONTEXTMENUITEM2 tempItem; ::memcpy(&tempItem, m, sizeof(CONTEXTMENUITEM2)); if (OnAddMenuItem(&tempItem, pInsertionAllowed)) { hr = pContextMenuCallback2->AddItem(&tempItem); if (FAILED(hr)) break; } } } } else if (pNodeList->GetCount() > 1) // multiple selection { hr = OnAddMenuItemsMultipleSelect(pContextMenuCallback2, type, pInsertionAllowed, pNodeList); } return hr; } BOOL CTreeNode::OnSetRenameVerbState(DATA_OBJECT_TYPES, BOOL* pbHide, CNodeList*) { *pbHide = TRUE; return FALSE; } BOOL CTreeNode::OnSetDeleteVerbState(DATA_OBJECT_TYPES, BOOL* pbHide, CNodeList*) { *pbHide = TRUE; return FALSE; } BOOL CTreeNode::OnSetRefreshVerbState(DATA_OBJECT_TYPES, BOOL* pbHide, CNodeList*) { *pbHide = TRUE; return FALSE; } BOOL CTreeNode::OnSetCutVerbState(DATA_OBJECT_TYPES, BOOL* pbHide, CNodeList*) { *pbHide = TRUE; return FALSE; } BOOL CTreeNode::OnSetCopyVerbState(DATA_OBJECT_TYPES, BOOL* pbHide, CNodeList*) { *pbHide = TRUE; return FALSE; } BOOL CTreeNode::OnSetPasteVerbState(DATA_OBJECT_TYPES, BOOL* pbHide, CNodeList*) { *pbHide = TRUE; return FALSE; } BOOL CTreeNode::OnSetPrintVerbState(DATA_OBJECT_TYPES, BOOL* pbHide, CNodeList*) { *pbHide = TRUE; return FALSE; } MMC_CONSOLE_VERB CTreeNode::GetDefaultVerb(DATA_OBJECT_TYPES type, CNodeList* pNodeList) { ASSERT((type == CCT_SCOPE) || (type == CCT_RESULT)); if (type == CCT_SCOPE) return MMC_VERB_OPEN; BOOL bHideVerbDummy; if (HasPropertyPages(type, &bHideVerbDummy, pNodeList)) return MMC_VERB_PROPERTIES; return MMC_VERB_NONE; } void CTreeNode::OnSetVerbState(LPCONSOLEVERB pConsoleVerb, DATA_OBJECT_TYPES type, CNodeList* pNodeList) { // // Use the virtual functions to get the verb state // BOOL bHideCut; BOOL bCanCut = OnSetCutVerbState(type, &bHideCut, pNodeList); pConsoleVerb->SetVerbState(MMC_VERB_CUT, HIDDEN, bHideCut); pConsoleVerb->SetVerbState(MMC_VERB_CUT, ENABLED, bCanCut); BOOL bHideCopy; BOOL bCanCopy = OnSetCopyVerbState(type, &bHideCopy, pNodeList); pConsoleVerb->SetVerbState(MMC_VERB_COPY, HIDDEN, bHideCopy); pConsoleVerb->SetVerbState(MMC_VERB_COPY, ENABLED, bCanCopy); BOOL bHidePaste; BOOL bCanPaste = OnSetPasteVerbState(type, &bHidePaste, pNodeList); pConsoleVerb->SetVerbState(MMC_VERB_PASTE, HIDDEN, bHidePaste); pConsoleVerb->SetVerbState(MMC_VERB_PASTE, ENABLED, bCanPaste); BOOL bHidePrint; BOOL bCanPrint = OnSetPrintVerbState(type, &bHidePrint, pNodeList); pConsoleVerb->SetVerbState(MMC_VERB_PRINT, HIDDEN, bHidePrint); pConsoleVerb->SetVerbState(MMC_VERB_PRINT, ENABLED, bCanPrint); BOOL bHideRename; BOOL bCanRename = OnSetRenameVerbState(type, &bHideRename, pNodeList); pConsoleVerb->SetVerbState(MMC_VERB_RENAME, HIDDEN, bHideRename); pConsoleVerb->SetVerbState(MMC_VERB_RENAME, ENABLED, bCanRename); // MMC_VERB_PROPERTIES BOOL bHideProperties; BOOL bHasProperties = HasPropertyPages(type, &bHideProperties, pNodeList); pConsoleVerb->SetVerbState(MMC_VERB_PROPERTIES, ENABLED, bHasProperties); pConsoleVerb->SetVerbState(MMC_VERB_PROPERTIES, HIDDEN, bHideProperties); // MMC_VERB_DELETE BOOL bHideDelete; BOOL bCanDelete = OnSetDeleteVerbState(type, &bHideDelete, pNodeList); pConsoleVerb->SetVerbState(MMC_VERB_DELETE, ENABLED, bCanDelete); pConsoleVerb->SetVerbState(MMC_VERB_DELETE, HIDDEN, bHideDelete); // MMC_VERB_REFRESH BOOL bHideRefresh; BOOL bCanRefresh = OnSetRefreshVerbState(type, &bHideRefresh, pNodeList); pConsoleVerb->SetVerbState(MMC_VERB_REFRESH, ENABLED, bCanRefresh); pConsoleVerb->SetVerbState(MMC_VERB_REFRESH, HIDDEN, bHideRefresh); } HRESULT CTreeNode::OnSetToolbarVerbState(IToolbar*, CNodeList*) { HRESULT hr = S_OK; // // Set the button state for each button on the toolbar using // hr = pToolbar->SetButtonState(event, MMC_BUTTON_STATE, bState); // return hr; } void CTreeNode::DeleteHelper(CComponentDataObject* pComponentData) { ASSERT(pComponentData != NULL); ASSERT(m_pContainer != NULL); ASSERT((CTreeNode*)m_pContainer != this); CContainerNode* pCont = m_pContainer; VERIFY(m_pContainer->RemoveChildFromList(this)); ASSERT(m_pContainer == NULL); m_pContainer = pCont; // not in the container's list of children, but still needed // remove from UI only if the container is visible if (pCont->IsVisible()) VERIFY(SUCCEEDED(pComponentData->DeleteNode(this))); // remove from the UI } void CTreeNode::IncrementSheetLockCount() { ++m_nSheetLockCount; if (m_pContainer != NULL) m_pContainer->IncrementSheetLockCount(); } void CTreeNode::DecrementSheetLockCount() { --m_nSheetLockCount; if (m_pContainer != NULL) m_pContainer->DecrementSheetLockCount(); } void CTreeNode::OnPropertyChange(CComponentDataObject* pComponentData, BOOL, long changeMask) { // function called when the PPHolder successfully updated the node ASSERT(pComponentData != NULL); VERIFY(SUCCEEDED(pComponentData->ChangeNode(this, changeMask))); } void CTreeNode::OnCreateSheet() { ++m_nSheetCount; IncrementSheetLockCount(); SetFlagsUp(TN_FLAG_HAS_SHEET, TRUE); } void CTreeNode::OnDeleteSheet() { DecrementSheetLockCount(); --m_nSheetCount; SetFlagsUp(TN_FLAG_HAS_SHEET,FALSE); } //////////////////////////////////////////////////////////////////////// // CContainerNode void CContainerNode::IncrementThreadLockCount() { ++m_nThreadLockCount; if (m_pContainer != NULL) m_pContainer->IncrementThreadLockCount(); } void CContainerNode::DecrementThreadLockCount() { --m_nThreadLockCount; if (m_pContainer != NULL) m_pContainer->DecrementThreadLockCount(); } BOOL CContainerNode::OnRefresh(CComponentDataObject* pComponentData, CNodeList* pNodeList) { BOOL bRet = TRUE; if (pNodeList->GetCount() == 1) // single selection { if (IsSheetLocked()) { if (!CanCloseSheets()) return FALSE; pComponentData->GetPropertyPageHolderTable()->DeleteSheetsOfNode(this); } ASSERT(!IsSheetLocked()); RemoveAllChildrenHelper(pComponentData); ASSERT(!HasChildren()); OnEnumerate(pComponentData); AddCurrentChildrenToUI(pComponentData); MarkEnumerated(); } else // multiple selection { POSITION pos = pNodeList->GetHeadPosition(); while (pos != NULL) { CTreeNode* pNode = pNodeList->GetNext(pos); ASSERT(pNode != NULL); // // Have each node refresh itself // CNodeList nodeList; nodeList.AddTail(pNode); if (!pNode->OnRefresh(pComponentData, &nodeList)) { bRet = FALSE; } } } return bRet; } BOOL CContainerNode::RemoveChildFromList(CTreeNode* p) { if (p->IsContainer()) { if (m_containerChildList.RemoveNode(p)) { p->m_pContainer = NULL; return TRUE; } } else { if (m_leafChildList.RemoveNode(p)) { p->m_pContainer = NULL; return TRUE; } } return FALSE; } void CContainerNode::RemoveAllChildrenHelper(CComponentDataObject* pComponentData) { ASSERT(pComponentData != NULL); // remove from the UI VERIFY(SUCCEEDED(pComponentData->RemoveAllChildren(this))); // remove from memory, recursively from the bottom RemoveAllChildrenFromList(); } void CContainerNode::AddCurrentChildrenToUI(CComponentDataObject* pComponentData) { POSITION pos; // // Add leaves // for( pos = m_leafChildList.GetHeadPosition(); pos != NULL; ) { CTreeNode* pCurrentChild = m_leafChildList.GetNext(pos); VERIFY(SUCCEEDED(pComponentData->AddNode(pCurrentChild))); } // // Add Containers // for( pos = m_containerChildList.GetHeadPosition(); pos != NULL; ) { CTreeNode* pCurrentChild = m_containerChildList.GetNext(pos); VERIFY(SUCCEEDED(pComponentData->AddNode(pCurrentChild))); } } void CContainerNode::SetFlagsDown(DWORD dwNodeFlags, BOOL bSet) { CTreeNode::SetFlagsDown(dwNodeFlags,bSet); // scan the list of children POSITION pos; for( pos = m_containerChildList.GetHeadPosition(); pos != NULL; ) { CTreeNode* pCurrentChild = m_containerChildList.GetNext(pos); pCurrentChild->SetFlagsDown(dwNodeFlags,bSet); } for( pos = m_leafChildList.GetHeadPosition(); pos != NULL; ) { CTreeNode* pCurrentChild = m_leafChildList.GetNext(pos); pCurrentChild->SetFlagsDown(dwNodeFlags,bSet); } } void CContainerNode::SetFlagsOnNonContainers(DWORD dwNodeFlags, BOOL bSet) { // do not set on urselves, we are a container // scan the list of children POSITION pos; for( pos = m_leafChildList.GetHeadPosition(); pos != NULL; ) { CTreeNode* pCurrentChild = m_leafChildList.GetNext(pos); pCurrentChild->SetFlagsDown(dwNodeFlags,bSet); } for (pos = m_containerChildList.GetHeadPosition(); pos != NULL; ) { CTreeNode* pCurrentChild = m_containerChildList.GetNext(pos); ((CContainerNode*)pCurrentChild)->SetFlagsOnNonContainers(dwNodeFlags,bSet); } } BOOL CContainerNode::AddChildToList(CTreeNode* p) { BOOL bRet = FALSE; p->m_pContainer = this; if (p->IsContainer()) { bRet = NULL != m_containerChildList.AddTail(p); } else { bRet = NULL != m_leafChildList.AddTail(p); } return bRet; } BOOL CContainerNode::FindChild(CTreeNode* pNode, CTreeNode** ppContainer) { *ppContainer = NULL; if (pNode == NULL) return FALSE; // no sense in continuing if (pNode == this) { *ppContainer = m_pContainer; return TRUE; // the node is ourselves } // // If we are looking for a leaf node search the list of leaves first // if (!pNode->IsContainer()) { POSITION pos; for (pos = m_leafChildList.GetHeadPosition(); pos != NULL; ) { CLeafNode* pCurrentLeafNode = (CLeafNode*)m_leafChildList.GetNext(pos); ASSERT(pCurrentLeafNode != NULL); if (pCurrentLeafNode == pNode) { *ppContainer = this; return TRUE; } } } // // scan and recurse the containers if necessary // POSITION contPos; for( contPos = m_containerChildList.GetHeadPosition(); contPos != NULL; ) { CContainerNode* pCurrentChild = (CContainerNode*)m_containerChildList.GetNext(contPos); ASSERT(pCurrentChild != NULL); if (pCurrentChild == pNode) { *ppContainer = this; return TRUE; // we directly contain the node } // // if the current node is a container, look inside it // if (pCurrentChild->FindChild(pNode,ppContainer)) { return TRUE; // got it in the recursion } } return FALSE; // not found } BOOL CContainerNode::AddChildToListAndUI(CTreeNode* pChildToAdd, CComponentDataObject* pComponentData) { ASSERT(pComponentData != NULL); VERIFY(AddChildToList(pChildToAdd)); // at the end of the list of children ASSERT(pChildToAdd->GetContainer() == this); // inserted underneath // add to UI only if currently visible and already expanded if (!IsVisible() || !IsExpanded()) return TRUE; return SUCCEEDED(pComponentData->AddNode(pChildToAdd)); // add to the UI } BOOL CContainerNode::AddChildToListAndUISorted(CTreeNode* pChildToAdd, CComponentDataObject* pComponentData) { ASSERT(pComponentData != NULL); VERIFY(AddChildToListSorted(pChildToAdd, pComponentData)); ASSERT(pChildToAdd->GetContainer() == this); // inserted underneath // add to UI only if currently visible and already expanded if (!IsVisible() || !IsExpanded()) return TRUE; return SUCCEEDED(pComponentData->AddNodeSorted(pChildToAdd)); // add to the UI } BOOL CContainerNode::AddChildToListSorted(CTreeNode* p, CComponentDataObject*) { // // Containers will be sorted with respect to containers and leaves will be // sorted with respect to leaves but they won't be intermingled. // p->m_pContainer = this; CNodeList* pChildNodeList = NULL; if (p->IsContainer()) { pChildNodeList = &m_containerChildList; } else { pChildNodeList = &m_leafChildList; } // // Find the position to insert the node in the list in sorted order // POSITION pos = pChildNodeList->GetHeadPosition(); while (pos != NULL) { CTreeNode* pNodeInList = pChildNodeList->GetAt(pos); if (_wcsicoll(p->GetDisplayName(), pNodeInList->GetDisplayName()) < 0) { break; } pChildNodeList->GetNext(pos); } if (pos == NULL) { return NULL != pChildNodeList->AddTail(p); } return NULL != pChildNodeList->InsertBefore(pos, p); } void CContainerNode::RemoveAllChildrenFromList() { RemoveAllContainersFromList(); RemoveAllLeavesFromList(); } int CContainerNode::Compare(CTreeNode* pNodeA, CTreeNode* pNodeB, int nCol, LPARAM) { // default sorting behavior LPCTSTR lpszA = pNodeA->GetString(nCol); LPCTSTR lpszB = pNodeB->GetString(nCol); // cannot process NULL strings, have to use "" ASSERT(lpszA != NULL); ASSERT(lpszB != NULL); return _tcsicoll( (lpszA != NULL) ? lpszA : g_lpszNullString, (lpszB != NULL) ? lpszB : g_lpszNullString); } void CContainerNode::ForceEnumeration(CComponentDataObject* pComponentData) { if (IsEnumerated()) return; OnEnumerate(pComponentData); MarkEnumerated(); } void CContainerNode::MarkEnumerated(BOOL bEnum) { ASSERT(IsContainer()); if (bEnum) m_dwNodeFlags |= TN_FLAG_CONTAINER_ENUM; else m_dwNodeFlags &= ~TN_FLAG_CONTAINER_ENUM; } void CContainerNode::MarkEnumeratedAndLoaded(CComponentDataObject* pComponentData) { MarkEnumerated(); OnChangeState(pComponentData); // move to loading OnChangeState(pComponentData); // move to loaded } ///////////////////////////////////////////////////////////////////////////// // CBackgroundThread CBackgroundThread::CBackgroundThread() { m_pQueryObj = NULL; m_bAutoDelete = FALSE; m_bAbandoned = FALSE; m_pContNode = NULL; m_hEventHandle = NULL; ExceptionPropagatingInitializeCriticalSection(&m_cs); m_nQueueCountMax = 10; } CBackgroundThread::~CBackgroundThread() { TRACE(_T("CBackgroundThread::~CBackgroundThread()\n")); ASSERT(IsAbandoned() || IsQueueEmpty()); ::DeleteCriticalSection(&m_cs); if (m_hEventHandle != NULL) { VERIFY(::CloseHandle(m_hEventHandle)); m_hEventHandle = NULL; } if (m_pQueryObj != NULL) { delete m_pQueryObj; m_pQueryObj = NULL; } } void CBackgroundThread::SetQueryObj(CQueryObj* pQueryObj) { ASSERT(pQueryObj != NULL); m_pQueryObj = pQueryObj; m_pQueryObj->SetThread(this); } BOOL CBackgroundThread::Start(CMTContainerNode* pNode, CComponentDataObject* pComponentData) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); ASSERT(m_pContNode == NULL); m_pContNode = pNode; m_hHiddenWnd = pComponentData->GetHiddenWindow(); ASSERT(m_hEventHandle == NULL); // cannot call start twice or reuse the same C++ object m_hEventHandle = ::CreateEvent(NULL,TRUE /*bManualReset*/,FALSE /*signalled*/, NULL); if (m_hEventHandle == NULL) return FALSE; return CreateThread(); } int CBackgroundThread::Run() { ASSERT(m_pContNode != NULL); ASSERT(m_pQueryObj != NULL); TRACE(_T("CBackgroundThread::Run() started\n")); while (m_pQueryObj->Enumerate()); // before exiting, have to make sure there are no items in the queue if (!IsQueueEmpty()) VERIFY(PostHaveData()); VERIFY(PostExiting()); // wait for the main thread to acknowledge the exiting message WaitForExitAcknowledge(); ASSERT(IsAbandoned() || IsQueueEmpty()); // we cannot lose items in the queue TRACE(_T("CBackgroundThread::Run() terminated\n")); return 0; } void CBackgroundThread::Abandon() { Lock(); TRACE(_T("CBackgroundThread::Abandon()\n")); m_bAutoDelete = TRUE; m_bAbandoned = TRUE; Unlock(); VERIFY(0 != ::SetEvent(m_hEventHandle)); } BOOL CBackgroundThread::IsAbandoned() { Lock(); BOOL b = m_bAbandoned; Unlock(); return b; } BOOL CBackgroundThread::OnAddToQueue(INT_PTR nCount) { BOOL bPostedMessage = FALSE; if (nCount >= m_nQueueCountMax) { VERIFY(PostHaveData()); bPostedMessage = TRUE; } return bPostedMessage; } CObjBase* CBackgroundThread::RemoveFromQueue() { Lock(); ASSERT(m_pQueryObj != NULL); CObjBaseList* pQueue = m_pQueryObj->GetQueue(); CObjBase* p = pQueue->IsEmpty() ? NULL : pQueue->RemoveHead(); Unlock(); return p; } BOOL CBackgroundThread::IsQueueEmpty() { Lock(); ASSERT(m_pQueryObj != NULL); CObjBaseList* pQueue = m_pQueryObj->GetQueue(); BOOL bRes = pQueue->IsEmpty(); Unlock(); return bRes; } BOOL CBackgroundThread::PostHaveData() { return PostMessageToComponentDataRaw(CHiddenWnd::s_NodeThreadHaveDataNotificationMessage, (WPARAM)m_pContNode, (LPARAM)0); } BOOL CBackgroundThread::PostError(DWORD dwErr) { return PostMessageToComponentDataRaw(CHiddenWnd::s_NodeThreadErrorNotificationMessage, (WPARAM)m_pContNode, (LPARAM)dwErr); } BOOL CBackgroundThread::PostExiting() { return PostMessageToComponentDataRaw(CHiddenWnd::s_NodeThreadExitingNotificationMessage, (WPARAM)m_pContNode, (LPARAM)0); } BOOL CBackgroundThread::PostMessageToComponentDataRaw(UINT Msg, WPARAM wParam, LPARAM lParam) { BOOL b = IsAbandoned(); if (b) { return TRUE; // no need to post } ASSERT(m_pContNode != NULL); ASSERT(m_hHiddenWnd != NULL); ASSERT(::IsWindow(m_hHiddenWnd)); return ::PostMessage(m_hHiddenWnd, Msg, wParam, lParam); } void CBackgroundThread::WaitForExitAcknowledge() { VERIFY(WAIT_OBJECT_0 == ::WaitForSingleObject(m_hEventHandle,INFINITE)); } //////////////////////////////////////////////////////////////////////// // CMTContainerNode CMTContainerNode::~CMTContainerNode() { ASSERT(m_pThread == NULL); } BOOL CMTContainerNode::OnEnumerate(CComponentDataObject* pComponentData, BOOL bAsync) { OnChangeState(pComponentData); VERIFY(StartBackgroundThread(pComponentData, bAsync)); return FALSE; // children not added, the thread will add them later } BOOL CMTContainerNode::OnRefresh(CComponentDataObject* pComponentData, CNodeList* pNodeList) { BOOL bRet = TRUE; if (pNodeList->GetCount() == 1) // single selection { BOOL bLocked = IsThreadLocked(); ASSERT(!bLocked); // cannot do refresh on locked node, the UI should prevent this if (bLocked) return FALSE; if (IsSheetLocked()) { if (!CanCloseSheets()) { pComponentData->GetPropertyPageHolderTable()->BroadcastSelectPage(this, -1); return FALSE; } pComponentData->GetPropertyPageHolderTable()->DeleteSheetsOfNode(this); } ASSERT(!IsSheetLocked()); RemoveAllChildrenHelper(pComponentData); ASSERT(!HasChildren()); OnEnumerate(pComponentData); // will spawn a thread to do enumeration MarkEnumerated(); } else // multiple selection { POSITION pos = pNodeList->GetHeadPosition(); while (pos != NULL) { CTreeNode* pNode = pNodeList->GetNext(pos); ASSERT(pNode != NULL); CNodeList nodeList; nodeList.AddTail(pNode); if (!pNode->OnRefresh(pComponentData, &nodeList)) { bRet = FALSE; } } } return TRUE; } void CMTContainerNode::AbandonThread(CComponentDataObject* pComponentData) { if(m_pThread == NULL) // nothing running return; m_pThread->Abandon(); m_pThread = NULL; pComponentData->GetRunningThreadTable()->Remove(this); } BOOL CMTContainerNode::StartBackgroundThread(CComponentDataObject* pComponentData, BOOL bAsync) { ASSERT(m_pThread == NULL); // nothing running // notify the UI to change icon, if needed VERIFY(SUCCEEDED(pComponentData->ChangeNode(this, CHANGE_RESULT_ITEM_ICON))); m_pThread = CreateThreadObject(); ASSERT(m_pThread != NULL); m_pThread->SetQueryObj(OnCreateQuery()); BOOL bRes = m_pThread->Start(this, pComponentData); if (bRes) { pComponentData->GetRunningThreadTable()->Add(this); // we need to call UpdateVerbState() because the lock count changed // by adding the node from the running thread table VERIFY(SUCCEEDED(pComponentData->UpdateVerbState(this))); } // // If we don't want this call to be asynchronous then we have to wait for // the thread to finish // if (!bAsync) { pComponentData->WaitForThreadExitMessage(this); } return bRes; } void CMTContainerNode::OnThreadHaveDataNotification(CComponentDataObject* pComponentDataObject) { ASSERT(m_pThread != NULL); ASSERT(IsThreadLocked()); // do data transfer from thread queue CObjBase* p = m_pThread->RemoveFromQueue(); while (p) { // add new node to the list of children and propagate to the UI OnHaveData(p,pComponentDataObject); p = m_pThread->RemoveFromQueue(); } } void CMTContainerNode::OnThreadErrorNotification(DWORD dwErr, CComponentDataObject*) { ASSERT(m_pThread != NULL); ASSERT(IsThreadLocked()); OnError(dwErr); } void CMTContainerNode::OnThreadExitingNotification(CComponentDataObject* pComponentDataObject) { ASSERT(m_pThread != NULL); ASSERT(IsThreadLocked()); #if (TRUE) // let the thread know it can shut down m_pThread->AcknowledgeExiting(); VERIFY(WAIT_OBJECT_0 == ::WaitForSingleObject(m_pThread->m_hThread,INFINITE)); OnChangeState(pComponentDataObject); delete m_pThread; m_pThread = NULL; pComponentDataObject->GetRunningThreadTable()->Remove(this); // we need to call UpdateVerbState() because the lock count changed // by removing the node from the running thread table VERIFY(SUCCEEDED(pComponentDataObject->UpdateVerbState(this))); TRACE(_T("OnThreadExitingNotification()\n")); #else // maybe better way of doing it??? // we are going to detach from the thread, so make copies of variables HANDLE hThread = m_pThread->m_hThread; CBackgroundThread* pThread = m_pThread; AbandonThread(pComponentDataObject); // sets m_pThread = NULL // acknowledge to thread pThread->AcknowledgeExiting(); VERIFY(WAIT_OBJECT_0 == ::WaitForSingleObject(hThread,INFINITE)); OnChangeState(pComponentDataObject); #endif VERIFY(SUCCEEDED(pComponentDataObject->SortResultPane(this))); } ///////////////////////////////////////////////////////////////////////////////