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.

336 lines
11 KiB

  1. //
  2. // NetEnum.cpp
  3. //
  4. // Functions to enumerate computers and/or shares on the network
  5. //
  6. #include "stdafx.h"
  7. #include "NetEnum.h"
  8. #include "NetUtil.h"
  9. #include "Util.h"
  10. static CNetEnum* g_pNetEnum = NULL;
  11. //////////////////////////////////////////////////////////////////////////////
  12. void InitNetEnum()
  13. {
  14. ASSERT(g_pNetEnum == NULL);
  15. g_pNetEnum = new CNetEnum;
  16. }
  17. void TermNetEnum()
  18. {
  19. delete g_pNetEnum;
  20. }
  21. void EnumComputers(NETENUMCALLBACK pfnCallback, LPVOID pvCallbackParam)
  22. {
  23. if (g_pNetEnum)
  24. g_pNetEnum->EnumComputers(pfnCallback, pvCallbackParam);
  25. }
  26. //////////////////////////////////////////////////////////////////////////////
  27. CNetEnum::CNetEnum()
  28. {
  29. m_hThread = NULL;
  30. InitializeCriticalSection(&m_cs);
  31. m_bAbort = FALSE;
  32. }
  33. CNetEnum::~CNetEnum()
  34. {
  35. m_bAbort = TRUE;
  36. // Wait for the thread to die
  37. EnterCriticalSection(&m_cs);
  38. HANDLE hThread = m_hThread;
  39. m_hThread = NULL;
  40. LeaveCriticalSection(&m_cs);
  41. if (hThread != NULL)
  42. {
  43. WaitForSingleObject(hThread, INFINITE);
  44. CloseHandle(hThread);
  45. }
  46. DeleteCriticalSection(&m_cs);
  47. }
  48. void CNetEnum::Abort()
  49. {
  50. EnterCriticalSection(&m_cs);
  51. m_bAbort = TRUE;
  52. LeaveCriticalSection(&m_cs);
  53. }
  54. void CNetEnum::EnumComputers(NETENUMCALLBACK pfnCallback, LPVOID pvCallbackParam)
  55. {
  56. EnumHelper(jtEnumComputers, pfnCallback, pvCallbackParam);
  57. }
  58. void CNetEnum::EnumNetPrinters(NETENUMCALLBACK pfnCallback, LPVOID pvCallbackParam)
  59. {
  60. EnumHelper(jtEnumPrinters, pfnCallback, pvCallbackParam);
  61. }
  62. void CNetEnum::EnumHelper(JOBTYPE eJobType, NETENUMCALLBACK pfnCallback, LPVOID pvCallbackParam)
  63. {
  64. EnterCriticalSection(&m_cs);
  65. HANDLE hThread = m_hThread;
  66. m_pfnCallback = pfnCallback;
  67. m_pvCallbackParam = pvCallbackParam;
  68. m_eJobType = eJobType;
  69. m_bAbort = FALSE;
  70. m_bNewJob = TRUE; // if thread in progress, tell it to start a new job
  71. LeaveCriticalSection(&m_cs);
  72. if (hThread == NULL)
  73. {
  74. DWORD dwThreadId;
  75. m_hThread = CreateThread(NULL, 0, EnumThreadProc, this, CREATE_SUSPENDED, &dwThreadId);
  76. if (m_hThread)
  77. {
  78. ResumeThread(m_hThread);
  79. }
  80. }
  81. }
  82. DWORD WINAPI CNetEnum::EnumThreadProc(LPVOID pvParam)
  83. {
  84. ((CNetEnum*)pvParam)->EnumThreadProc();
  85. return 0;
  86. }
  87. #ifdef _DEBUG
  88. void TraceNetResource(const NETRESOURCE* pNetRes)
  89. {
  90. DWORD dwScope = pNetRes->dwScope;
  91. DWORD dwType = pNetRes->dwType;
  92. DWORD dwDisplayType = pNetRes->dwDisplayType;
  93. DWORD dwUsage = pNetRes->dwUsage;
  94. TRACE("NETRESOURCE (0x%08X):\n\tdwScope = %s\n\tdwType = %s\n\tdwDisplayType = %s\n\tdwUsage = %s\n\tlpLocalName = %s\n\tlpRemoteName = %s\n\tlpComment = %s\n\tlpProvider = %s\n",
  95. (DWORD_PTR)pNetRes,
  96. (dwScope == RESOURCE_CONNECTED) ? "RESOURCE_CONNECTED" : (dwScope == RESOURCE_GLOBALNET) ? "RESOURCE_GLOBALNET" : (dwScope == RESOURCE_REMEMBERED) ? "RESOURCE_REMEMBERED" : "(unknown)",
  97. (dwType == RESOURCETYPE_ANY) ? "RESOURCETYPE_ANY" : (dwType == RESOURCETYPE_DISK) ? "RESOURCETYPE_DISK" : (dwType == RESOURCETYPE_PRINT) ? "RESOURCETYPE_PRINT" : "(unknown)",
  98. (dwDisplayType == RESOURCEDISPLAYTYPE_DOMAIN) ? "RESOURCEDISPLAYTYPE_DOMAIN" : (dwDisplayType == RESOURCEDISPLAYTYPE_GENERIC) ? "RESOURCEDISPLAYTYPE_GENERIC" : (dwDisplayType == RESOURCEDISPLAYTYPE_SERVER) ? "RESOURCEDISPLAYTYPE_SERVER" : (dwDisplayType == RESOURCEDISPLAYTYPE_SHARE) ? "RESOURCEDISPLAYTYPE_SHARE" : "(unknown)",
  99. (dwUsage == RESOURCEUSAGE_CONNECTABLE) ? "RESOURCEUSAGE_CONNECTABLE" : (dwUsage == RESOURCEUSAGE_CONTAINER) ? "RESOURCEUSAGE_CONTAINER" : "(unknown)",
  100. pNetRes->lpLocalName == NULL ? L"(null)" : pNetRes->lpLocalName,
  101. pNetRes->lpRemoteName == NULL ? L"(null)" : pNetRes->lpRemoteName,
  102. pNetRes->lpComment == NULL ? L"(null)" : pNetRes->lpComment,
  103. pNetRes->lpProvider == NULL ? L"(null)" : pNetRes->lpProvider);
  104. }
  105. #endif
  106. void CNetEnum::EnumThreadProc()
  107. {
  108. // Init stuff we don't want to do more than once
  109. NETRESOURCE* prgNetResOuter = (NETRESOURCE*)malloc(1024);
  110. NETRESOURCE* prgNetResInnerT = (NETRESOURCE*)malloc(1024);
  111. TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH+1];
  112. DWORD cch = _countof(szComputerName);
  113. GetComputerName(szComputerName, &cch);
  114. HANDLE hEnumOuter = NULL;
  115. begin:
  116. // If the job ID changes out from under us, that means we need to stop
  117. // the current task and jump back to the beginning.
  118. EnterCriticalSection(&m_cs);
  119. JOBTYPE eJobType = m_eJobType;
  120. m_bNewJob = FALSE;
  121. LeaveCriticalSection(&m_cs);
  122. // Close enumeration left open by a previous job
  123. if (hEnumOuter != NULL)
  124. {
  125. WNetCloseEnum(hEnumOuter);
  126. hEnumOuter = NULL;
  127. }
  128. #ifdef _DEBUG
  129. // Sleep(eJobType == jtEnumComputers ? 6000 : 12000); // simulate WNetOpenEnum taking a long time
  130. #endif
  131. // REVIEW: should we look for computers outside the current workgroup?
  132. DWORD dwResult;
  133. if (eJobType == jtEnumComputers)
  134. {
  135. dwResult = WNetOpenEnum(RESOURCE_CONTEXT, RESOURCETYPE_ANY, RESOURCEUSAGE_CONTAINER,
  136. NULL, &hEnumOuter);
  137. }
  138. else
  139. {
  140. ASSERT(eJobType == jtEnumPrinters);
  141. dwResult = WNetOpenEnum(RESOURCE_CONTEXT, RESOURCETYPE_PRINT, RESOURCEUSAGE_CONNECTABLE,
  142. NULL, &hEnumOuter);
  143. }
  144. if (dwResult == NO_ERROR)
  145. {
  146. if (m_bAbort) goto cleanup;
  147. if (m_bNewJob) goto begin;
  148. BOOL bCallbackResult = TRUE;
  149. // Keep looping until no more items
  150. for (;;)
  151. {
  152. DWORD cOuterEntries = 20;
  153. DWORD cbBuffer = 1024;
  154. dwResult = WNetEnumResource(hEnumOuter, &cOuterEntries, prgNetResOuter, &cbBuffer);
  155. if (dwResult == ERROR_NO_MORE_ITEMS)
  156. break;
  157. for (DWORD iOuter = 0; iOuter < cOuterEntries; iOuter++)
  158. {
  159. NETRESOURCE* pNetResOuter = &prgNetResOuter[iOuter];
  160. BOOL bDoCallback = FALSE;
  161. #ifdef _DEBUG
  162. if (eJobType == jtEnumPrinters)
  163. TraceNetResource(pNetResOuter);
  164. #endif
  165. if (pNetResOuter->dwDisplayType != RESOURCEDISPLAYTYPE_SERVER)
  166. continue;
  167. if (DoComputerNamesMatch(pNetResOuter->lpRemoteName, szComputerName))
  168. continue;
  169. HANDLE hEnumInner = NULL;
  170. if (eJobType == jtEnumPrinters)
  171. {
  172. #ifdef _DEBUG
  173. DWORD dwTicksBefore = GetTickCount();
  174. #endif
  175. dwResult = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_PRINT, RESOURCEUSAGE_CONNECTABLE,
  176. pNetResOuter, &hEnumInner);
  177. #ifdef _DEBUG
  178. DWORD dwTicks = GetTickCount() - dwTicksBefore;
  179. if (dwTicks > 100)
  180. {
  181. TRACE("PERFORMANCE NOTE - took %d.%d sec to look for printers on %s\r\n", dwTicks / 1000, (dwTicks % 1000) - (dwTicks % 100), pNetResOuter->lpRemoteName);
  182. }
  183. #endif
  184. if (dwResult != NO_ERROR)
  185. continue;
  186. }
  187. for (;;)
  188. {
  189. DWORD cInnerEntries;
  190. const NETRESOURCE* prgNetResInner = NULL;
  191. if (eJobType == jtEnumPrinters)
  192. {
  193. cInnerEntries = 20;
  194. cbBuffer = 1024;
  195. dwResult = WNetEnumResource(hEnumInner, &cInnerEntries, prgNetResInnerT, &cbBuffer);
  196. if (dwResult == ERROR_NO_MORE_ITEMS)
  197. break;
  198. prgNetResInner = prgNetResInnerT;
  199. }
  200. else
  201. {
  202. cInnerEntries = 1;
  203. prgNetResInner = prgNetResOuter + iOuter;
  204. }
  205. for (DWORD iInner = 0; iInner < cInnerEntries; iInner++)
  206. {
  207. const NETRESOURCE* pNetResInner = &prgNetResInner[iInner];
  208. LPCTSTR pszShareName;
  209. #ifdef _DEBUG
  210. if (eJobType == jtEnumPrinters)
  211. TraceNetResource(pNetResInner);
  212. #endif
  213. if (eJobType == jtEnumComputers)
  214. {
  215. if (pNetResInner->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER)
  216. {
  217. bDoCallback = TRUE;
  218. pszShareName = NULL;
  219. }
  220. }
  221. else // eJobType == jtEnumPrinters
  222. {
  223. bDoCallback = TRUE;
  224. pszShareName = FindFileTitle(pNetResInner->lpRemoteName);
  225. }
  226. // We must call the callback inside the same critical section where
  227. // we check if we should stop or restart, otherwise we might call
  228. // the wrong callback!
  229. // TODO: Get the real printer share name!!
  230. EnterCriticalSection(&m_cs);
  231. if (m_bAbort || m_bNewJob)
  232. bCallbackResult = FALSE;
  233. else if (bDoCallback)
  234. bCallbackResult = (*m_pfnCallback)(m_pvCallbackParam, pNetResOuter->lpRemoteName, pszShareName);
  235. LeaveCriticalSection(&m_cs);
  236. if (!bCallbackResult)
  237. break;
  238. }
  239. if (eJobType == jtEnumComputers)
  240. break;
  241. }
  242. if (eJobType == jtEnumPrinters)
  243. {
  244. WNetCloseEnum(hEnumInner);
  245. }
  246. }
  247. if (m_bAbort) goto cleanup;
  248. if (m_bNewJob) goto begin;
  249. if (!bCallbackResult)
  250. break;
  251. }
  252. }
  253. cleanup:
  254. if (hEnumOuter != NULL)
  255. {
  256. WNetCloseEnum(hEnumOuter);
  257. hEnumOuter = NULL;
  258. }
  259. // Be careful to close m_hThread only if we don't need to start another job
  260. {
  261. EnterCriticalSection(&m_cs);
  262. BOOL bThreadDone = (m_bAbort || !m_bNewJob);
  263. if (bThreadDone)
  264. {
  265. // Call callback function one more time
  266. if (!m_bAbort)
  267. {
  268. (*m_pfnCallback)(m_pvCallbackParam, NULL, NULL);
  269. }
  270. CloseHandle(m_hThread);
  271. m_hThread = NULL;
  272. }
  273. LeaveCriticalSection(&m_cs);
  274. // Check if another job has been requested
  275. if (!bThreadDone)
  276. goto begin;
  277. }
  278. free(prgNetResInnerT);
  279. free(prgNetResOuter);
  280. }