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.

578 lines
14 KiB

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