// NetTree.cpp : implementation file // #include "stdafx.h" #include "NetTree.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // Global variables extern TCHAR pszTreeEvent[]; ///////////////////////////////////////////////////////////////////////////// // CNetTreeCtrl CNetTreeCtrl::CNetTreeCtrl() : m_pThread(NULL), m_bExitThread(FALSE), m_event(TRUE, TRUE, pszTreeEvent) { // Get a handle to the process heap m_hHeap = ::GetProcessHeap(); ASSERT(m_hHeap != NULL); } CNetTreeCtrl::~CNetTreeCtrl() { // Make sure the thread knows it's time to terminate. NotifyThread(TRUE); // Create an event object to match the tree thread event object. CEvent event(TRUE, TRUE, pszTreeEvent); // Create a lock object for the event object. CSingleLock lock(&event); // Lock the lock object and make the main thread wait for the // threads to signal their event objects. lock.Lock(); // Free all of the pointers to LPTSTRs in the list POSITION pos = m_ptrlistStrings.GetHeadPosition(); while (pos != NULL) { // Memory deallocation fails if there's a null char // at the end of the string. LPTSTR psz = m_ptrlistStrings.GetNext(pos); *(::_tcslen(psz) + psz) = (TCHAR)0xFD; delete[] psz; } // Free all of the pointers to NETRESOURCE structs in the list pos = m_ptrlistContainers.GetHeadPosition(); while (pos != NULL) { delete m_ptrlistContainers.GetNext(pos); } } BEGIN_MESSAGE_MAP(CNetTreeCtrl, CTreeCtrl) //{{AFX_MSG_MAP(CNetTreeCtrl) ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemExpanding) ON_WM_SETCURSOR() ON_WM_DESTROY() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // Static member functions UINT CNetTreeCtrl::FillTree(LPVOID pParam) { CEvent event(TRUE, TRUE, pszTreeEvent); CLicCompWizApp* pApp = (CLicCompWizApp*)AfxGetApp(); PTREEINFO pti = (PTREEINFO)pParam; CNetTreeCtrl* pTree = (CNetTreeCtrl*)pti->pTree; BOOL bResult = FALSE; DWORD dwEntries = 0xFFFFFFFF; LPVOID lpvBuffer = NULL; HANDLE hEnum = NULL; // Because this function may call itself, keep a usage count // so that pti is freed only when the first instance returns. static USHORT uUsage = 0; // Keep a handle to the heap in case the CNetTreeCtrl object // goes away before the thread ends. HANDLE hHeap = pTree->m_hHeap; DWORD dwResult; LPNETRESOURCE pnrRoot; HTREEITEM hTreeItem, hTreeExpand; hTreeItem = hTreeExpand = NULL; try { // Unsignal the event object. event.ResetEvent(); // Show the wait cursor pTree->BeginWaitCursor(); // Exit if the handle to the heap is invalid. if (hHeap == NULL) goto ExitFunction; if (pti->hTreeItem == TVI_ROOT) { pnrRoot = NULL; if (pTree->m_imagelist.Create(IDB_NET_TREE, 16, 3, CNetTreeCtrl::IMG_MASK)) { pTree->SetImageList(&(pTree->m_imagelist), TVSIL_NORMAL); pTree->m_imagelist.SetBkColor(CLR_NONE); } } else pnrRoot = (LPNETRESOURCE)pTree->GetItemData(pti->hTreeItem); // Get an enumeration handle. if ((dwResult = ::WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY, RESOURCEUSAGE_CONTAINER, pnrRoot, &hEnum)) != NO_ERROR) { // Exit if WNetOpenEnum fails. dwResult = ::GetLastError(); goto ExitFunction; } // Allocate a buffer for enumeration. if ((lpvBuffer = ::HeapAlloc(hHeap, HEAP_ZERO_MEMORY, pti->dwBufSize)) == NULL) // Exit if memory allocation failed goto ExitFunction; // Retrieve a block of network entries. while ((dwResult = ::WNetEnumResource(hEnum, &dwEntries, lpvBuffer, &(pti->dwBufSize))) != ERROR_NO_MORE_ITEMS) { // See if it's time to exit. if (pTree->m_bExitThread) { pTree->NotifyThread(FALSE); bResult = TRUE; goto ExitFunction; } // Exit if WNetEnumResource failed. if (dwResult != NO_ERROR) { dwResult = ::GetLastError(); goto ExitFunction; } LPNETRESOURCE pnrLeaf = (LPNETRESOURCE)lpvBuffer; TV_INSERTSTRUCT tviLeaf; // Fill in the TV_INSERTSTRUCT members. tviLeaf.hParent = pti->hTreeItem; tviLeaf.hInsertAfter = TVI_SORT; tviLeaf.item.hItem = NULL; tviLeaf.item.state = 0; tviLeaf.item.stateMask = 0; tviLeaf.item.cchTextMax = 0; tviLeaf.item.iSelectedImage = 0; // Set the correct image for the leaf. switch (pnrLeaf->dwDisplayType) { case RESOURCEDISPLAYTYPE_DOMAIN: tviLeaf.item.iImage = tviLeaf.item.iSelectedImage = CNetTreeCtrl::IMG_DOMAIN; break; case RESOURCEDISPLAYTYPE_SERVER: tviLeaf.item.iImage = tviLeaf.item.iSelectedImage = CNetTreeCtrl::IMG_SERVER; break; default: tviLeaf.item.iImage = tviLeaf.item.iSelectedImage = CNetTreeCtrl::IMG_ROOT; } // Fool the tree into thinking that this leaf has children // since we don't know initially. #if 0 if (pnrLeaf->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER) #else if (pnrLeaf->dwDisplayType == RESOURCEDISPLAYTYPE_DOMAIN || pnrLeaf->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER) #endif { tviLeaf.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; tviLeaf.item.cChildren = 0; } else { tviLeaf.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE; tviLeaf.item.cChildren = 1; } // Add leaves to the branch. for (DWORD i = 0; i < dwEntries; i++) { // See if it's time to exit. if (pTree->m_bExitThread) { pTree->NotifyThread(FALSE); bResult = TRUE; goto ExitFunction; } // Create a permanent NETRESOURCE struct for later use. LPNETRESOURCE pnrTemp = new NETRESOURCE; pTree->m_ptrlistContainers.AddTail(pnrTemp); ::CopyMemory(pnrTemp, pnrLeaf, sizeof(NETRESOURCE)); // Initialize members. pnrTemp->lpLocalName = NULL; pnrTemp->lpRemoteName = NULL; pnrTemp->lpComment = NULL; pnrTemp->lpProvider = NULL; if (pnrLeaf->lpRemoteName != NULL) { pnrTemp->lpRemoteName = new TCHAR[::_tcslen(pnrLeaf->lpRemoteName) + 1]; ::_tcscpy(pnrTemp->lpRemoteName, pnrLeaf->lpRemoteName); pTree->m_ptrlistStrings.AddTail(pnrTemp->lpRemoteName); } if (pnrLeaf->lpProvider != NULL) { pnrTemp->lpProvider = new TCHAR[::_tcslen(pnrLeaf->lpProvider) + 1]; ::_tcscpy(pnrTemp->lpProvider, pnrLeaf->lpProvider); pTree->m_ptrlistStrings.AddTail(pnrTemp->lpProvider); } // Increment the buffer pointer. pnrLeaf++; // Use "Enterprise" as the item text if this is the root. if (pti->hTreeItem == TVI_ROOT) { CString strRoot; strRoot.LoadString(IDS_TREE_ROOT); tviLeaf.item.pszText = new TCHAR[strRoot.GetLength() + 1]; ::_tcscpy(tviLeaf.item.pszText, (LPCTSTR)strRoot); } else if (pnrTemp->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER) { // Skip the initial backslashes before adding the server // name to the tree. tviLeaf.item.pszText = pnrTemp->lpRemoteName + 2; } else tviLeaf.item.pszText = pnrTemp->lpRemoteName; tviLeaf.item.lParam = (LPARAM)(LPVOID)pnrTemp; // Make sure the pointer to the tree control is still valid. if (::IsWindow(pTree->m_hWnd)) { hTreeItem = pTree->InsertItem(&tviLeaf); } else // Otherwise, exit the thread. { bResult = TRUE; goto ExitFunction; } // Delete the string allocated for the root node text. if (pti->hTreeItem == TVI_ROOT) delete tviLeaf.item.pszText; // See if the lpRemoteName member is equal to the default domain // name. #if 0 if (!_tcscmp(pnrTemp->lpRemoteName, pApp->m_strDomain) || #else if ( #endif pti->hTreeItem == TVI_ROOT) { // Store the handle. hTreeExpand = hTreeItem; } // Select the name of the license server in the tree. #if 0 if (!_tcsicmp(pnrTemp->lpRemoteName, pApp->m_strEnterpriseServer)) #else if (!_tcsicmp(pnrTemp->lpRemoteName, pApp->m_strDomain)) #endif { pTree->SelectItem(hTreeItem); pTree->EnsureVisible(hTreeItem); pTree->SetFocus(); } } // Everything went all right. bResult = TRUE; } // Expand the branch but only if it isn't the root. // The root item thinks it has children, but really doesn't the first time through. if (pti->hTreeItem != TVI_ROOT && pTree->ItemHasChildren(pti->hTreeItem)) { // Indicate that the branch has been expanded once. pTree->SetItemState(pti->hTreeItem, TVIS_EXPANDEDONCE, TVIS_EXPANDEDONCE); pTree->Expand(pti->hTreeItem, TVE_EXPAND); } // Fill the branch for the current domain if the bExpand member is TRUE. if (hTreeExpand != NULL && pti->bExpand) { TREEINFO ti; ti.hTreeItem = hTreeExpand; ti.dwBufSize = pti->dwBufSize; ti.pTree = pti->pTree; ti.bExpand = TRUE; // Increment the usage count. uUsage++; FillTree((LPVOID)&ti); // Decrement the usage count. uUsage--; } ExitFunction: // Display a message if an error occurred. if (!bResult) pTree->ErrorHandler(dwResult); // Close the enumeration handle. if (hEnum != NULL) if (!(bResult = (::WNetCloseEnum(hEnum) == NO_ERROR))) dwResult = ::GetLastError(); // Free memory allocated on the heap. if (lpvBuffer != NULL) ::HeapFree(hHeap, 0, lpvBuffer); // Free the TREEINFO pointer only if the usage count is zero. if (uUsage == 0) delete pti; // Reset the thread pointer. pTree->m_pThread = NULL; // Turn off the wait cursor pTree->EndWaitCursor(); // Make sure the tree control still exists before posting a message. if (::IsWindow(pTree->m_hWnd)) pTree->PostMessage(WM_SETCURSOR); // Signal the event object. if (uUsage == 0) event.SetEvent(); return (UINT)!bResult; } catch(...) { // Close the enumeration handle. if (hEnum != NULL) if (!(bResult = (::WNetCloseEnum(hEnum) == NO_ERROR))) dwResult = ::GetLastError(); // Free memory allocated on the heap. if (lpvBuffer != NULL) ::HeapFree(hHeap, 0, lpvBuffer); // Free the TREEINFO pointer delete pti; // Reset the thread pointer. pTree->m_pThread = NULL; // Turn off the wait cursor pTree->EndWaitCursor(); // Signal the event object. event.SetEvent(); return (UINT)2; } } ///////////////////////////////////////////////////////////////////////////// // CNetTreeCtrl member functions BOOL CNetTreeCtrl::PopulateTree(BOOL bExpand /* = TRUE */, const HTREEITEM hParentBranch /* = TVI_ROOT */, DWORD dwBufSize /* = BUFFER_SIZE */) { PTREEINFO pti = new TREEINFO; pti->hTreeItem = hParentBranch; pti->dwBufSize = dwBufSize; pti->pTree = this; pti->bExpand = bExpand; // Don't begin a new thread until the last one has ended. if (m_pThread != NULL) { NotifyThread(TRUE); CEvent event(TRUE, TRUE, pszTreeEvent); CSingleLock lock(&event); // Wait. lock.Lock(); } m_pThread = AfxBeginThread((AFX_THREADPROC)FillTree, (LPVOID)pti); return TRUE; } void CNetTreeCtrl::ErrorHandler(const DWORD dwCode) { CString strError; BOOL bNetError = FALSE; #ifdef _DEBUG switch (dwCode) { case ERROR_MORE_DATA: strError = "ERROR_MORE_DATA"; break; case ERROR_INVALID_HANDLE: strError = "ERROR_INVALID_HANDLE"; break; case ERROR_NOT_CONTAINER: strError = "ERROR_NOT_CONTAINER"; break; case ERROR_INVALID_PARAMETER: strError = "ERROR_INVALID_PARAMETER"; break; case ERROR_NO_NETWORK: strError = "ERROR_NO_NETWORK"; break; case ERROR_EXTENDED_ERROR: strError = "ERROR_EXTENDED_ERROR"; break; default: { #endif // _DEBUG DWORD dwErrCode; CString strErrDesc, strProvider; LPTSTR pszErrDesc = strErrDesc.GetBuffer(MAX_STRING); LPTSTR pszProvider = strProvider.GetBuffer(MAX_STRING); if (::WNetGetLastError(&dwErrCode, pszErrDesc, MAX_STRING, pszProvider, MAX_STRING) == NO_ERROR) { strErrDesc.ReleaseBuffer(); strProvider.ReleaseBuffer(); CString strErrMsg; // Don't display the WNetGetLastError message if dwErrCode == 0. if (dwErrCode) { // Trim of any leading or trailing white space. strProvider.TrimRight(); strProvider.TrimLeft(); strErrDesc.TrimRight(); strErrDesc.TrimLeft(); strErrMsg.Format(IDS_NET_ERROR, strProvider, strErrDesc); } else strErrMsg.LoadString(IDS_NET_NO_SERVERS); MessageBox(strErrMsg, AfxGetAppName(), MB_OK | MB_ICONEXCLAMATION); bNetError = TRUE; } else strError.LoadString(IDS_ERROR); #ifdef _DEBUG } } #endif // _DEBUG if (!bNetError) AfxMessageBox(strError, MB_OK | MB_ICONEXCLAMATION); } ///////////////////////////////////////////////////////////////////////////// // CNetTreeCtrl functions void CNetTreeCtrl::NotifyThread(BOOL bExit) { CCriticalSection cs; if (cs.Lock()) { m_bExitThread = bExit; cs.Unlock(); } } void CNetTreeCtrl::PumpMessages() { // Must call Create() before using the dialog ASSERT(m_hWnd!=NULL); MSG msg; try { // Handle dialog messages while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if(!IsDialogMessage(&msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } catch(...) { TRACE(_T("Exception in CNetTreeCtrl::PumpMessages()\n")); } } ///////////////////////////////////////////////////////////////////////////// // CNetTreeCtrl message handlers void CNetTreeCtrl::OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; // Exit and stop expansion if the thread is running. if (m_pThread != NULL) { *pResult = TRUE; return; } // Exit if this branch has been expanded once. if (!(pNMTreeView->itemNew.state & TVIS_EXPANDEDONCE)) { // Add new leaves to the branch. if (pNMTreeView->itemNew.mask & TVIF_HANDLE) { PopulateTree(FALSE, pNMTreeView->itemNew.hItem); pNMTreeView->itemNew.mask |= TVIS_EXPANDEDONCE; } } *pResult = FALSE; } BOOL CNetTreeCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { CLicCompWizApp* pApp = (CLicCompWizApp*)AfxGetApp(); if (m_pThread == NULL && pApp->m_pLicenseThread == NULL) { return CTreeCtrl::OnSetCursor(pWnd, nHitTest, message); } else { // Restore the wait cursor if the thread is running. RestoreWaitCursor(); return TRUE; } } void CNetTreeCtrl::OnDestroy() { NotifyThread(TRUE); PumpMessages(); CTreeCtrl::OnDestroy(); }