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.

914 lines
29 KiB

  1. #include "shellprv.h"
  2. #include "commctrl.h"
  3. #include "comctrlp.h"
  4. #pragma hdrstop
  5. #include "netview.h"
  6. #include "msprintx.h"
  7. #include "setupapi.h"
  8. #include "ras.h"
  9. #include "ids.h"
  10. // a COM object to enumerate shares and printers in the shell, its acts
  11. // as a monitor for all that is going on.
  12. class CWorkgroupCrawler : public INetCrawler, IPersistPropertyBag
  13. {
  14. public:
  15. CWorkgroupCrawler();
  16. ~CWorkgroupCrawler();
  17. // IUnknown
  18. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  19. STDMETHOD_(ULONG, AddRef)();
  20. STDMETHOD_(ULONG, Release)();
  21. // INetCrawler
  22. STDMETHOD(Update)(DWORD dwFlags);
  23. // IPersist
  24. STDMETHOD(GetClassID)(CLSID *pclsid)
  25. { *pclsid = CLSID_WorkgroupNetCrawler; return S_OK; }
  26. // IPersistPropertyBag
  27. STDMETHOD(InitNew)()
  28. { return S_OK; }
  29. STDMETHOD(Load)(IPropertyBag *ppb, IErrorLog *pel)
  30. { IUnknown_Set((IUnknown**)&_ppb, ppb); return S_OK; }
  31. STDMETHOD(Save)(IPropertyBag *ppb, BOOL fClearDirty, BOOL fSaveAll)
  32. { return S_OK; }
  33. private:
  34. HRESULT _GetMRUs();
  35. void _AgeOutShares(BOOL fDeleteAll);
  36. HRESULT _CreateShortcutToShare(LPCTSTR pszRemoteName);
  37. HRESULT _InstallPrinter(LPCTSTR pszRemoteName);
  38. BOOL _KeepGoing(int *pcMachines, int *pcShares, int *pcPrinters);
  39. void _EnumResources(LPNETRESOURCE pnr, int *pcMachines, HDPA hdaShares, HDPA hdaPrinters);
  40. HANDLE _AddPrinterConnectionNoUI(LPCWSTR pszRemoteName, BOOL *pfInstalled);
  41. static int CALLBACK _DiscardCB(void *pvItem, void *pv);
  42. static int CALLBACK _InstallSharesCB(void *pvItem, void *pv);
  43. static int CALLBACK _InstallPrinterCB(void *pvItem, void *pv);
  44. LONG _cRef; // reference count for the object
  45. HANDLE _hPrinters; // MRU for printers
  46. HKEY _hShares; // registry key for the printer shares
  47. HINSTANCE _hPrintUI; // instance handle for printui.dll
  48. IPropertyBag *_ppb; // property bag object for state
  49. };
  50. // constants for the MRU's and buffers
  51. #define WORKGROUP_PATH \
  52. REGSTR_PATH_EXPLORER TEXT("\\WorkgroupCrawler")
  53. #define PRINTER_SUBKEY \
  54. (WORKGROUP_PATH TEXT("\\Printers"))
  55. #define SHARE_SUBKEY \
  56. (WORKGROUP_PATH TEXT("\\Shares"))
  57. #define LAST_VISITED TEXT("DateLastVisited")
  58. #define SHORTCUT_NAME TEXT("Filename")
  59. #define MAX_MACHINES 32
  60. #define MAX_PRINTERS 10
  61. #define MAX_SHARES 10
  62. #define CB_WNET_BUFFER (8*1024)
  63. typedef HANDLE (* ADDPRINTCONNECTIONNOUI)(LPCWSTR, BOOL *);
  64. // construction and IUnknown
  65. CWorkgroupCrawler::CWorkgroupCrawler() :
  66. _cRef(1)
  67. {
  68. }
  69. CWorkgroupCrawler::~CWorkgroupCrawler()
  70. {
  71. if (_hPrinters)
  72. FreeMRUList(_hPrinters);
  73. if (_hShares)
  74. RegCloseKey(_hShares);
  75. if (_hPrintUI)
  76. FreeLibrary(_hPrintUI);
  77. if (_ppb)
  78. _ppb->Release();
  79. }
  80. STDMETHODIMP CWorkgroupCrawler::QueryInterface(REFIID riid, void **ppv)
  81. {
  82. static const QITAB qit[] =
  83. {
  84. QITABENT(CWorkgroupCrawler, INetCrawler), // IID_INetCrawler
  85. QITABENT(CWorkgroupCrawler, IPersist), // IID_IPersist
  86. QITABENT(CWorkgroupCrawler, IPersistPropertyBag), // IID_IPersistPropertyBag
  87. { 0 },
  88. };
  89. return QISearch(this, qit, riid, ppv);
  90. }
  91. STDMETHODIMP_(ULONG) CWorkgroupCrawler::AddRef()
  92. {
  93. return InterlockedIncrement(&_cRef);
  94. }
  95. STDMETHODIMP_(ULONG) CWorkgroupCrawler::Release()
  96. {
  97. if (InterlockedDecrement(&_cRef))
  98. return _cRef;
  99. delete this;
  100. return 0;
  101. }
  102. STDAPI CWorkgroupCrawler_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv)
  103. {
  104. CWorkgroupCrawler *pwgc = new CWorkgroupCrawler();
  105. if (!pwgc)
  106. return E_OUTOFMEMORY;
  107. HRESULT hr = pwgc->QueryInterface(riid, ppv);
  108. pwgc->Release();
  109. return hr;
  110. }
  111. // lets open the keys for the objects we are about to install
  112. HRESULT CWorkgroupCrawler::_GetMRUs()
  113. {
  114. // get the printers MRU if we need to allocate one
  115. if (!_hPrinters)
  116. {
  117. MRUINFO mi = { 0 };
  118. mi.cbSize = sizeof(mi);
  119. mi.hKey = HKEY_CURRENT_USER;
  120. mi.uMax = (MAX_PRINTERS * MAX_MACHINES);
  121. mi.lpszSubKey = PRINTER_SUBKEY;
  122. _hPrinters = CreateMRUList(&mi);
  123. if (!_hPrinters)
  124. return E_OUTOFMEMORY;
  125. }
  126. if (!_hShares)
  127. {
  128. DWORD dwres = RegCreateKeyEx(HKEY_CURRENT_USER, SHARE_SUBKEY, 0,
  129. TEXT(""), 0,
  130. MAXIMUM_ALLOWED,
  131. NULL,
  132. &_hShares,
  133. NULL);
  134. if (WN_SUCCESS != dwres)
  135. {
  136. return E_FAIL;
  137. }
  138. }
  139. return S_OK; // success
  140. }
  141. // lets create a folder shortcut to the object
  142. HRESULT CWorkgroupCrawler::_CreateShortcutToShare(LPCTSTR pszRemoteName)
  143. {
  144. HRESULT hr = S_OK;
  145. TCHAR szTemp[MAX_PATH];
  146. BOOL fCreateLink = FALSE;
  147. HKEY hk = NULL;
  148. // the share information is stored in the registry as follows:
  149. //
  150. // Shares
  151. // Remote Name
  152. // value: shortcut name
  153. // value: last seen
  154. //
  155. // as we add each share we update the information stored in this list in
  156. // the registry. for each entry we have the shortcut name (so we can remove it)
  157. // and the time and date we last visited the share.
  158. // determine if we need to recreate the object?
  159. StrCpyN(szTemp, pszRemoteName+2, ARRAYSIZE(szTemp));
  160. LPTSTR pszTemp = StrChr(szTemp, TEXT('\\'));
  161. if (pszTemp)
  162. {
  163. *pszTemp = TEXT('/'); // convert the \\...\... to .../...
  164. DWORD dwres = RegOpenKeyEx(_hShares, szTemp, 0, MAXIMUM_ALLOWED, &hk);
  165. if (WN_SUCCESS != dwres)
  166. {
  167. fCreateLink = TRUE;
  168. dwres = RegCreateKeyEx(_hShares, szTemp, 0, TEXT(""), 0, MAXIMUM_ALLOWED, NULL, &hk, NULL);
  169. }
  170. if (WN_SUCCESS == dwres)
  171. {
  172. // if we haven't already seen the link (eg. the key didn't exist in the registry
  173. // then lets create it now.
  174. if (fCreateLink)
  175. {
  176. // NOTE: we must use SHCoCreateInstance() here because we are being called from a thread
  177. // that intentionally did not initialize COM (see comment in Update())
  178. IShellLink *psl;
  179. hr = SHCoCreateInstance(NULL, &CLSID_FolderShortcut, NULL, IID_PPV_ARG(IShellLink, &psl));
  180. if (SUCCEEDED(hr))
  181. {
  182. psl->SetPath(pszRemoteName); // sotore the remote name, its kinda important
  183. // get a description for the link, this comes either from the desktop.ini or the
  184. // is a pretty version of teh remote name.
  185. if (GetShellClassInfo(pszRemoteName, TEXT("InfoTip"), szTemp, ARRAYSIZE(szTemp)))
  186. {
  187. psl->SetDescription(szTemp);
  188. }
  189. else
  190. {
  191. StrCpyN(szTemp, pszRemoteName, ARRAYSIZE(szTemp));
  192. PathMakePretty(szTemp);
  193. psl->SetDescription(szTemp);
  194. }
  195. // some links (shared documents) can specify a shortcut name, if this is specified
  196. // then use it, otherwise get a filename from the nethood folder (eg. foo on bah).
  197. //
  198. // we musst also record the name we save the shortcut as, this used when we
  199. // age out the links from the hood folder.
  200. if (!GetShellClassInfo(pszRemoteName, TEXT("NetShareDisplayName"), szTemp, ARRAYSIZE(szTemp)))
  201. {
  202. LPITEMIDLIST pidl;
  203. hr = SHILCreateFromPath(pszRemoteName, &pidl, NULL);
  204. if (SUCCEEDED(hr))
  205. {
  206. hr = SHGetNameAndFlags(pidl, SHGDN_NORMAL, szTemp, ARRAYSIZE(szTemp), NULL);
  207. ILFree(pidl);
  208. }
  209. }
  210. else
  211. {
  212. hr = S_OK;
  213. }
  214. // should we find a unique name (daviddv)
  215. if (SUCCEEDED(hr))
  216. {
  217. if (NO_ERROR == SHSetValue(hk, NULL, SHORTCUT_NAME, REG_SZ, szTemp, lstrlen(szTemp)*sizeof(TCHAR)))
  218. {
  219. hr = SaveShortcutInFolder(CSIDL_NETHOOD, szTemp, psl);
  220. }
  221. else
  222. {
  223. hr = E_FAIL;
  224. }
  225. }
  226. psl->Release();
  227. }
  228. }
  229. // lets update the time we last saw the link into the registry - this is used for the clean up
  230. // pass we will perform.
  231. if (SUCCEEDED(hr))
  232. {
  233. FILETIME ft;
  234. GetSystemTimeAsFileTime(&ft);
  235. dwres = SHSetValue(hk, NULL, LAST_VISITED, REG_BINARY, (void*)&ft, sizeof(ft));
  236. hr = (NO_ERROR != dwres) ? E_FAIL:S_OK;
  237. }
  238. }
  239. if (hk)
  240. RegCloseKey(hk);
  241. }
  242. else
  243. {
  244. hr = E_UNEXPECTED;
  245. }
  246. return hr;
  247. }
  248. // walk the list of shares stored in the registry to determine which ones should be
  249. // removed from the file system and the list. all files older than 7 days need to
  250. // be removed.
  251. #define FILETIME_SECOND_OFFSET (LONGLONG)((1 * 10 * 1000 * (LONGLONG)1000))
  252. void CWorkgroupCrawler::_AgeOutShares(BOOL fDeleteAll)
  253. {
  254. FILETIME ft;
  255. ULARGE_INTEGER ulTime;
  256. DWORD index = 0;
  257. TCHAR szFilesToDelete[1024];
  258. int cchFilesToDelete = 0;
  259. GetSystemTimeAsFileTime(&ft);
  260. ulTime = *((ULARGE_INTEGER*)&ft);
  261. ulTime.QuadPart -= FILETIME_SECOND_OFFSET*((60*60*24)*2);
  262. SHQueryInfoKey(_hShares, &index, NULL, NULL, NULL); // retrieve the count of the keys
  263. while (((LONG)(--index)) >= 0)
  264. {
  265. TCHAR szKey[MAX_PATH];
  266. DWORD cb = ARRAYSIZE(szKey);
  267. BOOL fRemoveKey = FALSE;
  268. if (WN_SUCCESS == SHEnumKeyEx(_hShares, index, szKey, &cb))
  269. {
  270. // we enumerated a key name, so lets open it so we can look around inside.
  271. HKEY hk;
  272. if (WN_SUCCESS == RegOpenKeyEx(_hShares, szKey, 0, MAXIMUM_ALLOWED, &hk))
  273. {
  274. ULARGE_INTEGER ulLastSeen;
  275. // when did we last crawl to this object, if it was less than the time we
  276. // have for our threshold, then we go through the process of cleaning up the
  277. // object.
  278. cb = sizeof(ulLastSeen);
  279. if (ERROR_SUCCESS == SHGetValue(hk, NULL, LAST_VISITED, NULL, (void*)&ulLastSeen, &cb))
  280. {
  281. if (fDeleteAll || (ulLastSeen.QuadPart <= ulTime.QuadPart))
  282. {
  283. TCHAR szName[MAX_PATH];
  284. cb = ARRAYSIZE(szName)*sizeof(TCHAR);
  285. if (ERROR_SUCCESS == SHGetValue(hk, NULL, SHORTCUT_NAME, NULL, &szName, &cb))
  286. {
  287. TCHAR szPath[MAX_PATH];
  288. // compose the path to the object we want to delete. if the buffer
  289. // is full (eg. this item would over run the size) then flush the
  290. // buffer.
  291. SHGetFolderPath(NULL, CSIDL_NETHOOD|CSIDL_FLAG_CREATE, NULL, 0, szPath);
  292. PathAppend(szPath, szName);
  293. if ((lstrlen(szPath)+cchFilesToDelete) >= ARRAYSIZE(szFilesToDelete))
  294. {
  295. SHFILEOPSTRUCT shfo = { NULL, FO_DELETE, szFilesToDelete, NULL,
  296. FOF_SILENT|FOF_NOCONFIRMATION|FOF_NOERRORUI, FALSE, NULL, NULL };
  297. szFilesToDelete[cchFilesToDelete] = 0; // double terminate
  298. SHFileOperation(&shfo);
  299. cchFilesToDelete = 0;
  300. }
  301. // add this name to the buffer
  302. StrCpyN(&szFilesToDelete[cchFilesToDelete], szPath, ARRAYSIZE(szFilesToDelete) - cchFilesToDelete);
  303. cchFilesToDelete += lstrlen(szPath)+1;
  304. }
  305. fRemoveKey = TRUE;
  306. }
  307. }
  308. RegCloseKey(hk);
  309. // we can only close the key once it has been closed
  310. if (fRemoveKey)
  311. SHDeleteKey(_hShares, szKey);
  312. }
  313. }
  314. }
  315. // are there any trailing files in the buffer? if so then lets nuke them also
  316. if (cchFilesToDelete)
  317. {
  318. SHFILEOPSTRUCT shfo = { NULL, FO_DELETE, szFilesToDelete, NULL,
  319. FOF_SILENT|FOF_NOCONFIRMATION|FOF_NOERRORUI, FALSE, NULL, NULL };
  320. szFilesToDelete[cchFilesToDelete] = 0; // double terminate
  321. SHFileOperation(&shfo);
  322. }
  323. }
  324. // silently install printers we have discovered. we have the remote name of the
  325. // printer share, so we then call printui to perform the printer installation
  326. // which it does without UI (hopefully).
  327. HANDLE CWorkgroupCrawler::_AddPrinterConnectionNoUI(LPCWSTR pszRemoteName, BOOL *pfInstalled)
  328. {
  329. HANDLE hResult = NULL;
  330. if (!_hPrintUI)
  331. _hPrintUI = LoadLibrary(TEXT("printui.dll"));
  332. if (_hPrintUI)
  333. {
  334. ADDPRINTCONNECTIONNOUI apc = (ADDPRINTCONNECTIONNOUI)GetProcAddress(_hPrintUI, (LPCSTR)200);
  335. if (apc)
  336. {
  337. hResult = apc(pszRemoteName, pfInstalled);
  338. }
  339. }
  340. return hResult;
  341. }
  342. HRESULT CWorkgroupCrawler::_InstallPrinter(LPCTSTR pszRemoteName)
  343. {
  344. if (-1 == FindMRUString(_hPrinters, pszRemoteName, NULL))
  345. {
  346. BOOL fInstalled;
  347. HANDLE hPrinter = _AddPrinterConnectionNoUI(pszRemoteName, &fInstalled);
  348. if (hPrinter)
  349. {
  350. ClosePrinter(hPrinter);
  351. hPrinter = NULL;
  352. }
  353. }
  354. AddMRUString(_hPrinters, pszRemoteName); // promote back to the top of the list
  355. return S_OK;
  356. }
  357. // check the counters, if we have max'd out then lets stop enumerating
  358. BOOL CWorkgroupCrawler::_KeepGoing(int *pcMachines, int *pcShares, int *pcPrinters)
  359. {
  360. if (pcMachines && (*pcMachines > MAX_MACHINES))
  361. return FALSE;
  362. if (pcShares && (*pcShares > MAX_SHARES))
  363. return FALSE;
  364. if (pcPrinters && (*pcPrinters > MAX_PRINTERS))
  365. return FALSE;
  366. return TRUE;
  367. }
  368. void CWorkgroupCrawler::_EnumResources(LPNETRESOURCE pnr, int *pcMachines, HDPA hdaShares, HDPA hdaPrinters)
  369. {
  370. HANDLE hEnum = NULL;
  371. int cPrinters = 0;
  372. int cShares = 0;
  373. DWORD dwScope = RESOURCE_GLOBALNET;
  374. // if no net resource structure passed then lets enumerate the workgroup
  375. // (this is used for debugging)
  376. NETRESOURCE nr = { 0 };
  377. if (!pnr)
  378. {
  379. pnr = &nr;
  380. dwScope = RESOURCE_CONTEXT;
  381. nr.dwType = RESOURCETYPE_ANY;
  382. nr.dwUsage = RESOURCEUSAGE_CONTAINER;
  383. }
  384. // open the enumerator
  385. DWORD dwres = WNetOpenEnum(dwScope, RESOURCETYPE_ANY, 0, pnr, &hEnum);
  386. if (NO_ERROR == dwres)
  387. {
  388. NETRESOURCE *pnrBuffer = (NETRESOURCE*)SHAlloc(CB_WNET_BUFFER); // avoid putting the buffer on the stack
  389. if (pnrBuffer)
  390. {
  391. while ((WN_SUCCESS == dwres) || (dwres == ERROR_MORE_DATA) && _KeepGoing(pcMachines, &cShares, &cPrinters))
  392. {
  393. DWORD cbEnumBuffer= CB_WNET_BUFFER;
  394. DWORD dwCount = -1;
  395. // enumerate the resources for this enum context and then lets
  396. // determine the objects which we should see.
  397. dwres = WNetEnumResource(hEnum, &dwCount, pnrBuffer, &cbEnumBuffer);
  398. if ((WN_SUCCESS == dwres) || (dwres == ERROR_MORE_DATA))
  399. {
  400. DWORD index;
  401. for (index = 0 ; (index != dwCount) && _KeepGoing(pcMachines, &cShares, &cPrinters) ; index++)
  402. {
  403. LPNETRESOURCE pnr = &pnrBuffer[index];
  404. LPTSTR pszRemoteName = pnr->lpRemoteName;
  405. switch (pnr->dwDisplayType)
  406. {
  407. case RESOURCEDISPLAYTYPE_ROOT: // ignore the entire network object
  408. default:
  409. break;
  410. case RESOURCEDISPLAYTYPE_NETWORK:
  411. {
  412. // ensure that we only crawl the local network providers (eg. Windows Networking)
  413. // crawling DAV, TSCLIENT etc can cause all sorts of random pop ups.
  414. DWORD dwType, cbProviderType = sizeof(dwType);
  415. if (WN_SUCCESS == WNetGetProviderType(pnr->lpProvider, &dwType))
  416. {
  417. if (dwType == WNNC_NET_LANMAN)
  418. {
  419. _EnumResources(pnr, pcMachines, hdaShares, hdaPrinters);
  420. }
  421. }
  422. break;
  423. }
  424. case RESOURCEDISPLAYTYPE_DOMAIN:
  425. _EnumResources(pnr, pcMachines, hdaShares, hdaPrinters);
  426. break;
  427. case RESOURCEDISPLAYTYPE_SERVER:
  428. {
  429. *pcMachines += 1; // another machine found
  430. if (!PathIsSlow(pszRemoteName, -1))
  431. {
  432. SHCacheComputerDescription(pszRemoteName, pnr->lpComment);
  433. _EnumResources(pnr, pcMachines, hdaShares, hdaPrinters);
  434. }
  435. break;
  436. }
  437. case RESOURCEDISPLAYTYPE_SHARE:
  438. {
  439. HDPA hdpa = NULL;
  440. switch (pnr->dwType)
  441. {
  442. case RESOURCETYPE_PRINT:
  443. cPrinters++;
  444. hdpa = hdaPrinters;
  445. break;
  446. case RESOURCETYPE_DISK:
  447. cShares++;
  448. hdpa = hdaShares;
  449. break;
  450. default:
  451. break;
  452. }
  453. if (hdpa)
  454. {
  455. LPTSTR pszName = StrDup(pszRemoteName);
  456. if (pszName)
  457. {
  458. if (-1 == DPA_AppendPtr(hdpa, pszName))
  459. {
  460. LocalFree(pszName);
  461. }
  462. }
  463. }
  464. break;
  465. }
  466. }
  467. }
  468. }
  469. }
  470. SHFree(pnrBuffer);
  471. }
  472. WNetCloseEnum(hEnum);
  473. }
  474. }
  475. // handle the clean up of the DPA's, either we are installing or
  476. // we are releasing objects.
  477. int CALLBACK CWorkgroupCrawler::_DiscardCB(void *pvItem, void *pv)
  478. {
  479. LPTSTR pszRemoteName = (LPTSTR)pvItem;
  480. LocalFree(pszRemoteName);
  481. return 1;
  482. }
  483. int CALLBACK CWorkgroupCrawler::_InstallPrinterCB(void *pvItem, void *pv)
  484. {
  485. CWorkgroupCrawler* pnc = (CWorkgroupCrawler*)pv;
  486. if (pnc)
  487. {
  488. LPTSTR pszRemoteName = (LPTSTR)pvItem;
  489. pnc->_InstallPrinter(pszRemoteName);
  490. }
  491. return _DiscardCB(pvItem, pv);
  492. }
  493. int CALLBACK CWorkgroupCrawler::_InstallSharesCB(void *pvItem, void *pv)
  494. {
  495. CWorkgroupCrawler* pnc = (CWorkgroupCrawler*)pv;
  496. if (pnc)
  497. {
  498. LPTSTR pszRemoteName = (LPTSTR)pvItem;
  499. pnc->_CreateShortcutToShare(pszRemoteName);
  500. }
  501. return _DiscardCB(pvItem, pv);
  502. }
  503. HRESULT CWorkgroupCrawler::Update(DWORD dwFlags)
  504. {
  505. // don't crawl if we are logged in on a TS client, this will discover the shares and
  506. // printers local to the terminal server machine, rather than the ones local to the
  507. // users login domain - badness.
  508. if (SHGetMachineInfo(GMI_TSCLIENT))
  509. return S_OK;
  510. // by default we will only crawl if there isn't a RAS connection, therefore lets
  511. // check the status using RasEnumConnections.
  512. RASCONN rc = { 0 };
  513. DWORD cbConnections = sizeof(rc);
  514. DWORD cConnections = 0;
  515. rc.dwSize = sizeof(rc);
  516. if (!RasEnumConnections(&rc, &cbConnections, &cConnections) && cConnections)
  517. return S_OK;
  518. // check to see if we are in a domain or not, if we are then we shouldn't crawl. however
  519. // we do a provide a "WorkgroupOnly" policy which overrides this behaviour. setting
  520. // this causes us to skip the check, and perform a CONTEXT ENUM below...
  521. BOOL fWorkgroupOnly = (_ppb ? SHPropertyBag_ReadBOOLDefRet(_ppb, L"WorkgroupOnly", FALSE):FALSE);
  522. if (IsOS(OS_DOMAINMEMBER) && !fWorkgroupOnly)
  523. return S_OK;
  524. // populate the DPAs with shares and printer objects we find on the network, to
  525. // do this enumeration lets fake up a NETRESOURCE structure for entire network
  526. int cMachines = 0;
  527. HDPA hdaShares = DPA_Create(MAX_SHARES);
  528. HDPA hdaPrinters = DPA_Create(MAX_PRINTERS);
  529. if (hdaShares && hdaPrinters)
  530. {
  531. NETRESOURCE nr = { 0 };
  532. nr.dwDisplayType = RESOURCEDISPLAYTYPE_ROOT;
  533. nr.dwType = RESOURCETYPE_ANY;
  534. nr.dwUsage = RESOURCEUSAGE_CONTAINER;
  535. _EnumResources(fWorkgroupOnly ? NULL:&nr, &cMachines, hdaShares, hdaPrinters);
  536. }
  537. // now attempt to make connections to the shares and printers. to do this
  538. // we need to look at the number of machines we have visited, if its less
  539. // than our threshold then we can install.
  540. if (SUCCEEDED(_GetMRUs()) && (cMachines < MAX_MACHINES))
  541. {
  542. DPA_DestroyCallback(hdaShares, _InstallSharesCB, this);
  543. DPA_DestroyCallback(hdaPrinters, _InstallPrinterCB, this);
  544. _AgeOutShares(FALSE);
  545. }
  546. else
  547. {
  548. DPA_DestroyCallback(hdaShares, _DiscardCB, this);
  549. DPA_DestroyCallback(hdaPrinters, _DiscardCB, this);
  550. }
  551. return S_OK;
  552. }
  553. // this is the main crawler object, from this we create the protocol specific
  554. // crawlers which handle enumerating the resources for the various network types.
  555. #define CRAWLER_SUBKEY \
  556. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\NetworkCrawler\\Objects")
  557. class CNetCrawler : public INetCrawler
  558. {
  559. public:
  560. CNetCrawler();
  561. // IUnknown
  562. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  563. STDMETHOD_(ULONG, AddRef)();
  564. STDMETHOD_(ULONG, Release)();
  565. // INetCrawler
  566. STDMETHOD(Update)(DWORD dwFlags);
  567. private:
  568. static DWORD CALLBACK s_DoCrawl(void* pv);
  569. DWORD _DoCrawl();
  570. LONG _cRef;
  571. LONG _cUpdateLock; // > 0 then we are already spinning
  572. DWORD _dwFlags; // flags from update - passed to each of the crawler sub-objects
  573. };
  574. CNetCrawler* g_pnc = NULL; // there is a single instance of this object
  575. // construction / IUnknown
  576. CNetCrawler::CNetCrawler() :
  577. _cRef(1)
  578. {
  579. }
  580. STDMETHODIMP CNetCrawler::QueryInterface(REFIID riid, void **ppv)
  581. {
  582. static const QITAB qit[] =
  583. {
  584. QITABENT(CNetCrawler, INetCrawler),
  585. { 0 },
  586. };
  587. return QISearch(this, qit, riid, ppv);
  588. }
  589. STDMETHODIMP_(ULONG) CNetCrawler::AddRef()
  590. {
  591. return InterlockedIncrement(&_cRef);
  592. }
  593. STDMETHODIMP_(ULONG) CNetCrawler::Release()
  594. {
  595. if (InterlockedDecrement(&_cRef))
  596. return _cRef;
  597. ENTERCRITICAL;
  598. g_pnc = NULL;
  599. LEAVECRITICAL;
  600. delete this;
  601. return 0;
  602. }
  603. // there is a single instance of the object, therefore in a critical section
  604. // lets check to see if the global exists, if so then QI it, otherwise
  605. // create a new one and QI that instead.
  606. STDAPI CNetCrawler_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv)
  607. {
  608. HRESULT hr = E_OUTOFMEMORY;
  609. ENTERCRITICAL;
  610. if (g_pnc)
  611. {
  612. hr = g_pnc->QueryInterface(riid, ppv);
  613. }
  614. else
  615. {
  616. g_pnc = new CNetCrawler();
  617. if (g_pnc)
  618. {
  619. hr = g_pnc->QueryInterface(riid, ppv);
  620. g_pnc->Release();
  621. }
  622. }
  623. LEAVECRITICAL;
  624. return hr;
  625. }
  626. // this object has a execution count to inidicate if we are already crawling.
  627. // only if the count is 0 when the ::Update method is called, will create a thread
  628. // which inturn will create each of the crawler objects and allow them to
  629. // do their enumeration.
  630. DWORD CALLBACK CNetCrawler::s_DoCrawl(void* pv)
  631. {
  632. CNetCrawler *pnc = (CNetCrawler*)pv;
  633. return pnc->_DoCrawl();
  634. }
  635. DWORD CNetCrawler::_DoCrawl()
  636. {
  637. // enumrate all the keys under the crawler sub-key, from that we can then
  638. // create the individual crawler objects.
  639. HKEY hk;
  640. DWORD dwres = RegOpenKeyEx(HKEY_LOCAL_MACHINE, CRAWLER_SUBKEY, 0, KEY_READ, &hk);
  641. if (WN_SUCCESS == dwres)
  642. {
  643. DWORD index = 0;
  644. SHQueryInfoKey(hk, &index, NULL, NULL, NULL); // retrieve the count of the keys
  645. while (((LONG)(--index)) >= 0)
  646. {
  647. TCHAR szKey[MAX_PATH];
  648. DWORD cb = ARRAYSIZE(szKey);
  649. if (WN_SUCCESS == SHEnumKeyEx(hk, index, szKey, &cb))
  650. {
  651. // given the keyname, create a property bag so we can access
  652. IPropertyBag *ppb;
  653. HRESULT hr = SHCreatePropertyBagOnRegKey(hk, szKey, STGM_READ, IID_PPV_ARG(IPropertyBag, &ppb));
  654. if (SUCCEEDED(hr))
  655. {
  656. // we have a property bag mapped to the registry for the items we need
  657. // to read back, so lets get the CLSID and create a crawler from it.
  658. CLSID clsid;
  659. hr = SHPropertyBag_ReadGUID(ppb, L"CLSID", &clsid);
  660. if (SUCCEEDED(hr))
  661. {
  662. INetCrawler *pnc;
  663. hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(INetCrawler, &pnc));
  664. if (SUCCEEDED(hr))
  665. {
  666. // if the crawler supports IPersistPropertyBag then lets allow it
  667. // to slurp its settings into from the registry.
  668. SHLoadFromPropertyBag(pnc, ppb);
  669. // allow it to update and load.
  670. pnc->Update(_dwFlags); // we don't care about the failure
  671. pnc->Release();
  672. }
  673. }
  674. ppb->Release();
  675. }
  676. }
  677. }
  678. RegCloseKey(hk);
  679. }
  680. InterlockedDecrement(&_cUpdateLock); // release the lock that signifies that we're updating:
  681. Release();
  682. return 0;
  683. }
  684. STDMETHODIMP CNetCrawler::Update(DWORD dwFlags)
  685. {
  686. // we either have policy defined to disable the crawler, or the
  687. // users has selected that they don't want to be able to auto discover
  688. // the world.
  689. SHELLSTATE ss;
  690. SHGetSetSettings(&ss, SSF_NONETCRAWLING, FALSE);
  691. if (ss.fNoNetCrawling || SHRestricted(REST_NONETCRAWL))
  692. {
  693. return S_OK;
  694. }
  695. // increase the lock, if its >0 then we should not bother crawling again
  696. // as we already have it covered, therefore decrease the lock counter.
  697. //
  698. // if the lock is ==0 then create the thread which will do the crawling
  699. // and inturn create the objects.
  700. HRESULT hr = S_OK;
  701. if (InterlockedIncrement(&_cUpdateLock) == 1)
  702. {
  703. _dwFlags = dwFlags; // store the flags for use later
  704. AddRef();
  705. if (!SHCreateThread(s_DoCrawl, (void*)this, CTF_COINIT, NULL))
  706. {
  707. Release();
  708. hr = E_FAIL;
  709. }
  710. }
  711. else
  712. {
  713. InterlockedDecrement(&_cUpdateLock);
  714. }
  715. return hr;
  716. }
  717. // helper function that will invoke the net crawler to perform a async refresh,
  718. // to ensure that we don't block we will create a thread which inturn will CoCreate
  719. // the net crawler and then call its refresh method.
  720. DWORD _RefreshCrawlerThreadProc(void *pv)
  721. {
  722. INetCrawler *pnc;
  723. if (SUCCEEDED(CoCreateInstance(CLSID_NetCrawler, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARG(INetCrawler, &pnc))))
  724. {
  725. pnc->Update(SNCF_REFRESHLIST);
  726. pnc->Release();
  727. }
  728. return 0;
  729. }
  730. STDAPI_(void) RefreshNetCrawler()
  731. {
  732. SHCreateThread(_RefreshCrawlerThreadProc, NULL, CTF_COINIT, NULL);
  733. }