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

  1. // NetTree.cpp : implementation file
  2. //
  3. #include "stdafx.h"
  4. #include "NetTree.h"
  5. #ifdef _DEBUG
  6. #define new DEBUG_NEW
  7. #undef THIS_FILE
  8. static char THIS_FILE[] = __FILE__;
  9. #endif
  10. /////////////////////////////////////////////////////////////////////////////
  11. // Global variables
  12. extern TCHAR pszTreeEvent[];
  13. /////////////////////////////////////////////////////////////////////////////
  14. // CNetTreeCtrl
  15. CNetTreeCtrl::CNetTreeCtrl()
  16. : m_pThread(NULL), m_bExitThread(FALSE), m_event(TRUE, TRUE, pszTreeEvent)
  17. {
  18. // Get a handle to the process heap
  19. m_hHeap = ::GetProcessHeap();
  20. ASSERT(m_hHeap != NULL);
  21. }
  22. CNetTreeCtrl::~CNetTreeCtrl()
  23. {
  24. // Make sure the thread knows it's time to terminate.
  25. NotifyThread(TRUE);
  26. // Create an event object to match the tree thread event object.
  27. CEvent event(TRUE, TRUE, pszTreeEvent);
  28. // Create a lock object for the event object.
  29. CSingleLock lock(&event);
  30. // Lock the lock object and make the main thread wait for the
  31. // threads to signal their event objects.
  32. lock.Lock();
  33. // Free all of the pointers to LPTSTRs in the list
  34. POSITION pos = m_ptrlistStrings.GetHeadPosition();
  35. while (pos != NULL)
  36. {
  37. // Memory deallocation fails if there's a null char
  38. // at the end of the string.
  39. LPTSTR psz = m_ptrlistStrings.GetNext(pos);
  40. *(::_tcslen(psz) + psz) = (TCHAR)0xFD;
  41. delete[] psz;
  42. }
  43. // Free all of the pointers to NETRESOURCE structs in the list
  44. pos = m_ptrlistContainers.GetHeadPosition();
  45. while (pos != NULL)
  46. {
  47. delete m_ptrlistContainers.GetNext(pos);
  48. }
  49. }
  50. BEGIN_MESSAGE_MAP(CNetTreeCtrl, CTreeCtrl)
  51. //{{AFX_MSG_MAP(CNetTreeCtrl)
  52. ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemExpanding)
  53. ON_WM_SETCURSOR()
  54. ON_WM_DESTROY()
  55. //}}AFX_MSG_MAP
  56. END_MESSAGE_MAP()
  57. /////////////////////////////////////////////////////////////////////////////
  58. // Static member functions
  59. UINT CNetTreeCtrl::FillTree(LPVOID pParam)
  60. {
  61. CEvent event(TRUE, TRUE, pszTreeEvent);
  62. CLicCompWizApp* pApp = (CLicCompWizApp*)AfxGetApp();
  63. PTREEINFO pti = (PTREEINFO)pParam;
  64. CNetTreeCtrl* pTree = (CNetTreeCtrl*)pti->pTree;
  65. BOOL bResult = FALSE;
  66. DWORD dwEntries = 0xFFFFFFFF;
  67. LPVOID lpvBuffer = NULL;
  68. HANDLE hEnum = NULL;
  69. // Because this function may call itself, keep a usage count
  70. // so that pti is freed only when the first instance returns.
  71. static USHORT uUsage = 0;
  72. // Keep a handle to the heap in case the CNetTreeCtrl object
  73. // goes away before the thread ends.
  74. HANDLE hHeap = pTree->m_hHeap;
  75. DWORD dwResult;
  76. LPNETRESOURCE pnrRoot;
  77. HTREEITEM hTreeItem, hTreeExpand;
  78. hTreeItem = hTreeExpand = NULL;
  79. try
  80. {
  81. // Unsignal the event object.
  82. event.ResetEvent();
  83. // Show the wait cursor
  84. pTree->BeginWaitCursor();
  85. // Exit if the handle to the heap is invalid.
  86. if (hHeap == NULL)
  87. goto ExitFunction;
  88. if (pti->hTreeItem == TVI_ROOT)
  89. {
  90. pnrRoot = NULL;
  91. if (pTree->m_imagelist.Create(IDB_NET_TREE, 16, 3, CNetTreeCtrl::IMG_MASK))
  92. {
  93. pTree->SetImageList(&(pTree->m_imagelist), TVSIL_NORMAL);
  94. pTree->m_imagelist.SetBkColor(CLR_NONE);
  95. }
  96. }
  97. else
  98. pnrRoot = (LPNETRESOURCE)pTree->GetItemData(pti->hTreeItem);
  99. // Get an enumeration handle.
  100. if ((dwResult = ::WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
  101. RESOURCEUSAGE_CONTAINER, pnrRoot, &hEnum)) != NO_ERROR)
  102. {
  103. // Exit if WNetOpenEnum fails.
  104. dwResult = ::GetLastError();
  105. goto ExitFunction;
  106. }
  107. // Allocate a buffer for enumeration.
  108. if ((lpvBuffer = ::HeapAlloc(hHeap, HEAP_ZERO_MEMORY, pti->dwBufSize)) == NULL)
  109. // Exit if memory allocation failed
  110. goto ExitFunction;
  111. // Retrieve a block of network entries.
  112. while ((dwResult = ::WNetEnumResource(hEnum, &dwEntries, lpvBuffer, &(pti->dwBufSize))) != ERROR_NO_MORE_ITEMS)
  113. {
  114. // See if it's time to exit.
  115. if (pTree->m_bExitThread)
  116. {
  117. pTree->NotifyThread(FALSE);
  118. bResult = TRUE;
  119. goto ExitFunction;
  120. }
  121. // Exit if WNetEnumResource failed.
  122. if (dwResult != NO_ERROR)
  123. {
  124. dwResult = ::GetLastError();
  125. goto ExitFunction;
  126. }
  127. LPNETRESOURCE pnrLeaf = (LPNETRESOURCE)lpvBuffer;
  128. TV_INSERTSTRUCT tviLeaf;
  129. // Fill in the TV_INSERTSTRUCT members.
  130. tviLeaf.hParent = pti->hTreeItem;
  131. tviLeaf.hInsertAfter = TVI_SORT;
  132. tviLeaf.item.hItem = NULL;
  133. tviLeaf.item.state = 0;
  134. tviLeaf.item.stateMask = 0;
  135. tviLeaf.item.cchTextMax = 0;
  136. tviLeaf.item.iSelectedImage = 0;
  137. // Set the correct image for the leaf.
  138. switch (pnrLeaf->dwDisplayType)
  139. {
  140. case RESOURCEDISPLAYTYPE_DOMAIN:
  141. tviLeaf.item.iImage = tviLeaf.item.iSelectedImage = CNetTreeCtrl::IMG_DOMAIN;
  142. break;
  143. case RESOURCEDISPLAYTYPE_SERVER:
  144. tviLeaf.item.iImage = tviLeaf.item.iSelectedImage = CNetTreeCtrl::IMG_SERVER;
  145. break;
  146. default:
  147. tviLeaf.item.iImage = tviLeaf.item.iSelectedImage = CNetTreeCtrl::IMG_ROOT;
  148. }
  149. // Fool the tree into thinking that this leaf has children
  150. // since we don't know initially.
  151. #if 0
  152. if (pnrLeaf->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER)
  153. #else
  154. if (pnrLeaf->dwDisplayType == RESOURCEDISPLAYTYPE_DOMAIN ||
  155. pnrLeaf->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER)
  156. #endif
  157. {
  158. tviLeaf.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
  159. tviLeaf.item.cChildren = 0;
  160. }
  161. else
  162. {
  163. tviLeaf.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
  164. tviLeaf.item.cChildren = 1;
  165. }
  166. // Add leaves to the branch.
  167. for (DWORD i = 0; i < dwEntries; i++)
  168. {
  169. // See if it's time to exit.
  170. if (pTree->m_bExitThread)
  171. {
  172. pTree->NotifyThread(FALSE);
  173. bResult = TRUE;
  174. goto ExitFunction;
  175. }
  176. // Create a permanent NETRESOURCE struct for later use.
  177. LPNETRESOURCE pnrTemp = new NETRESOURCE;
  178. pTree->m_ptrlistContainers.AddTail(pnrTemp);
  179. ::CopyMemory(pnrTemp, pnrLeaf, sizeof(NETRESOURCE));
  180. // Initialize members.
  181. pnrTemp->lpLocalName = NULL;
  182. pnrTemp->lpRemoteName = NULL;
  183. pnrTemp->lpComment = NULL;
  184. pnrTemp->lpProvider = NULL;
  185. if (pnrLeaf->lpRemoteName != NULL)
  186. {
  187. pnrTemp->lpRemoteName = new TCHAR[::_tcslen(pnrLeaf->lpRemoteName) + 1];
  188. ::_tcscpy(pnrTemp->lpRemoteName, pnrLeaf->lpRemoteName);
  189. pTree->m_ptrlistStrings.AddTail(pnrTemp->lpRemoteName);
  190. }
  191. if (pnrLeaf->lpProvider != NULL)
  192. {
  193. pnrTemp->lpProvider = new TCHAR[::_tcslen(pnrLeaf->lpProvider) + 1];
  194. ::_tcscpy(pnrTemp->lpProvider, pnrLeaf->lpProvider);
  195. pTree->m_ptrlistStrings.AddTail(pnrTemp->lpProvider);
  196. }
  197. // Increment the buffer pointer.
  198. pnrLeaf++;
  199. // Use "Enterprise" as the item text if this is the root.
  200. if (pti->hTreeItem == TVI_ROOT)
  201. {
  202. CString strRoot;
  203. strRoot.LoadString(IDS_TREE_ROOT);
  204. tviLeaf.item.pszText = new TCHAR[strRoot.GetLength() + 1];
  205. ::_tcscpy(tviLeaf.item.pszText, (LPCTSTR)strRoot);
  206. }
  207. else if (pnrTemp->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER)
  208. {
  209. // Skip the initial backslashes before adding the server
  210. // name to the tree.
  211. tviLeaf.item.pszText = pnrTemp->lpRemoteName + 2;
  212. }
  213. else
  214. tviLeaf.item.pszText = pnrTemp->lpRemoteName;
  215. tviLeaf.item.lParam = (LPARAM)(LPVOID)pnrTemp;
  216. // Make sure the pointer to the tree control is still valid.
  217. if (::IsWindow(pTree->m_hWnd))
  218. {
  219. hTreeItem = pTree->InsertItem(&tviLeaf);
  220. }
  221. else // Otherwise, exit the thread.
  222. {
  223. bResult = TRUE;
  224. goto ExitFunction;
  225. }
  226. // Delete the string allocated for the root node text.
  227. if (pti->hTreeItem == TVI_ROOT)
  228. delete tviLeaf.item.pszText;
  229. // See if the lpRemoteName member is equal to the default domain
  230. // name.
  231. #if 0
  232. if (!_tcscmp(pnrTemp->lpRemoteName, pApp->m_strDomain) ||
  233. #else
  234. if (
  235. #endif
  236. pti->hTreeItem == TVI_ROOT)
  237. {
  238. // Store the handle.
  239. hTreeExpand = hTreeItem;
  240. }
  241. // Select the name of the license server in the tree.
  242. #if 0
  243. if (!_tcsicmp(pnrTemp->lpRemoteName, pApp->m_strEnterpriseServer))
  244. #else
  245. if (!_tcsicmp(pnrTemp->lpRemoteName, pApp->m_strDomain))
  246. #endif
  247. {
  248. pTree->SelectItem(hTreeItem);
  249. pTree->EnsureVisible(hTreeItem);
  250. pTree->SetFocus();
  251. }
  252. }
  253. // Everything went all right.
  254. bResult = TRUE;
  255. }
  256. // Expand the branch but only if it isn't the root.
  257. // The root item thinks it has children, but really doesn't the first time through.
  258. if (pti->hTreeItem != TVI_ROOT && pTree->ItemHasChildren(pti->hTreeItem))
  259. {
  260. // Indicate that the branch has been expanded once.
  261. pTree->SetItemState(pti->hTreeItem, TVIS_EXPANDEDONCE, TVIS_EXPANDEDONCE);
  262. pTree->Expand(pti->hTreeItem, TVE_EXPAND);
  263. }
  264. // Fill the branch for the current domain if the bExpand member is TRUE.
  265. if (hTreeExpand != NULL && pti->bExpand)
  266. {
  267. TREEINFO ti;
  268. ti.hTreeItem = hTreeExpand;
  269. ti.dwBufSize = pti->dwBufSize;
  270. ti.pTree = pti->pTree;
  271. ti.bExpand = TRUE;
  272. // Increment the usage count.
  273. uUsage++;
  274. FillTree((LPVOID)&ti);
  275. // Decrement the usage count.
  276. uUsage--;
  277. }
  278. ExitFunction:
  279. // Display a message if an error occurred.
  280. if (!bResult)
  281. pTree->ErrorHandler(dwResult);
  282. // Close the enumeration handle.
  283. if (hEnum != NULL)
  284. if (!(bResult = (::WNetCloseEnum(hEnum) == NO_ERROR)))
  285. dwResult = ::GetLastError();
  286. // Free memory allocated on the heap.
  287. if (lpvBuffer != NULL)
  288. ::HeapFree(hHeap, 0, lpvBuffer);
  289. // Free the TREEINFO pointer only if the usage count is zero.
  290. if (uUsage == 0)
  291. delete pti;
  292. // Reset the thread pointer.
  293. pTree->m_pThread = NULL;
  294. // Turn off the wait cursor
  295. pTree->EndWaitCursor();
  296. // Make sure the tree control still exists before posting a message.
  297. if (::IsWindow(pTree->m_hWnd))
  298. pTree->PostMessage(WM_SETCURSOR);
  299. // Signal the event object.
  300. if (uUsage == 0)
  301. event.SetEvent();
  302. return (UINT)!bResult;
  303. }
  304. catch(...)
  305. {
  306. // Close the enumeration handle.
  307. if (hEnum != NULL)
  308. if (!(bResult = (::WNetCloseEnum(hEnum) == NO_ERROR)))
  309. dwResult = ::GetLastError();
  310. // Free memory allocated on the heap.
  311. if (lpvBuffer != NULL)
  312. ::HeapFree(hHeap, 0, lpvBuffer);
  313. // Free the TREEINFO pointer
  314. delete pti;
  315. // Reset the thread pointer.
  316. pTree->m_pThread = NULL;
  317. // Turn off the wait cursor
  318. pTree->EndWaitCursor();
  319. // Signal the event object.
  320. event.SetEvent();
  321. return (UINT)2;
  322. }
  323. }
  324. /////////////////////////////////////////////////////////////////////////////
  325. // CNetTreeCtrl member functions
  326. BOOL CNetTreeCtrl::PopulateTree(BOOL bExpand /* = TRUE */, const HTREEITEM hParentBranch /* = TVI_ROOT */,
  327. DWORD dwBufSize /* = BUFFER_SIZE */)
  328. {
  329. PTREEINFO pti = new TREEINFO;
  330. pti->hTreeItem = hParentBranch;
  331. pti->dwBufSize = dwBufSize;
  332. pti->pTree = this;
  333. pti->bExpand = bExpand;
  334. // Don't begin a new thread until the last one has ended.
  335. if (m_pThread != NULL)
  336. {
  337. NotifyThread(TRUE);
  338. CEvent event(TRUE, TRUE, pszTreeEvent);
  339. CSingleLock lock(&event);
  340. // Wait.
  341. lock.Lock();
  342. }
  343. m_pThread = AfxBeginThread((AFX_THREADPROC)FillTree, (LPVOID)pti);
  344. return TRUE;
  345. }
  346. void CNetTreeCtrl::ErrorHandler(const DWORD dwCode)
  347. {
  348. CString strError;
  349. BOOL bNetError = FALSE;
  350. #ifdef _DEBUG
  351. switch (dwCode)
  352. {
  353. case ERROR_MORE_DATA:
  354. strError = "ERROR_MORE_DATA";
  355. break;
  356. case ERROR_INVALID_HANDLE:
  357. strError = "ERROR_INVALID_HANDLE";
  358. break;
  359. case ERROR_NOT_CONTAINER:
  360. strError = "ERROR_NOT_CONTAINER";
  361. break;
  362. case ERROR_INVALID_PARAMETER:
  363. strError = "ERROR_INVALID_PARAMETER";
  364. break;
  365. case ERROR_NO_NETWORK:
  366. strError = "ERROR_NO_NETWORK";
  367. break;
  368. case ERROR_EXTENDED_ERROR:
  369. strError = "ERROR_EXTENDED_ERROR";
  370. break;
  371. default:
  372. {
  373. #endif // _DEBUG
  374. DWORD dwErrCode;
  375. CString strErrDesc, strProvider;
  376. LPTSTR pszErrDesc = strErrDesc.GetBuffer(MAX_STRING);
  377. LPTSTR pszProvider = strProvider.GetBuffer(MAX_STRING);
  378. if (::WNetGetLastError(&dwErrCode, pszErrDesc, MAX_STRING,
  379. pszProvider, MAX_STRING) == NO_ERROR)
  380. {
  381. strErrDesc.ReleaseBuffer();
  382. strProvider.ReleaseBuffer();
  383. CString strErrMsg;
  384. // Don't display the WNetGetLastError message if dwErrCode == 0.
  385. if (dwErrCode)
  386. {
  387. // Trim of any leading or trailing white space.
  388. strProvider.TrimRight();
  389. strProvider.TrimLeft();
  390. strErrDesc.TrimRight();
  391. strErrDesc.TrimLeft();
  392. strErrMsg.Format(IDS_NET_ERROR, strProvider, strErrDesc);
  393. }
  394. else
  395. strErrMsg.LoadString(IDS_NET_NO_SERVERS);
  396. MessageBox(strErrMsg, AfxGetAppName(), MB_OK | MB_ICONEXCLAMATION);
  397. bNetError = TRUE;
  398. }
  399. else
  400. strError.LoadString(IDS_ERROR);
  401. #ifdef _DEBUG
  402. }
  403. }
  404. #endif // _DEBUG
  405. if (!bNetError)
  406. AfxMessageBox(strError, MB_OK | MB_ICONEXCLAMATION);
  407. }
  408. /////////////////////////////////////////////////////////////////////////////
  409. // CNetTreeCtrl functions
  410. void CNetTreeCtrl::NotifyThread(BOOL bExit)
  411. {
  412. CCriticalSection cs;
  413. if (cs.Lock())
  414. {
  415. m_bExitThread = bExit;
  416. cs.Unlock();
  417. }
  418. }
  419. void CNetTreeCtrl::PumpMessages()
  420. {
  421. // Must call Create() before using the dialog
  422. ASSERT(m_hWnd!=NULL);
  423. MSG msg;
  424. try
  425. {
  426. // Handle dialog messages
  427. while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  428. {
  429. if(!IsDialogMessage(&msg))
  430. {
  431. TranslateMessage(&msg);
  432. DispatchMessage(&msg);
  433. }
  434. }
  435. }
  436. catch(...)
  437. {
  438. TRACE(_T("Exception in CNetTreeCtrl::PumpMessages()\n"));
  439. }
  440. }
  441. /////////////////////////////////////////////////////////////////////////////
  442. // CNetTreeCtrl message handlers
  443. void CNetTreeCtrl::OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult)
  444. {
  445. NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
  446. // Exit and stop expansion if the thread is running.
  447. if (m_pThread != NULL)
  448. {
  449. *pResult = TRUE;
  450. return;
  451. }
  452. // Exit if this branch has been expanded once.
  453. if (!(pNMTreeView->itemNew.state & TVIS_EXPANDEDONCE))
  454. {
  455. // Add new leaves to the branch.
  456. if (pNMTreeView->itemNew.mask & TVIF_HANDLE)
  457. {
  458. PopulateTree(FALSE, pNMTreeView->itemNew.hItem);
  459. pNMTreeView->itemNew.mask |= TVIS_EXPANDEDONCE;
  460. }
  461. }
  462. *pResult = FALSE;
  463. }
  464. BOOL CNetTreeCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
  465. {
  466. CLicCompWizApp* pApp = (CLicCompWizApp*)AfxGetApp();
  467. if (m_pThread == NULL && pApp->m_pLicenseThread == NULL)
  468. {
  469. return CTreeCtrl::OnSetCursor(pWnd, nHitTest, message);
  470. }
  471. else
  472. {
  473. // Restore the wait cursor if the thread is running.
  474. RestoreWaitCursor();
  475. return TRUE;
  476. }
  477. }
  478. void CNetTreeCtrl::OnDestroy()
  479. {
  480. NotifyThread(TRUE);
  481. PumpMessages();
  482. CTreeCtrl::OnDestroy();
  483. }