Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

595 lines
14 KiB

// 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();
}