Leaked source code of windows server 2003
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.

3105 lines
92 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include <initguid.h>
  4. #include <dbt.h>
  5. #include "printer.h"
  6. #include <dpa.h>
  7. #include "idltree.h"
  8. #include "scnotifyp.h"
  9. #include "mtpt.h"
  10. #include "shitemid.h"
  11. #include <ioevent.h>
  12. #define TF_SHELLCHANGENOTIFY 0x40000
  13. #define SCNM_REGISTERCLIENT WM_USER + 1
  14. #define SCNM_DEREGISTERCLIENT WM_USER + 2
  15. #define SCNM_NOTIFYEVENT WM_USER + 3
  16. #define SCNM_FLUSHEVENTS WM_USER + 4
  17. #define SCNM_TERMINATE WM_USER + 5
  18. #define SCNM_SUSPENDRESUME WM_USER + 6
  19. #define SCNM_DEREGISTERWINDOW WM_USER + 7
  20. #define SCNM_AUTOPLAYDRIVE WM_USER + 8
  21. enum
  22. {
  23. FLUSH_OVERFLOW = 1,
  24. FLUSH_SOFT,
  25. FLUSH_HARD,
  26. FLUSH_INTERRUPT,
  27. };
  28. #define IDT_SCN_FLUSHEVENTS 1
  29. #define IDT_SCN_FRESHENTREES 2
  30. #define EVENT_OVERFLOW 10
  31. HWND g_hwndSCN = NULL;
  32. CChangeNotify *g_pscn = NULL;
  33. EXTERN_C CRITICAL_SECTION g_csSCN;
  34. CRITICAL_SECTION g_csSCN = {0};
  35. #define PERFTEST(x)
  36. EXTERN_C void SFP_FSEvent (LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra);
  37. EXTERN_C int WINAPI RLFSChanged (LONG lEvent, LPITEMIDLIST pidl, LPITEMIDLIST pidlExtra);
  38. STDAPI CFSFolder_IconEvent(LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra);
  39. STDAPI_(HWND) _SCNGetWindow(BOOL fUseDesktop, BOOL fNeedsFallback);
  40. STDAPI SHChangeNotifyAutoplayDrive(PCWSTR pszDrive)
  41. {
  42. ASSERT(PathIsRoot(pszDrive));
  43. HWND hwnd = _SCNGetWindow(TRUE, FALSE);
  44. if (hwnd)
  45. {
  46. DWORD dwProcessID = 0;
  47. GetWindowThreadProcessId(hwnd, &dwProcessID);
  48. if (dwProcessID)
  49. {
  50. AllowSetForegroundWindow(dwProcessID);
  51. }
  52. PostMessage(g_hwndSCN, SCNM_AUTOPLAYDRIVE, DRIVEID(pszDrive), 0);
  53. return S_OK;
  54. }
  55. return E_FAIL;
  56. }
  57. //
  58. // special folders that are aliases. these are always running
  59. // csidlAlias refers to the users perceived namespace
  60. // csidlReal refers to the actual filesystem folder behind the alias
  61. //
  62. typedef struct ALIASFOLDER {
  63. int csidlAlias;
  64. int csidlReal;
  65. } ALIASFOLDER, *PALIASFOLDER;
  66. static const ALIASFOLDER s_rgaf[] = {
  67. {CSIDL_DESKTOP, CSIDL_DESKTOPDIRECTORY},
  68. {CSIDL_DESKTOP, CSIDL_COMMON_DESKTOPDIRECTORY },
  69. {CSIDL_PERSONAL, CSIDL_PERSONAL | CSIDL_FLAG_NO_ALIAS},
  70. {CSIDL_NETWORK, CSIDL_NETHOOD},
  71. {CSIDL_PRINTERS, CSIDL_PRINTHOOD},
  72. };
  73. void InitAliasFolderTable(void)
  74. {
  75. for (int i = 0; i < ARRAYSIZE(s_rgaf); i++)
  76. {
  77. g_pscn->AddSpecialAlias(s_rgaf[i].csidlReal, s_rgaf[i].csidlAlias);
  78. }
  79. }
  80. #pragma pack(1)
  81. typedef struct {
  82. WORD cb;
  83. LONG lEEvent;
  84. } ALIASREGISTER;
  85. typedef struct {
  86. ALIASREGISTER ar;
  87. WORD wNull;
  88. } ALIASREGISTERLIST;
  89. #pragma pack()
  90. STDAPI_(void) SHChangeNotifyRegisterAlias(LPCITEMIDLIST pidlReal, LPCITEMIDLIST pidlAlias)
  91. {
  92. static const ALIASREGISTERLIST arl = { {sizeof(ALIASREGISTER), SHCNEE_ALIASINUSE}, 0};
  93. LPITEMIDLIST pidlRegister = ILCombine((LPCITEMIDLIST)&arl, pidlReal);
  94. if (pidlRegister)
  95. {
  96. SHChangeNotify(SHCNE_EXTENDED_EVENT, SHCNF_ONLYNOTIFYINTERNALS | SHCNF_IDLIST, pidlRegister, pidlAlias);
  97. ILFree(pidlRegister);
  98. }
  99. }
  100. LPCITEMIDLIST IsAliasRegisterPidl(LPCITEMIDLIST pidl)
  101. {
  102. ALIASREGISTER *par = (ALIASREGISTER *)pidl;
  103. if (par->cb == sizeof(ALIASREGISTER)
  104. && par->lEEvent == SHCNEE_ALIASINUSE)
  105. return _ILNext(pidl);
  106. return NULL;
  107. }
  108. LONG g_cAliases = 0;
  109. LPITEMIDLIST TranslateAlias(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlReal, LPCITEMIDLIST pidlAlias)
  110. {
  111. // see if its child of one of our watched items
  112. LPCITEMIDLIST pidlChild = pidl ? ILFindChild(pidlReal, pidl) : NULL;
  113. if (pidlChild)
  114. {
  115. return ILCombine(pidlAlias, pidlChild);
  116. }
  117. return NULL;
  118. }
  119. CAnyAlias::~CAnyAlias()
  120. {
  121. ILFree(_pidlAlias);
  122. ATOMICRELEASE(_ptscn);
  123. }
  124. BOOL CCollapsingClient::Init(LPCITEMIDLIST pidl, BOOL fRecursive)
  125. {
  126. _pidl = ILClone(pidl);
  127. _fRecursive = fRecursive;
  128. return (_pidl && _dpaPendingEvents.Create(EVENT_OVERFLOW + 1));
  129. }
  130. BOOL CAnyAlias::Init(LPCITEMIDLIST pidlReal, LPCITEMIDLIST pidlAlias)
  131. {
  132. ASSERT(!_fSpecial);
  133. _pidlAlias = ILClone(pidlAlias);
  134. return (_pidlAlias && CCollapsingClient::Init(pidlReal, TRUE));
  135. }
  136. BOOL CAnyAlias::_WantsEvent(LONG lEvent)
  137. {
  138. return (lEvent & (SHCNE_DISKEVENTS | SHCNE_DRIVEREMOVED | SHCNE_NETSHARE | SHCNE_NETUNSHARE));
  139. }
  140. BOOL CAnyAlias::InitSpecial(int csidlReal, int csidlAlias)
  141. {
  142. _fSpecial = TRUE;
  143. _csidlReal = csidlReal;
  144. _csidlAlias = csidlAlias;
  145. LPITEMIDLIST pidlNew;
  146. WIN32_FIND_DATA fd = {0};
  147. fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; // Special folders are always directories
  148. SHGetSpecialFolderPath(NULL, fd.cFileName, csidlReal | CSIDL_FLAG_DONT_VERIFY, FALSE);
  149. SHSimpleIDListFromFindData(fd.cFileName, &fd, &pidlNew);
  150. SHGetSpecialFolderLocation(NULL, csidlAlias | CSIDL_FLAG_DONT_VERIFY, &_pidlAlias);
  151. BOOL fRet = _pidlAlias && CCollapsingClient::Init(pidlNew, TRUE);
  152. ILFree(pidlNew);
  153. return fRet;
  154. }
  155. BOOL CAnyAlias::IsAlias(LPCITEMIDLIST pidlReal, LPCITEMIDLIST pidlAlias)
  156. {
  157. // if this hits, an alias has been registered already
  158. // this means the guy doing the registration isn't doing it at the junction point like
  159. // theyre supposed to
  160. ASSERT((ILIsEqual(pidlReal, _pidl) && ILIsEqual(pidlAlias, _pidlAlias)) ||
  161. !(ILIsParent(_pidl, pidlReal, FALSE) && ILIsParent(_pidlAlias, pidlAlias, FALSE)));
  162. return (ILIsEqual(pidlReal, _pidl)
  163. && ILIsEqual(pidlAlias, _pidlAlias));
  164. }
  165. BOOL CAnyAlias::IsSpecial(int csidlReal, int csidlAlias)
  166. {
  167. return (_fSpecial && csidlReal == _csidlReal && csidlAlias == _csidlAlias);
  168. }
  169. CAnyAlias::_CustomTranslate()
  170. {
  171. if (!_fCheckedCustom)
  172. {
  173. SHBindToObjectEx(NULL, _pidlAlias, NULL, IID_PPV_ARG(ITranslateShellChangeNotify, &_ptscn));
  174. _fCheckedCustom = TRUE;
  175. }
  176. return (_ptscn != NULL);
  177. }
  178. // some pidl translators may not translate the event. if we pass on a notifyevent thats identical,
  179. // we'll get into an infinite loop. our translators are good about this so this doesnt happen, but
  180. // we'll catch it here to be more robust -- we wouldnt want a bad translating shell extension to
  181. // be able to spinlock the changenotify thread.
  182. BOOL CAnyAlias::_OkayToNotifyTranslatedEvent(CNotifyEvent *pne, LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra)
  183. {
  184. // mydocs has an issue where it can be removed from the desktop when
  185. // it is redirected, because the alias only propagates the first
  186. // half of the notification (remove). so we dont Translate the remove.
  187. if (_fSpecial && _csidlAlias == CSIDL_PERSONAL)
  188. {
  189. if (pne->lEvent == SHCNE_RENAMEFOLDER || pne->lEvent == SHCNE_RMDIR)
  190. {
  191. if (ILIsEqual(pidl, _pidlAlias))
  192. return FALSE;
  193. }
  194. }
  195. // if the original event wasn't already translated, let it proceed.
  196. // if its a different event, its fine -- a translator could flip-flop between events but we cant detect that case.
  197. // in addition we need to beware of aliases that translate to themselves or their children --
  198. // for example a my computer shortcut in the start menu will be registered recursively, so if you try
  199. // to delete it it will get into a loop.
  200. // so if the events are the same, verify that both the resultant pidls aren't underneath _pidl.
  201. return !(pne->uEventFlags & SHCNF_TRANSLATEDALIAS) ||
  202. (lEvent != pne->lEvent) ||
  203. !(pidl && ILIsParent(_pidl, pidl, FALSE)) && !(pidlExtra && ILIsParent(_pidl, pidlExtra, FALSE));
  204. }
  205. void CAnyAlias::_SendNotification(CNotifyEvent *pne, BOOL fNeedsCallbackEvent, SENDASYNCPROC pfncb)
  206. {
  207. //
  208. // see if its child of one of our watched items
  209. if (_CustomTranslate())
  210. {
  211. LPITEMIDLIST pidl1Alias = pne->pidl;
  212. LPITEMIDLIST pidl1AliasExtra = pne->pidlExtra;
  213. LPITEMIDLIST pidl2Alias = NULL, pidl2AliasExtra = NULL;
  214. LONG lEvent1 = pne->lEvent & ~SHCNE_INTERRUPT; // translator shouldn't see this flag
  215. LONG lEvent2 = -1;
  216. if (SUCCEEDED(_ptscn->TranslateIDs(&lEvent1, pne->pidl, pne->pidlExtra, &pidl1Alias, &pidl1AliasExtra,
  217. &lEvent2, &pidl2Alias, &pidl2AliasExtra)))
  218. {
  219. if (_OkayToNotifyTranslatedEvent(pne, lEvent1, pidl1Alias, pidl1AliasExtra))
  220. {
  221. g_pscn->NotifyEvent(lEvent1, SHCNF_IDLIST | SHCNF_TRANSLATEDALIAS,
  222. pidl1Alias, pidl1AliasExtra,
  223. pne->dwEventTime);
  224. }
  225. if ((lEvent2 != -1) && _OkayToNotifyTranslatedEvent(pne, lEvent2, pidl2Alias, pidl2AliasExtra))
  226. {
  227. g_pscn->NotifyEvent(lEvent2, SHCNF_IDLIST | SHCNF_TRANSLATEDALIAS,
  228. pidl2Alias, pidl2AliasExtra,
  229. pne->dwEventTime);
  230. }
  231. if (pidl1Alias != pne->pidl)
  232. ILFree(pidl1Alias);
  233. if (pidl1AliasExtra != pne->pidlExtra)
  234. ILFree(pidl1AliasExtra);
  235. ILFree(pidl2Alias);
  236. ILFree(pidl2AliasExtra);
  237. }
  238. }
  239. else
  240. {
  241. LPITEMIDLIST pidlAlias = TranslateAlias(pne->pidl, _pidl, _pidlAlias);
  242. LPITEMIDLIST pidlAliasExtra = TranslateAlias(pne->pidlExtra, _pidl, _pidlAlias);
  243. if (pidlAlias || pidlAliasExtra)
  244. {
  245. LPCITEMIDLIST pidlNotify = pidlAlias ? pidlAlias : pne->pidl;
  246. LPCITEMIDLIST pidlNotifyExtra = pidlAliasExtra ? pidlAliasExtra : pne->pidlExtra;
  247. if (_OkayToNotifyTranslatedEvent(pne, pne->lEvent, pidlNotify, pidlNotifyExtra))
  248. {
  249. g_pscn->NotifyEvent(pne->lEvent, SHCNF_IDLIST | SHCNF_TRANSLATEDALIAS,
  250. pidlNotify, pidlNotifyExtra,
  251. pne->dwEventTime);
  252. }
  253. // do some special handling here
  254. // like refresh folders or something will clean out an entry.
  255. switch (pne->lEvent)
  256. {
  257. case SHCNE_UPDATEDIR:
  258. if (!_fSpecial && ILIsEqual(pne->pidl, _pidl))
  259. {
  260. // this is target, and it will be refreshed.
  261. // if the alias is still around, then it will
  262. // have to reenum and re-register
  263. // there-fore we will clean this out now.
  264. _fRemove = TRUE;
  265. }
  266. break;
  267. default:
  268. break;
  269. }
  270. ILFree(pidlAlias);
  271. ILFree(pidlAliasExtra);
  272. }
  273. }
  274. // this is the notify we get when a drive mapping is deleted
  275. // when this happens we need to kill the aliases to that drive
  276. if (pne->lEvent == SHCNE_DRIVEREMOVED)
  277. {
  278. if (!_fSpecial && ILIsEqual(pne->pidl, _pidlAlias))
  279. {
  280. // when net drives are removed
  281. // pidlExtra is the UNC
  282. _fRemove = TRUE;
  283. }
  284. }
  285. }
  286. void CAnyAlias::Activate(BOOL fActivate)
  287. {
  288. if (fActivate)
  289. {
  290. ASSERT(_cActivated >= 0);
  291. if (!_cActivated++)
  292. {
  293. // turn this puppy on!
  294. _fRemove = FALSE;
  295. if (!_fInterrupt)
  296. _fInterrupt = g_pscn->AddInterruptSource(_pidl, TRUE);
  297. }
  298. }
  299. else
  300. {
  301. ASSERT(_cActivated > 0);
  302. if (!--_cActivated)
  303. {
  304. // now turn it off
  305. _fRemove = TRUE;
  306. g_pscn->SetFlush(FLUSH_SOFT);
  307. }
  308. }
  309. }
  310. void CChangeNotify::_CheckAliasRollover(void)
  311. {
  312. static DWORD s_tick = 0;
  313. DWORD tick = GetTickCount();
  314. if (tick < s_tick)
  315. {
  316. // we rolled the tick count over
  317. CLinkedWalk<CAnyAlias> lw(&_listAliases);
  318. while (lw.Step())
  319. {
  320. lw.That()->_dwTime = tick;
  321. }
  322. }
  323. s_tick = tick;
  324. }
  325. CAnyAlias *CChangeNotify::_FindSpecialAlias(int csidlReal, int csidlAlias)
  326. {
  327. CLinkedWalk<CAnyAlias> lw(&_listAliases);
  328. while (lw.Step())
  329. {
  330. CAnyAlias *paa = lw.That();
  331. if (paa->IsSpecial(csidlReal, csidlAlias))
  332. {
  333. // we found it
  334. return paa;
  335. }
  336. }
  337. return NULL;
  338. }
  339. CAnyAlias *CChangeNotify::_FindAlias(LPCITEMIDLIST pidlReal, LPCITEMIDLIST pidlAlias)
  340. {
  341. CLinkedWalk<CAnyAlias> lw(&_listAliases);
  342. while (lw.Step())
  343. {
  344. CAnyAlias *paa = lw.That();
  345. if (paa->IsAlias(pidlReal, pidlAlias))
  346. {
  347. // we found it
  348. return paa;
  349. }
  350. }
  351. return NULL;
  352. }
  353. void CChangeNotify::AddSpecialAlias(int csidlReal, int csidlAlias)
  354. {
  355. CAnyAlias *paa = _FindSpecialAlias(csidlReal, csidlAlias);
  356. if (!paa)
  357. {
  358. CLinkedNode<CAnyAlias> *p = new CLinkedNode<CAnyAlias>;
  359. if (p)
  360. {
  361. if (p->that.InitSpecial(csidlReal, csidlAlias))
  362. {
  363. if (_InsertAlias(p))
  364. paa = &p->that;
  365. }
  366. if (!paa)
  367. delete p;
  368. }
  369. }
  370. }
  371. void CChangeNotify::UpdateSpecialAlias(int csidlAlias)
  372. {
  373. for (int i = 0; i < ARRAYSIZE(s_rgaf); i++)
  374. {
  375. if (csidlAlias == s_rgaf[i].csidlAlias)
  376. {
  377. CLinkedNode<CAnyAlias> *p = new CLinkedNode<CAnyAlias>;
  378. if (p)
  379. {
  380. if (!p->that.InitSpecial(s_rgaf[i].csidlReal, csidlAlias)
  381. || !_InsertAlias(p))
  382. {
  383. delete p;
  384. }
  385. }
  386. break;
  387. }
  388. }
  389. }
  390. // the semantic of the return value of this function is not necessarily success or failure,
  391. // since it's possible to stick something in _ptreeAliases with AddData and not be able to
  392. // clean up and remove it with RemoveData (if CompareIDs fails along the way).
  393. // reordering our inserts won't help since g_pscn->AddClient does the same thing.
  394. // so,
  395. // return TRUE == do not free p, something has ownership
  396. // return FALSE == free p, we dont reference it anywhere
  397. BOOL CChangeNotify::_InsertAlias(CLinkedNode<CAnyAlias> *p)
  398. {
  399. BOOL fRet = _InitTree(&_ptreeAliases);
  400. if (fRet)
  401. {
  402. fRet = _listAliases.Insert(p);
  403. if (fRet)
  404. {
  405. fRet = SUCCEEDED(_ptreeAliases->AddData(IDLDATAF_MATCH_RECURSIVE, p->that._pidlAlias, (INT_PTR)&p->that));
  406. if (fRet)
  407. {
  408. fRet = g_pscn->AddClient(IDLDATAF_MATCH_RECURSIVE, p->that._pidl, NULL, FALSE, SAFECAST(&p->that, CCollapsingClient *));
  409. if (fRet)
  410. {
  411. if (_ptreeClients)
  412. {
  413. // now tell all the registered clients already waiting on this to wake up.
  414. CLinkedWalk<CRegisteredClient> lw(&_listClients);
  415. while (lw.Step())
  416. {
  417. if (ILIsParent(p->that._pidlAlias, lw.That()->_pidl, FALSE))
  418. {
  419. // increase activation count one time on this alias for each client that wants this one.
  420. p->that.Activate(TRUE);
  421. }
  422. }
  423. }
  424. }
  425. else
  426. {
  427. // if we blow it, then we need to clean up.
  428. // right now both the tree and _listAliases have p.
  429. _listAliases.Remove(p); // the list always succeeds
  430. if (FAILED(_ptreeAliases->RemoveData(p->that._pidlAlias, (INT_PTR)&p->that)))
  431. {
  432. // oh no! we added it to the tree but we cant find it to remove it.
  433. // return TRUE to prevent freeing it later.
  434. fRet = TRUE;
  435. }
  436. }
  437. }
  438. else
  439. {
  440. // we only have to remove from _listAliases.
  441. _listAliases.Remove(p); // the list always succeeds
  442. }
  443. }
  444. }
  445. return fRet;
  446. }
  447. void CChangeNotify::AddAlias(LPCITEMIDLIST pidlReal, LPCITEMIDLIST pidlAlias, DWORD dwEventTime)
  448. {
  449. CAnyAlias *paa = _FindAlias(pidlReal, pidlAlias);
  450. if (!paa)
  451. {
  452. CLinkedNode<CAnyAlias> *p = new CLinkedNode<CAnyAlias>;
  453. if (p)
  454. {
  455. if (p->that.Init(pidlReal, pidlAlias))
  456. {
  457. if (_InsertAlias(p))
  458. {
  459. paa = &p->that;
  460. g_cAliases++;
  461. }
  462. }
  463. if (!paa)
  464. delete p;
  465. }
  466. }
  467. if (paa)
  468. {
  469. // we just want to update the time on the existing entry
  470. paa->_dwTime = dwEventTime;
  471. paa->_fRemove = FALSE;
  472. _CheckAliasRollover();
  473. }
  474. }
  475. BOOL CAnyAlias::Remove()
  476. {
  477. if (_fRemove)
  478. {
  479. if (_fSpecial)
  480. {
  481. // we dont remove the special aliases,
  482. // we only quiet them a little
  483. if (_fInterrupt)
  484. {
  485. g_pscn->ReleaseInterruptSource(_pidl);
  486. _fInterrupt = FALSE;
  487. }
  488. _fRemove = FALSE;
  489. }
  490. else
  491. {
  492. return SUCCEEDED(g_pscn->RemoveClient(_pidl, _fInterrupt, SAFECAST(this, CCollapsingClient *)));
  493. }
  494. }
  495. return FALSE;
  496. }
  497. void CChangeNotify::_FreshenAliases(void)
  498. {
  499. CLinkedWalk<CAnyAlias> lw(&_listAliases);
  500. while (lw.Step())
  501. {
  502. CAnyAlias *paa = lw.That();
  503. if (paa->Remove())
  504. {
  505. if (SUCCEEDED(_ptreeAliases->RemoveData(paa->_pidlAlias, (INT_PTR)paa)))
  506. {
  507. // if RemoveData failed, we have to leak the client so the tree doesnt point to freed memory.
  508. lw.Delete();
  509. }
  510. }
  511. }
  512. }
  513. void AnyAlias_Change(LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime)
  514. {
  515. if (lEvent == SHCNE_EXTENDED_EVENT)
  516. {
  517. LPCITEMIDLIST pidlAlias = IsAliasRegisterPidl(pidl);
  518. if (pidlAlias)
  519. g_pscn->AddAlias(pidlAlias, pidlExtra, dwEventTime);
  520. else
  521. {
  522. SHChangeDWORDAsIDList *pdwidl = (SHChangeDWORDAsIDList *)pidl;
  523. if (pdwidl->dwItem1 == SHCNEE_UPDATEFOLDERLOCATION)
  524. {
  525. g_pscn->UpdateSpecialAlias(pdwidl->dwItem2);
  526. }
  527. }
  528. }
  529. }
  530. void NotifyShellInternals(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime)
  531. {
  532. // if they are only interested in the real deal
  533. // make sure we dont pass them the translated events
  534. // this keeps them from getting multiple notifications
  535. // about the same paths, since the alias and the non-alias
  536. // pidls will generally resolve to the same parsing name
  537. // for the events/pidls that these guys are interested in
  538. if (!(SHCNF_TRANSLATEDALIAS & uFlags))
  539. {
  540. PERFTEST(RLFS_EVENT) RLFSChanged(lEvent, (LPITEMIDLIST)pidl, (LPITEMIDLIST)pidlExtra);
  541. PERFTEST(SFP_EVENT) SFP_FSEvent(lEvent, pidl, pidlExtra);
  542. PERFTEST(ICON_EVENT) CFSFolder_IconEvent(lEvent, pidl, pidlExtra);
  543. }
  544. // aliases actually can be children of other aliases, so we need
  545. // them to get the translated events
  546. PERFTEST(ALIAS_EVENT) AnyAlias_Change(lEvent, pidl, pidlExtra, dwEventTime);
  547. }
  548. BOOL IsMultiBitSet(LONG l)
  549. {
  550. return (l && (l & (l-1)));
  551. }
  552. #define CHANGELOCK_SIG 0xbabebabe
  553. #define CHANGEEVENT_SIG 0xfadefade
  554. #define CHANGEREGISTER_SIG 0xdeafdeaf
  555. #ifdef DEBUG
  556. BOOL IsValidChangeEvent(CHANGEEVENT *pce)
  557. {
  558. return (pce && (pce->dwSig == CHANGEEVENT_SIG)
  559. && (!IsMultiBitSet(pce->lEvent)));
  560. }
  561. BOOL _LockSizeMatchEvent(CHANGELOCK *pcl)
  562. {
  563. UINT cbPidlMainAligned = (ILGetSize(pcl->pidlMain) + 3) & ~(0x0000003); // Round up to dword size
  564. UINT cbPidlExtra = ILGetSize(pcl->pidlExtra);
  565. DWORD cbSize = sizeof(CHANGEEVENT) + cbPidlMainAligned + cbPidlExtra;
  566. return cbSize == pcl->pce->cbSize;
  567. }
  568. BOOL IsValidChangeLock(CHANGELOCK *pcl)
  569. {
  570. return (pcl && IsValidChangeEvent(pcl->pce)
  571. && (pcl->dwSig == CHANGELOCK_SIG)
  572. && _LockSizeMatchEvent(pcl));
  573. }
  574. BOOL IsValidChangeEventHandle(HANDLE h, DWORD id)
  575. {
  576. CHANGEEVENT *pce = (CHANGEEVENT *)SHLockSharedEx(h, id, FALSE);
  577. #ifdef DEBUG
  578. BOOL fRet = TRUE; // can fail in low memory so must default to TRUE
  579. #endif // force DEBUG
  580. if (pce)
  581. {
  582. fRet = IsValidChangeEvent(pce);
  583. SHUnlockShared(pce);
  584. }
  585. return fRet;
  586. }
  587. #define ISVALIDCHANGEEVENTHANDLE(h, id) IsValidChangeEventHandle(h, id)
  588. #define ISVALIDCHANGEEVENT(p) IsValidChangeEvent(p)
  589. #define ISVALIDCHANGELOCK(p) IsValidChangeLock(p)
  590. #define ISVALIDCHANGEREGISTER(p) TRUE
  591. #endif
  592. ULONG SHChangeNotification_Destroy(HANDLE hChange, DWORD dwProcId)
  593. {
  594. ASSERT(ISVALIDCHANGEEVENTHANDLE(hChange, dwProcId));
  595. TraceMsg(TF_SHELLCHANGENOTIFY, "CHANGEEVENT destroyed [0x%X]", hChange);
  596. return SHFreeShared(hChange, dwProcId);
  597. }
  598. HANDLE SHChangeNotification_Create(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidlMain, LPCITEMIDLIST pidlExtra, DWORD dwProcId, DWORD dwEventTime)
  599. {
  600. // some bad callers send us multiple events
  601. RIP(!IsMultiBitSet(lEvent));
  602. if (!IsMultiBitSet(lEvent))
  603. {
  604. UINT cbPidlMain = ILGetSize(pidlMain);
  605. UINT cbPidlMainAligned = (cbPidlMain + 3) & ~(0x0000003); // Round up to dword size
  606. UINT cbPidlExtra = ILGetSize(pidlExtra);
  607. DWORD cbSize = sizeof(CHANGEEVENT) + cbPidlMainAligned + cbPidlExtra;
  608. HANDLE h = SHAllocShared(NULL, cbSize, dwProcId);
  609. if (h)
  610. {
  611. CHANGEEVENT * pce = (CHANGEEVENT *) SHLockSharedEx(h, dwProcId, TRUE);
  612. if (pce)
  613. {
  614. BYTE *lpb = (LPBYTE)(pce + 1);
  615. pce->cbSize = cbSize;
  616. pce->dwSig = CHANGEEVENT_SIG;
  617. pce->lEvent = lEvent;
  618. pce->uFlags = uFlags;
  619. pce->dwEventTime = dwEventTime;
  620. if (pidlMain)
  621. {
  622. pce->uidlMain = sizeof(CHANGEEVENT);
  623. CopyMemory(lpb, pidlMain, cbPidlMain);
  624. lpb += cbPidlMainAligned;
  625. }
  626. if (pidlExtra)
  627. {
  628. pce->uidlExtra = (UINT) (lpb - (LPBYTE)pce);
  629. CopyMemory(lpb, pidlExtra, cbPidlExtra);
  630. }
  631. SHUnlockShared(pce);
  632. TraceMsg(TF_SHELLCHANGENOTIFY, "CHANGEEVENT created [0x%X]", h);
  633. }
  634. else
  635. {
  636. SHFreeShared(h, dwProcId);
  637. h = NULL;
  638. }
  639. }
  640. return h;
  641. }
  642. return NULL;
  643. }
  644. CHANGELOCK *_SHChangeNotification_Lock(HANDLE hChange, DWORD dwProcId)
  645. {
  646. CHANGEEVENT *pce = (CHANGEEVENT *) SHLockSharedEx(hChange, dwProcId, FALSE);
  647. if (pce)
  648. {
  649. #ifdef DEBUG
  650. if (!ISVALIDCHANGEEVENT(pce))
  651. {
  652. // during shell32 development it is convenient to use .local to use
  653. // a different version of shell32 than the os version. but then
  654. // non-explorer processes use the old shell32 which might have
  655. // a different CHANGEEVENT structure causing this assert to fire
  656. // and us to fault shortly after. do this hack check to see if
  657. // we are in this situation...
  658. //
  659. static int nExplorerIsLocalized = -1;
  660. if (nExplorerIsLocalized < 1)
  661. {
  662. TCHAR szPath[MAX_PATH];
  663. if (GetModuleFileName(HINST_THISDLL, szPath, ARRAYSIZE(szPath)))
  664. {
  665. PathRemoveFileSpec(szPath);
  666. PathCombine(szPath, szPath, TEXT("explorer.exe.local"));
  667. if (PathFileExists(szPath))
  668. nExplorerIsLocalized = 1;
  669. else
  670. nExplorerIsLocalized = 0;
  671. }
  672. }
  673. if (0==nExplorerIsLocalized)
  674. {
  675. // We should never send ourselves an invalid changeevent!
  676. ASSERT(ISVALIDCHANGEEVENT(pce));
  677. }
  678. else
  679. {
  680. // Except in this case. Rip this out once hit -- I haven't been
  681. // able to repro this in a while...
  682. ASSERTMSG(ISVALIDCHANGEEVENT(pce), "Press 'g', if this doesn't fault you've validated a known .local bug fix for debug only that's hard to repro but a pain when it does. Remove this assert. Thanks.");
  683. return NULL;
  684. }
  685. }
  686. #endif
  687. CHANGELOCK *pcl = (CHANGELOCK *)LocalAlloc(LPTR, sizeof(CHANGELOCK));
  688. if (pcl)
  689. {
  690. pcl->dwSig = CHANGELOCK_SIG;
  691. pcl->pce = pce;
  692. if (pce->uidlMain)
  693. pcl->pidlMain = _ILSkip(pce, pce->uidlMain);
  694. if (pce->uidlExtra)
  695. pcl->pidlExtra = _ILSkip(pce, pce->uidlExtra);
  696. return pcl;
  697. }
  698. else
  699. SHUnlockShared(pce);
  700. }
  701. return NULL;
  702. }
  703. HANDLE SHChangeNotification_Lock(HANDLE hChange, DWORD dwProcId, LPITEMIDLIST **pppidl, LONG *plEvent)
  704. {
  705. CHANGELOCK *pcl = _SHChangeNotification_Lock(hChange, dwProcId);
  706. if (pcl)
  707. {
  708. //
  709. // Give back some easy values (causes less code to change for now)
  710. //
  711. if (pppidl)
  712. *pppidl = &(pcl->pidlMain);
  713. if (plEvent)
  714. *plEvent = pcl->pce->lEvent;
  715. }
  716. return (HANDLE) pcl;
  717. }
  718. BOOL SHChangeNotification_Unlock(HANDLE hLock)
  719. {
  720. CHANGELOCK *pcl = (CHANGELOCK *)hLock;
  721. ASSERT(ISVALIDCHANGELOCK(pcl));
  722. BOOL fRet = SHUnlockShared(pcl->pce);
  723. LocalFree(pcl);
  724. ASSERT(fRet);
  725. return fRet;
  726. }
  727. STDMETHODIMP_(ULONG) CNotifyEvent::AddRef()
  728. {
  729. return InterlockedIncrement(&_cRef);
  730. }
  731. STDMETHODIMP_(ULONG) CNotifyEvent::Release()
  732. {
  733. ASSERT( 0 != _cRef );
  734. ULONG cRef = InterlockedDecrement(&_cRef);
  735. if ( 0 == cRef )
  736. {
  737. delete this;
  738. }
  739. return cRef;
  740. }
  741. BOOL CNotifyEvent::Init(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra)
  742. {
  743. if (pidl)
  744. this->pidl = ILClone(pidl);
  745. if (pidlExtra)
  746. this->pidlExtra = ILClone(pidlExtra);
  747. return ((!pidl || this->pidl) && (!pidlExtra || this->pidlExtra));
  748. }
  749. CNotifyEvent *CNotifyEvent::Create(LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime, UINT uEventFlags)
  750. {
  751. CNotifyEvent *p = new CNotifyEvent(lEvent, dwEventTime, uEventFlags);
  752. if (p)
  753. {
  754. if (!p->Init(pidl, pidlExtra))
  755. {
  756. // we failed here
  757. p->Release();
  758. p = NULL;
  759. }
  760. }
  761. return p;
  762. }
  763. CCollapsingClient::CCollapsingClient()
  764. {
  765. }
  766. CCollapsingClient::~CCollapsingClient()
  767. {
  768. ILFree(_pidl);
  769. if (_dpaPendingEvents)
  770. {
  771. int iCount = _dpaPendingEvents.GetPtrCount();
  772. while (iCount--)
  773. {
  774. CNotifyEvent *pne = _dpaPendingEvents.FastGetPtr(iCount);
  775. // to parallel our UsingEvent() call
  776. pne->Release();
  777. }
  778. _dpaPendingEvents.Destroy();
  779. }
  780. }
  781. ULONG g_ulNextID = 1;
  782. CRegisteredClient::CRegisteredClient()
  783. {
  784. //
  785. // Skip ID 0, as this is our error value.
  786. //
  787. _ulID = g_ulNextID;
  788. if (!++g_ulNextID)
  789. g_ulNextID = 1;
  790. }
  791. CRegisteredClient::~CRegisteredClient()
  792. {
  793. TraceMsg(TF_SHELLCHANGENOTIFY, "SCN::~CRegisteredClient() [0x%X] id = %d", this, _ulID);
  794. }
  795. BOOL CRegisteredClient::Init(HWND hwnd, int fSources, LONG fEvents, UINT wMsg, SHChangeNotifyEntry *pfsne)
  796. {
  797. // need one or the other
  798. ASSERT(fSources & (SHCNRF_InterruptLevel | SHCNRF_ShellLevel));
  799. _hwnd = hwnd;
  800. GetWindowThreadProcessId(hwnd, &_dwProcId);
  801. _fSources = fSources;
  802. _fInterrupt = fSources & SHCNRF_InterruptLevel;
  803. _fEvents = fEvents;
  804. _wMsg = wMsg;
  805. LPITEMIDLIST pidlNew;
  806. if (pfsne->pidl)
  807. pidlNew = ILClone(pfsne->pidl);
  808. else
  809. pidlNew = SHCloneSpecialIDList(NULL, CSIDL_DESKTOP, FALSE);
  810. BOOL fRet = CCollapsingClient::Init(pidlNew, pfsne->fRecursive);
  811. ILFree(pidlNew);
  812. return fRet;
  813. }
  814. BOOL CRegisteredClient::_WantsEvent(LONG lEvent)
  815. {
  816. if (!_fDeadClient && (lEvent & _fEvents))
  817. {
  818. //
  819. // if this event was generated by an interrupt, and the
  820. // client has interrupt notification turned off, we dont want it
  821. //
  822. if (lEvent & SHCNE_INTERRUPT)
  823. {
  824. if (!(_fSources & SHCNRF_InterruptLevel))
  825. {
  826. return FALSE;
  827. }
  828. }
  829. else if (!(_fSources & SHCNRF_ShellLevel))
  830. {
  831. //
  832. // This event was generated by the shell, and the
  833. // client has shell notification turned off, so
  834. // we skip it.
  835. //
  836. return FALSE;
  837. }
  838. return TRUE;
  839. }
  840. return FALSE;
  841. }
  842. BOOL CCollapsingClient::_CanCollapse(LONG lEvent)
  843. {
  844. return (!_CheckUpdatingSelf()
  845. && (lEvent & SHCNE_DISKEVENTS)
  846. && !(lEvent & SHCNE_GLOBALEVENTS)
  847. && (_dpaPendingEvents.GetPtrCount() >= EVENT_OVERFLOW));
  848. }
  849. STDAPI_(BOOL) ILIsEqualEx(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, BOOL fMatchDepth, LPARAM lParam);
  850. //
  851. // checks for null so we dont assert in ILIsEqual
  852. //
  853. BOOL ILIsEqualOrBothNull(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, BOOL fMemCmpOnly)
  854. {
  855. if (!pidl1 || !pidl2)
  856. {
  857. return (pidl1 == pidl2);
  858. }
  859. if (!fMemCmpOnly)
  860. return ILIsEqualEx(pidl1, pidl2, TRUE, SHCIDS_CANONICALONLY);
  861. else
  862. {
  863. UINT cb1 = ILGetSize(pidl1);
  864. return (cb1 == ILGetSize(pidl2) && 0 == memcmp(pidl1, pidl2, cb1));
  865. }
  866. }
  867. #define SHCNE_ELIMINATE_DUPE_EVENTS (SHCNE_ATTRIBUTES | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM | SHCNE_UPDATEIMAGE | SHCNE_FREESPACE)
  868. BOOL CCollapsingClient::_IsDupe(CNotifyEvent *pne)
  869. {
  870. BOOL fRet = FALSE;
  871. if (pne->lEvent & SHCNE_ELIMINATE_DUPE_EVENTS)
  872. {
  873. // look for duplicates starting with the last one
  874. for (int i = _dpaPendingEvents.GetPtrCount() - 1; !fRet && i >= 0; i--)
  875. {
  876. CNotifyEvent *pneMaybe = _dpaPendingEvents.FastGetPtr(i);
  877. if (pne == pneMaybe)
  878. fRet = TRUE;
  879. else if ((pneMaybe->lEvent == pne->lEvent)
  880. && ILIsEqualOrBothNull(pne->pidl, pneMaybe->pidl, (pne->lEvent & SHCNE_GLOBALEVENTS))
  881. && ILIsEqualOrBothNull(pne->pidlExtra, pneMaybe->pidlExtra, (pneMaybe->lEvent & SHCNE_GLOBALEVENTS)))
  882. fRet = TRUE;
  883. }
  884. }
  885. return fRet;
  886. }
  887. BOOL CCollapsingClient::_AddEvent(CNotifyEvent *pneOld, BOOL fFromExtra)
  888. {
  889. CNotifyEvent *pne = pneOld;
  890. pne->AddRef();
  891. BOOL fCollapse = _CanCollapse(pne->lEvent);
  892. if (fCollapse)
  893. {
  894. //
  895. // If we get too many messages in the queue at any given time,
  896. // we set the last message in the cue to be an UPDATEDIR that will
  897. // stand for all messages that we cant fit because the queue is full.
  898. //
  899. BOOL fAddSelf = TRUE;
  900. if (_fRecursive && _dpaPendingEvents.GetPtrCount() < (EVENT_OVERFLOW *2))
  901. {
  902. BOOL fFreeUpdate = FALSE;
  903. LPITEMIDLIST pidlUpdate = fFromExtra ? pne->pidlExtra : pne->pidl;
  904. DWORD dwAttrs = SFGAO_FOLDER;
  905. SHGetNameAndFlags(pidlUpdate, 0, NULL, 0, &dwAttrs);
  906. if (!(dwAttrs & SFGAO_FOLDER))
  907. {
  908. pidlUpdate = ILCloneParent(pidlUpdate);
  909. fFreeUpdate = TRUE;
  910. }
  911. if (pidlUpdate)
  912. {
  913. if (ILGetSize(pidlUpdate) > ILGetSize(_pidl))
  914. {
  915. pne->Release();
  916. // then we should add this folder to the update list
  917. pne = g_pscn->GetEvent(SHCNE_UPDATEDIR, pidlUpdate, NULL, pne->dwEventTime, 0);
  918. if (pne)
  919. {
  920. fAddSelf = FALSE;
  921. }
  922. }
  923. if (fFreeUpdate)
  924. ILFree(pidlUpdate);
  925. }
  926. }
  927. if (fAddSelf && pne)
  928. {
  929. pne->Release();
  930. pne = g_pscn->GetEvent(SHCNE_UPDATEDIR, _pidl, NULL, pne->dwEventTime, 0);
  931. }
  932. }
  933. if (pne)
  934. {
  935. if (!_IsDupe(pne))
  936. {
  937. // if this is one of our special collapsed
  938. // events then we force it in even if we are full
  939. if ((fCollapse || _dpaPendingEvents.GetPtrCount() < EVENT_OVERFLOW)
  940. && _dpaPendingEvents.AppendPtr(pne) != -1)
  941. {
  942. pne->AddRef();
  943. g_pscn->SetFlush(FLUSH_SOFT);
  944. if (!_fUpdatingSelf && (pne->lEvent & SHCNE_UPDATEDIR) && ILIsEqualEx(_pidl, pne->pidl, TRUE, SHCIDS_CANONICALONLY))
  945. {
  946. _fUpdatingSelf = TRUE;
  947. _iUpdatingSelfIndex = _dpaPendingEvents.GetPtrCount() - 1;
  948. }
  949. }
  950. // if we are getting filesystem updates
  951. // always pretend that we overflowed
  952. // this is because UPDATEDIR's are the
  953. // most expensive thing we do.
  954. if (pne->lEvent & SHCNE_INTERRUPT)
  955. {
  956. TraceMsg(TF_SHELLCHANGENOTIFY, "SCN [0x%X]->_AddEvent adding interrupt", this);
  957. _cEvents += EVENT_OVERFLOW;
  958. }
  959. // count all events even if they
  960. // they werent added.
  961. _cEvents++;
  962. }
  963. pne->Release();
  964. }
  965. return TRUE;
  966. }
  967. void CCollapsingClient::Notify(CNotifyEvent *pne, BOOL fFromExtra)
  968. {
  969. if (_WantsEvent(pne->lEvent))
  970. {
  971. _AddEvent(pne, fFromExtra);
  972. }
  973. }
  974. //--------------------------------------------------------------------------
  975. // Notifies hCallbackEvent when all the notification packets for
  976. // all clients in this process have been handled.
  977. //
  978. // This function is primarily called from the FSNotifyThreadProc thread,
  979. // but in flush cases, it can be called from the desktop thread
  980. //
  981. void CALLBACK _DispatchCallbackNoRef(HWND hwnd, UINT uiMsg,
  982. DWORD_PTR dwParam, LRESULT result)
  983. {
  984. MSGEVENT *pme = (MSGEVENT *)dwParam;
  985. SHChangeNotification_Destroy(pme->hChange, pme->dwProcId);
  986. delete pme;
  987. }
  988. void CALLBACK _DispatchCallback(HWND hwnd, UINT uiMsg,
  989. DWORD_PTR hChange, LRESULT result)
  990. {
  991. _DispatchCallbackNoRef(hwnd, uiMsg, hChange, result);
  992. if (EVAL(g_pscn))
  993. g_pscn->PendingCallbacks(FALSE);
  994. }
  995. void CChangeNotify::PendingCallbacks(BOOL fAdd)
  996. {
  997. if (fAdd)
  998. {
  999. _cCallbacks++;
  1000. ASSERT(_cCallbacks != 0);
  1001. //
  1002. // callback count must be non-zero, we just incremented it.
  1003. // Put the event into the reset/false state.
  1004. //
  1005. if (!_hCallbackEvent)
  1006. {
  1007. _hCallbackEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1008. }
  1009. else
  1010. {
  1011. ResetEvent(_hCallbackEvent);
  1012. }
  1013. }
  1014. else
  1015. {
  1016. //
  1017. // PERF: Waits like this happen on flush, but that really cares about flushing that thread
  1018. // only, and this hCallbackEvent is per-process. So that thread may be stuck
  1019. // waiting for some dead app to respond. Fortunately the wait is only 30 seconds,
  1020. // but some wedged window could really make the system crawl...
  1021. //
  1022. ASSERT(_cCallbacks != 0);
  1023. _cCallbacks--;
  1024. if (!_cCallbacks && _hCallbackEvent)
  1025. {
  1026. // we just got the last of our callbacks
  1027. // signal incase somebody is waiting
  1028. SetEvent(_hCallbackEvent);
  1029. }
  1030. }
  1031. }
  1032. BOOL CCollapsingClient::Flush(BOOL fNeedsCallbackEvent)
  1033. {
  1034. BOOL fRet = FALSE;
  1035. if (fNeedsCallbackEvent || _cEvents < EVENT_OVERFLOW)
  1036. {
  1037. TraceMsg(TF_SHELLCHANGENOTIFY, "SCN [0x%X]->Flush is completing", this);
  1038. fRet = _Flush(fNeedsCallbackEvent);
  1039. }
  1040. else
  1041. {
  1042. TraceMsg(TF_SHELLCHANGENOTIFY, "SCN [0x%X]->Flush is deferred", this);
  1043. g_pscn->SetFlush(FLUSH_OVERFLOW);
  1044. }
  1045. _cEvents = 0;
  1046. return fRet;
  1047. }
  1048. void CRegisteredClient::_SendNotification(CNotifyEvent *pne, BOOL fNeedsCallbackEvent, SENDASYNCPROC pfncb)
  1049. {
  1050. // we could possibly reuse one in some cases
  1051. MSGEVENT * pme = pne->GetNotification(_dwProcId);
  1052. if (pme)
  1053. {
  1054. if (fNeedsCallbackEvent)
  1055. {
  1056. g_pscn->PendingCallbacks(TRUE);
  1057. }
  1058. if (!SendMessageCallback(_hwnd, _wMsg,
  1059. (WPARAM)pme->hChange,
  1060. (LPARAM)_dwProcId,
  1061. pfncb,
  1062. (DWORD_PTR)pme))
  1063. {
  1064. pfncb(_hwnd, _wMsg, (DWORD_PTR)pme, 0);
  1065. TraceMsg(TF_WARNING, "(_SHChangeNotifyHandleClientEvents) SendMessageCB timed out");
  1066. // if the hwnd is bad, the process probably died,
  1067. // remove the window from future notifications.
  1068. if (!IsWindow(_hwnd))
  1069. {
  1070. _fDeadClient = TRUE;
  1071. // we failed to Flush
  1072. }
  1073. }
  1074. }
  1075. }
  1076. BOOL CCollapsingClient::_Flush(BOOL fNeedsCallbackEvent)
  1077. {
  1078. if (fNeedsCallbackEvent && _hwnd)
  1079. {
  1080. DWORD_PTR dwResult = 0;
  1081. fNeedsCallbackEvent = (0 != SendMessageTimeout(_hwnd, WM_NULL, 0, 0, SMTO_ABORTIFHUNG, 0, &dwResult));
  1082. }
  1083. SENDASYNCPROC pfncb = fNeedsCallbackEvent ? _DispatchCallback : _DispatchCallbackNoRef;
  1084. BOOL fProcessedAny = FALSE;
  1085. // as long as there are events keep pulling them out
  1086. while (_dpaPendingEvents.GetPtrCount())
  1087. {
  1088. //
  1089. // 2000JUL3 - ZekeL - remove each one from our dpa so that if we reenter
  1090. // a flush during the sendmessage, we wont reprocess the event
  1091. // this also allows for an event to be added to the dpa while
  1092. // we proccessing and still be flushed on this pass.
  1093. //
  1094. CNotifyEvent *pne = _dpaPendingEvents.DeletePtr(0);
  1095. if (pne)
  1096. {
  1097. fProcessedAny = TRUE;
  1098. // we never send this if we are dead
  1099. if (_IsValidClient())
  1100. {
  1101. //
  1102. // if we are about to refresh this client (_fUpdatingSelf)
  1103. // only send if we are looking at the UPDATEDIR of _pidl
  1104. // or if this event is not a disk event.
  1105. //
  1106. if (!_CheckUpdatingSelf()
  1107. || (0 == _iUpdatingSelfIndex)
  1108. || !(pne->lEvent & SHCNE_DISKEVENTS))
  1109. {
  1110. BOOL fPreCall = BOOLIFY(_fUpdatingSelf);
  1111. _SendNotification(pne, fNeedsCallbackEvent, pfncb);
  1112. if (_fUpdatingSelf && !fPreCall)
  1113. {
  1114. // we were re-entered while sending this notification and
  1115. // during the re-entered call we collapsed notifications.
  1116. // the _iUpdatingSelfIndex value was set without knowing
  1117. // that we were going to decrement it after unwinding.
  1118. // account for that now:
  1119. _iUpdatingSelfIndex++;
  1120. }
  1121. }
  1122. #ifdef DEBUG
  1123. if (_fUpdatingSelf && 0 == _iUpdatingSelfIndex)
  1124. {
  1125. // RIP because fault injection
  1126. // can make this fail
  1127. if (!ILIsEqual(_pidl, pne->pidl))
  1128. TraceMsg(TF_WARNING, "CCollapsingClient::_Flush() maybe mismatched _fUpdatingSelf");
  1129. }
  1130. #endif // DEBUG
  1131. }
  1132. _iUpdatingSelfIndex--;
  1133. pne->Release();
  1134. }
  1135. }
  1136. _fUpdatingSelf = FALSE;
  1137. return fProcessedAny;
  1138. }
  1139. HRESULT CChangeNotify::RemoveClient(LPCITEMIDLIST pidl, BOOL fInterrupt, CCollapsingClient *pclient)
  1140. {
  1141. HRESULT hr = S_OK;
  1142. // remove this boy from the tree
  1143. if (_ptreeClients)
  1144. {
  1145. hr = _ptreeClients->RemoveData(pidl, (INT_PTR)pclient);
  1146. if (fInterrupt)
  1147. ReleaseInterruptSource(pidl);
  1148. }
  1149. return hr;
  1150. }
  1151. BOOL CChangeNotify::AddClient(IDLDATAF flags, LPCITEMIDLIST pidl, BOOL *pfInterrupt, BOOL fRecursive, CCollapsingClient *pclient)
  1152. {
  1153. BOOL fRet = FALSE;
  1154. if (_InitTree(&_ptreeClients))
  1155. {
  1156. ASSERT(pclient);
  1157. if (SUCCEEDED(_ptreeClients->AddData(flags, pidl, (INT_PTR)pclient)))
  1158. {
  1159. fRet = TRUE;
  1160. // set up the interrupt events if desired
  1161. if (pfInterrupt && *pfInterrupt)
  1162. {
  1163. *pfInterrupt = AddInterruptSource(pidl, fRecursive);
  1164. }
  1165. }
  1166. }
  1167. return fRet;
  1168. }
  1169. LPITEMIDLIST _ILCloneInterruptID(LPCITEMIDLIST pidl)
  1170. {
  1171. LPITEMIDLIST pidlRet = NULL;
  1172. if (pidl)
  1173. {
  1174. TCHAR sz[MAX_PATH];
  1175. if (SHGetPathFromIDList(pidl, sz))
  1176. {
  1177. WIN32_FIND_DATA fd = {0};
  1178. fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
  1179. SHSimpleIDListFromFindData(sz, &fd, &pidlRet);
  1180. }
  1181. }
  1182. else // NULL is special for desktop
  1183. pidlRet = SHCloneSpecialIDList(NULL, CSIDL_DESKTOPDIRECTORY, FALSE);
  1184. return pidlRet;
  1185. }
  1186. CInterruptSource *CChangeNotify::_InsertInterruptSource(LPCITEMIDLIST pidl, BOOL fRecursive)
  1187. {
  1188. CLinkedNode<CInterruptSource> *p = new CLinkedNode<CInterruptSource>;
  1189. if (p)
  1190. {
  1191. IDLDATAF flags = fRecursive ? IDLDATAF_MATCH_RECURSIVE : IDLDATAF_MATCH_IMMEDIATE;
  1192. if (p->that.Init(pidl, fRecursive)
  1193. && _listInterrupts.Insert(p))
  1194. {
  1195. if (SUCCEEDED(_ptreeInterrupts->AddData(flags, p->that.pidl, (INT_PTR)&p->that)))
  1196. {
  1197. return &p->that;
  1198. }
  1199. else
  1200. {
  1201. _listInterrupts.Remove(p);
  1202. delete p;
  1203. }
  1204. }
  1205. else
  1206. delete p;
  1207. }
  1208. return NULL;
  1209. }
  1210. BOOL CChangeNotify::AddInterruptSource(LPCITEMIDLIST pidlClient, BOOL fRecursive)
  1211. {
  1212. if (_InitTree(&_ptreeInterrupts))
  1213. {
  1214. LPITEMIDLIST pidl = _ILCloneInterruptID(pidlClient);
  1215. if (pidl)
  1216. {
  1217. CInterruptSource *pintc = NULL;
  1218. if (FAILED(_ptreeInterrupts->MatchOne(IDLDATAF_MATCH_EXACT, pidl, (INT_PTR*)&pintc, NULL)))
  1219. {
  1220. pintc = _InsertInterruptSource(pidl, fRecursive);
  1221. }
  1222. ILFree(pidl);
  1223. if (pintc)
  1224. {
  1225. pintc->cClients++;
  1226. return TRUE;
  1227. }
  1228. }
  1229. }
  1230. return FALSE;
  1231. }
  1232. void CChangeNotify::ReleaseInterruptSource(LPCITEMIDLIST pidlClient)
  1233. {
  1234. if (_ptreeInterrupts)
  1235. {
  1236. LPITEMIDLIST pidl = _ILCloneInterruptID(pidlClient);
  1237. if (pidl)
  1238. {
  1239. CInterruptSource *pintc;
  1240. if (SUCCEEDED(_ptreeInterrupts->MatchOne(IDLDATAF_MATCH_EXACT, pidl, (INT_PTR*)&pintc, NULL)))
  1241. {
  1242. if (--(pintc->cClients) == 0)
  1243. {
  1244. // if RemoveData fails, we have to leak the client so the tree doesnt point to freed memory.
  1245. if (SUCCEEDED(_ptreeInterrupts->RemoveData(pidl, (INT_PTR)pintc)))
  1246. {
  1247. CLinkedWalk<CInterruptSource> lw(&_listInterrupts);
  1248. while (lw.Step())
  1249. {
  1250. if (lw.That() == pintc)
  1251. {
  1252. lw.Delete();
  1253. break;
  1254. }
  1255. }
  1256. }
  1257. }
  1258. }
  1259. ILFree(pidl);
  1260. }
  1261. }
  1262. }
  1263. void CChangeNotify::_ActivateAliases(LPCITEMIDLIST pidl, BOOL fActivate)
  1264. {
  1265. if (_ptreeAliases)
  1266. {
  1267. CIDLMatchMany *pmany;
  1268. if (SUCCEEDED(_ptreeAliases->MatchMany(IDLDATAF_MATCH_RECURSIVE, pidl, &pmany)))
  1269. {
  1270. CAnyAlias *paa;
  1271. while (S_OK == pmany->Next((INT_PTR *)&paa, NULL))
  1272. {
  1273. paa->Activate(fActivate);
  1274. }
  1275. delete pmany;
  1276. }
  1277. }
  1278. }
  1279. ULONG CChangeNotify::_RegisterClient(HWND hwnd, int fSources, LONG fEvents, UINT wMsg, SHChangeNotifyEntry *pfsne)
  1280. {
  1281. ULONG ulRet = 0;
  1282. CLinkedNode<CRegisteredClient> *p = new CLinkedNode<CRegisteredClient>;
  1283. if (p)
  1284. {
  1285. if (p->that.Init(hwnd, fSources, fEvents, wMsg, pfsne))
  1286. {
  1287. IDLDATAF flags = IDLDATAF_MATCH_IMMEDIATE;
  1288. if (!pfsne->pidl || pfsne->fRecursive)
  1289. flags = IDLDATAF_MATCH_RECURSIVE;
  1290. if (_listClients.Insert(p)
  1291. && AddClient( flags,
  1292. pfsne->pidl,
  1293. &(p->that._fInterrupt),
  1294. pfsne->fRecursive && (fSources & SHCNRF_RecursiveInterrupt),
  1295. SAFECAST(&p->that, CCollapsingClient *)))
  1296. {
  1297. #ifdef DEBUG
  1298. TCHAR szName[MAX_PATH];
  1299. SHGetNameAndFlags(p->that._pidl, 0, szName, ARRAYSIZE(szName), NULL);
  1300. TraceMsg(TF_SHELLCHANGENOTIFY, "SCN::RegCli() added %s [0x%X] id = %d", szName, p, p->that._ulID);
  1301. #endif
  1302. _ActivateAliases(pfsne->pidl, TRUE);
  1303. ulRet = p->that._ulID;
  1304. }
  1305. }
  1306. if (!ulRet)
  1307. {
  1308. _listClients.Remove(p);
  1309. delete p;
  1310. }
  1311. }
  1312. return ulRet;
  1313. }
  1314. BOOL CChangeNotify::_InitTree(CIDLTree**pptree)
  1315. {
  1316. if (!*pptree)
  1317. {
  1318. CIDLTree::Create(pptree);
  1319. }
  1320. return *pptree != NULL;
  1321. }
  1322. CNotifyEvent *CChangeNotify::GetEvent(LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime, UINT uEventFlags)
  1323. {
  1324. return CNotifyEvent::Create(lEvent, pidl, pidlExtra, dwEventTime, uEventFlags);
  1325. }
  1326. BOOL CChangeNotify::_DeregisterClient(CRegisteredClient *pclient)
  1327. {
  1328. TraceMsg(TF_SHELLCHANGENOTIFY, "SCN::RegCli() removing [0x%X] id = %d", pclient, pclient->_ulID);
  1329. if (SUCCEEDED(RemoveClient(pclient->_pidl, pclient->_fInterrupt, SAFECAST(pclient, CCollapsingClient *))))
  1330. {
  1331. _ActivateAliases(pclient->_pidl, FALSE);
  1332. return TRUE;
  1333. }
  1334. return FALSE;
  1335. }
  1336. BOOL CChangeNotify::_DeregisterClientByID(ULONG ulID)
  1337. {
  1338. BOOL fRet = FALSE;
  1339. CLinkedWalk <CRegisteredClient> lw(&_listClients);
  1340. while (lw.Step())
  1341. {
  1342. if (lw.That()->_ulID == ulID)
  1343. {
  1344. // if we are flushing,
  1345. // then this is coming in while
  1346. // we are in SendMessageTimeout()
  1347. if (!_cFlushing)
  1348. {
  1349. fRet = _DeregisterClient(lw.That());
  1350. if (fRet)
  1351. {
  1352. lw.Delete();
  1353. }
  1354. }
  1355. else
  1356. lw.That()->_fDeadClient = TRUE;
  1357. break;
  1358. }
  1359. }
  1360. return fRet;
  1361. }
  1362. BOOL CChangeNotify::_DeregisterClientsByWindow(HWND hwnd)
  1363. {
  1364. BOOL fRet = FALSE;
  1365. CLinkedWalk <CRegisteredClient> lw(&_listClients);
  1366. while (lw.Step())
  1367. {
  1368. if (lw.That()->_hwnd == hwnd)
  1369. {
  1370. // if we are flushing,
  1371. // then this is coming in while
  1372. // we are in SendMessageTimeout()
  1373. if (!_cFlushing)
  1374. {
  1375. fRet = _DeregisterClient(lw.That());
  1376. if (fRet)
  1377. {
  1378. lw.Delete();
  1379. }
  1380. }
  1381. else
  1382. lw.That()->_fDeadClient = TRUE;
  1383. }
  1384. }
  1385. return fRet;
  1386. }
  1387. void CChangeNotify::_AddGlobalEvent(CNotifyEvent *pne)
  1388. {
  1389. CLinkedWalk <CRegisteredClient> lw(&_listClients);
  1390. while (lw.Step())
  1391. {
  1392. lw.That()->Notify(pne, FALSE);
  1393. }
  1394. // this is the notify we get when a drive mapping is deleted
  1395. // when this happens we need to kill the aliases to that drive
  1396. if ((pne->lEvent == SHCNE_DRIVEREMOVED) && !(pne->uEventFlags & SHCNF_TRANSLATEDALIAS))
  1397. {
  1398. CLinkedWalk<CAnyAlias> lw(&_listAliases);
  1399. while (lw.Step())
  1400. {
  1401. lw.That()->Notify(pne, FALSE);
  1402. }
  1403. }
  1404. }
  1405. void CChangeNotify::_MatchAndNotify(LPCITEMIDLIST pidl, CNotifyEvent *pne, BOOL fFromExtra)
  1406. {
  1407. if (_ptreeClients)
  1408. {
  1409. CIDLMatchMany *pmany;
  1410. if (SUCCEEDED(_ptreeClients->MatchMany(IDLDATAF_MATCH_RECURSIVE, pidl, &pmany)))
  1411. {
  1412. CCollapsingClient *pclient;
  1413. while (S_OK == pmany->Next((INT_PTR *)&pclient, NULL))
  1414. {
  1415. pclient->Notify(pne, fFromExtra);
  1416. }
  1417. delete pmany;
  1418. }
  1419. }
  1420. }
  1421. BOOL CChangeNotify::_AddToClients(LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime, UINT uEventFlags)
  1422. {
  1423. BOOL bOnlyUpdateDirs = TRUE;
  1424. CNotifyEvent *pne = GetEvent(lEvent, pidl, pidlExtra, dwEventTime, uEventFlags);
  1425. if (pne)
  1426. {
  1427. if (lEvent & SHCNE_GLOBALEVENTS)
  1428. {
  1429. _AddGlobalEvent(pne);
  1430. }
  1431. else
  1432. {
  1433. _MatchAndNotify(pidl, pne, FALSE);
  1434. if (pidlExtra)
  1435. _MatchAndNotify(pidlExtra, pne, TRUE);
  1436. }
  1437. pne->Release();
  1438. }
  1439. return bOnlyUpdateDirs;
  1440. }
  1441. BOOL CChangeNotify::_HandleMessages(void)
  1442. {
  1443. MSG msg;
  1444. // There was some message put in our queue, so we need to dispose
  1445. // of it
  1446. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  1447. {
  1448. if (msg.hwnd)
  1449. {
  1450. TranslateMessage(&msg);
  1451. DispatchMessage(&msg);
  1452. }
  1453. else
  1454. {
  1455. switch (msg.message)
  1456. {
  1457. case SCNM_TERMINATE:
  1458. DestroyWindow(g_hwndSCN);
  1459. g_hwndSCN = NULL;
  1460. return TRUE;
  1461. break;
  1462. default:
  1463. TraceMsg(TF_SHELLCHANGENOTIFY, "SCN thread proc: eating unknown message %#lx", msg.message);
  1464. break;
  1465. }
  1466. }
  1467. }
  1468. return FALSE;
  1469. }
  1470. CInterruptSource::~CInterruptSource()
  1471. {
  1472. _Reset(TRUE);
  1473. ILFree(pidl);
  1474. }
  1475. BOOL CInterruptSource::Init(LPCITEMIDLIST pidl, BOOL fRecursive)
  1476. {
  1477. this->pidl = ILClone(pidl);
  1478. _fRecursive = fRecursive;
  1479. return (this->pidl != NULL);
  1480. }
  1481. BOOL CInterruptSource::Flush(void)
  1482. {
  1483. if (FS_SIGNAL == _ssSignal)
  1484. {
  1485. g_pscn->NotifyEvent(SHCNE_UPDATEDIR | SHCNE_INTERRUPT, SHCNF_IDLIST, pidl, NULL, GetTickCount());
  1486. }
  1487. _ssSignal = NO_SIGNAL;
  1488. return TRUE;
  1489. }
  1490. void CInterruptSource::_Reset(BOOL fDeviceNotify)
  1491. {
  1492. if (_hEvent && _hEvent != INVALID_HANDLE_VALUE)
  1493. {
  1494. FindCloseChangeNotification(_hEvent);
  1495. _hEvent = NULL;
  1496. }
  1497. if (fDeviceNotify && _hPNP)
  1498. {
  1499. UnregisterDeviceNotification(_hPNP);
  1500. _hPNP = NULL;
  1501. }
  1502. }
  1503. void CInterruptSource::Reset(BOOL fSignal)
  1504. {
  1505. if (fSignal) // file system event
  1506. {
  1507. switch(_ssSignal)
  1508. {
  1509. case NO_SIGNAL: _ssSignal = FS_SIGNAL; break;
  1510. case SH_SIGNAL: _ssSignal = NO_SIGNAL; break;
  1511. }
  1512. if (!FindNextChangeNotification(_hEvent))
  1513. {
  1514. _Reset(FALSE);
  1515. // when we fail, we dont want
  1516. // to retry. which we will do
  1517. // in the case of _hEvent = NULL;
  1518. _hEvent = INVALID_HANDLE_VALUE;
  1519. }
  1520. }
  1521. else // shell event
  1522. {
  1523. switch(_ssSignal)
  1524. {
  1525. case NO_SIGNAL: _ssSignal = SH_SIGNAL; break;
  1526. case FS_SIGNAL: _ssSignal = NO_SIGNAL; break;
  1527. }
  1528. }
  1529. }
  1530. #define FFCN_INTERESTING_EVENTS (FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES)
  1531. BOOL CInterruptSource::GetEvent(HANDLE *phEvent)
  1532. {
  1533. if (_cSuspend == 0 && cClients)
  1534. {
  1535. // create this here so that it will be owned by our global thread
  1536. if (!_hEvent)
  1537. {
  1538. TCHAR szPath[MAX_PATH];
  1539. if (SHGetPathFromIDList(pidl, szPath))
  1540. {
  1541. _hEvent = FindFirstChangeNotification(szPath, _fRecursive, FFCN_INTERESTING_EVENTS);
  1542. if (_hEvent != INVALID_HANDLE_VALUE)
  1543. {
  1544. // PERF optimization alert: RegisterDeviceNotification is being used for removable drives
  1545. // to ensure that the FindFirstChangeNotification call will not prevent the disk
  1546. // from being ejected or dismounted. However, RegisterDeviceNotification is a very expensive
  1547. // call to make at startup as it brings a bunch of DLLs in the address space. Besides,
  1548. // we really don't need to call this for the system drive since it needs to remain
  1549. // mounted at all times. - FabriceD
  1550. // Exclude FIXED drives too
  1551. int iDrive = PathGetDriveNumber(szPath);
  1552. int nType = DRIVE_UNKNOWN;
  1553. if (iDrive != -1)
  1554. {
  1555. nType = DriveType(iDrive);
  1556. }
  1557. // PERF: Exclude the system drive from the RegisterDeviceNotification calls.
  1558. TCHAR chDrive = *szPath;
  1559. if ((!GetEnvironmentVariable(TEXT("SystemDrive"), szPath, ARRAYSIZE(szPath)) || *szPath != chDrive) &&
  1560. nType != DRIVE_FIXED)
  1561. {
  1562. // DO WE NEED TO UnRegister() first?
  1563. DEV_BROADCAST_HANDLE dbh;
  1564. ZeroMemory(&dbh, sizeof(dbh));
  1565. dbh.dbch_size = sizeof(dbh);
  1566. dbh.dbch_devicetype = DBT_DEVTYP_HANDLE;
  1567. dbh.dbch_handle = _hEvent;
  1568. _hPNP = RegisterDeviceNotification(g_hwndSCN, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE);
  1569. }
  1570. }
  1571. }
  1572. else
  1573. _hEvent = INVALID_HANDLE_VALUE;
  1574. }
  1575. if (_hEvent != INVALID_HANDLE_VALUE)
  1576. {
  1577. *phEvent = _hEvent;
  1578. return TRUE;
  1579. }
  1580. }
  1581. return FALSE;
  1582. }
  1583. void CChangeNotify::_SignalInterrupt(HANDLE hEvent)
  1584. {
  1585. CLinkedWalk<CInterruptSource> lw(&_listInterrupts);
  1586. while (lw.Step())
  1587. {
  1588. // searching for valid clients
  1589. HANDLE h;
  1590. if (lw.That()->GetEvent(&h) && h == hEvent)
  1591. {
  1592. g_pscn->SetFlush(FLUSH_INTERRUPT);
  1593. lw.That()->Reset(TRUE);
  1594. break;
  1595. }
  1596. }
  1597. }
  1598. DWORD CChangeNotify::_GetInterruptEvents(HANDLE *ahEvents, DWORD cEventsSize)
  1599. {
  1600. DWORD cEvents = 0;
  1601. CLinkedWalk<CInterruptSource> lw(&_listInterrupts);
  1602. while (cEvents < cEventsSize && lw.Step())
  1603. {
  1604. // go through and find all the valid
  1605. // clients that need waiting on
  1606. if (lw.That()->GetEvent(&ahEvents[cEvents]))
  1607. {
  1608. // lw.That()->Reset(FALSE);
  1609. cEvents++;
  1610. }
  1611. }
  1612. return cEvents;
  1613. }
  1614. void CChangeNotify::_MessagePump(void)
  1615. {
  1616. DWORD cFails = 0;
  1617. while (TRUE)
  1618. {
  1619. HANDLE ahEvents[MAXIMUM_WAIT_OBJECTS - 1];
  1620. DWORD cEvents = _GetInterruptEvents(ahEvents, ARRAYSIZE(ahEvents));
  1621. // maybe cache the events?
  1622. // NEED to handle pending Events with a Timer
  1623. DWORD dwWaitResult = MsgWaitForMultipleObjectsEx(cEvents, ahEvents,
  1624. INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);
  1625. if (dwWaitResult != (DWORD)-1)
  1626. {
  1627. if (dwWaitResult != WAIT_IO_COMPLETION)
  1628. {
  1629. dwWaitResult -= WAIT_OBJECT_0;
  1630. if (dwWaitResult == cEvents)
  1631. {
  1632. // there is a message
  1633. if (_HandleMessages())
  1634. break;
  1635. }
  1636. else if (dwWaitResult < cEvents)
  1637. {
  1638. _SignalInterrupt(ahEvents[dwWaitResult]);
  1639. }
  1640. }
  1641. cFails = 0;
  1642. }
  1643. else
  1644. {
  1645. // there was some kind of error
  1646. TraceMsg(TF_ERROR, "SCNotify WaitForMulti() failed with %d", GetLastError());
  1647. // if MWFM() fails over and over, we give up.
  1648. if (++cFails > 10)
  1649. {
  1650. TraceMsg(TF_ERROR, "SCNotify WaitForMulti() bailing out");
  1651. break;
  1652. }
  1653. }
  1654. }
  1655. }
  1656. void SCNUninitialize(void)
  1657. {
  1658. if (g_pscn)
  1659. {
  1660. if (IsWindow(g_hwndSCN))
  1661. DestroyWindow(g_hwndSCN);
  1662. g_hwndSCN = NULL;
  1663. delete g_pscn;
  1664. g_pscn = NULL;
  1665. }
  1666. }
  1667. // the real thread proc, runs after CChangeNotify::ThreadStartUp runs sync
  1668. DWORD WINAPI CChangeNotify::ThreadProc(void *pv)
  1669. {
  1670. if (g_pscn)
  1671. {
  1672. CMountPoint::RegisterForHardwareNotifications();
  1673. #ifdef RESTARTSCN
  1674. __try
  1675. #endif
  1676. {
  1677. g_pscn->_MessagePump();
  1678. }
  1679. #ifdef RESTARTSCN
  1680. __except (EXCEPTION_EXECUTE_HANDLER)
  1681. {
  1682. ASSERT(FALSE);
  1683. }
  1684. #endif
  1685. }
  1686. SCNUninitialize();
  1687. return 0;
  1688. }
  1689. BOOL CChangeNotify::_OnChangeRegistration(HANDLE hChangeRegistration, DWORD dwProcId)
  1690. {
  1691. BOOL fResult = FALSE;
  1692. CHANGEREGISTER *pcr = (CHANGEREGISTER *)SHLockSharedEx(hChangeRegistration, dwProcId, TRUE);
  1693. if (pcr)
  1694. {
  1695. SHChangeNotifyEntry fsne;
  1696. fsne.pidl = NULL;
  1697. fsne.fRecursive = pcr->fRecursive;
  1698. if (pcr->uidlRegister)
  1699. fsne.pidl = _ILSkip(pcr, pcr->uidlRegister);
  1700. pcr->ulID = _RegisterClient((HWND)ULongToPtr(pcr->ulHwnd), pcr->fSources,
  1701. pcr->lEvents, pcr->uMsg, &fsne);
  1702. fResult = TRUE;
  1703. SHUnlockShared(pcr);
  1704. }
  1705. return fResult;
  1706. }
  1707. void CChangeNotify::_ResetRelatedInterrupts(LPCITEMIDLIST pidl)
  1708. {
  1709. if (_ptreeInterrupts)
  1710. {
  1711. // we need to match whoever listens on this pidl
  1712. CIDLMatchMany *pmany;
  1713. if (SUCCEEDED(_ptreeInterrupts->MatchMany(IDLDATAF_MATCH_RECURSIVE, pidl, &pmany)))
  1714. {
  1715. CInterruptSource *pintc;
  1716. while (S_OK == pmany->Next((INT_PTR *)&pintc, NULL))
  1717. {
  1718. // we might need WFSO(pintc->GetEvent()) here first
  1719. // if this is already signaled,
  1720. // we need to unsignal
  1721. pintc->Reset(FALSE);
  1722. }
  1723. delete pmany;
  1724. }
  1725. }
  1726. }
  1727. void CChangeNotify::_FlushInterrupts(void)
  1728. {
  1729. CLinkedWalk<CInterruptSource> lw(&_listInterrupts);
  1730. while (lw.Step())
  1731. {
  1732. lw.That()->Flush();
  1733. }
  1734. }
  1735. #define CALLBACK_TIMEOUT 30000 // 30 seconds
  1736. void CChangeNotify::_WaitForCallbacks(void)
  1737. {
  1738. while (_cCallbacks && _hCallbackEvent)
  1739. {
  1740. MSG msg;
  1741. DWORD dwWaitResult = MsgWaitForMultipleObjects(1, &_hCallbackEvent, FALSE,
  1742. CALLBACK_TIMEOUT, QS_SENDMESSAGE);
  1743. TraceMsg(TF_SHELLCHANGENOTIFY, "FSN_WaitForCallbacks returned 0x%X", dwWaitResult);
  1744. if (dwWaitResult == WAIT_OBJECT_0) break; // Event completed
  1745. if (dwWaitResult == WAIT_TIMEOUT) break; // Ran out of time
  1746. if (dwWaitResult == WAIT_OBJECT_0+1)
  1747. {
  1748. //
  1749. // Some message came in, reset message event, deliver callbacks, etc.
  1750. //
  1751. PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); // we need to do this to flush callbacks
  1752. }
  1753. }
  1754. if (_hCallbackEvent)
  1755. {
  1756. CloseHandle(_hCallbackEvent);
  1757. _hCallbackEvent = NULL;
  1758. }
  1759. }
  1760. void CChangeNotify::SetFlush(int idt)
  1761. {
  1762. switch (idt)
  1763. {
  1764. case FLUSH_OVERFLOW:
  1765. case FLUSH_SOFT:
  1766. SetTimer(g_hwndSCN, IDT_SCN_FLUSHEVENTS, 500, NULL);
  1767. break;
  1768. case FLUSH_HARD:
  1769. PostMessage(g_hwndSCN, SCNM_FLUSHEVENTS, 0, 0);
  1770. break;
  1771. case FLUSH_INTERRUPT:
  1772. SetTimer(g_hwndSCN, IDT_SCN_FLUSHEVENTS, 1000, NULL);
  1773. break;
  1774. }
  1775. }
  1776. void CChangeNotify::_Flush(BOOL fShouldWait)
  1777. {
  1778. _cFlushing++;
  1779. KillTimer(g_hwndSCN, IDT_SCN_FLUSHEVENTS);
  1780. // flush any pending interrupt events
  1781. _FlushInterrupts();
  1782. int iNumLoops = 0;
  1783. BOOL fProcessedAny;
  1784. do
  1785. {
  1786. fProcessedAny = FALSE;
  1787. CLinkedWalk<CAnyAlias> lwAliases(&_listAliases);
  1788. while (lwAliases.Step())
  1789. {
  1790. if (lwAliases.That()->Flush(TRUE))
  1791. {
  1792. fProcessedAny = TRUE;
  1793. }
  1794. }
  1795. iNumLoops++;
  1796. // in free builds bail out if there's a loop so we don't spin the thread.
  1797. // but this is pretty bad so assert anyway (the most people would usually have
  1798. // is 2 -- a folder shortcut to something on the desktop / mydocs)
  1799. ASSERTMSG(iNumLoops < 10, "we're in an alias loop, we're screwed");
  1800. } while (fProcessedAny && (iNumLoops < 10));
  1801. CLinkedWalk<CRegisteredClient> lwRegistered(&_listClients);
  1802. while (lwRegistered.Step())
  1803. {
  1804. lwRegistered.That()->Flush(fShouldWait);
  1805. }
  1806. if (fShouldWait)
  1807. {
  1808. // now wait for all the callbacks to empty out
  1809. _WaitForCallbacks();
  1810. }
  1811. _cFlushing--;
  1812. // wait until we have 10 seconds of free time
  1813. SetTimer(g_hwndSCN, IDT_SCN_FRESHENTREES, 10000, NULL);
  1814. }
  1815. BOOL IsILShared(LPCITEMIDLIST pidl, BOOL fUpdateCache)
  1816. {
  1817. TCHAR szTemp[MAXPATHLEN];
  1818. SHGetPathFromIDList(pidl, szTemp);
  1819. return IsShared(szTemp, fUpdateCache);
  1820. }
  1821. void CChangeNotify::NotifyEvent(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime)
  1822. {
  1823. if (!(uFlags & SHCNF_ONLYNOTIFYINTERNALS) && lEvent)
  1824. {
  1825. /// now do the actual generating of the event
  1826. if (lEvent & (SHCNE_NETSHARE | SHCNE_NETUNSHARE))
  1827. {
  1828. // Update the cache.
  1829. IsILShared(pidl, TRUE);
  1830. }
  1831. _AddToClients(lEvent, pidl, pidlExtra, dwEventTime, uFlags);
  1832. // remove any shell generated events for the file system
  1833. if ((lEvent & SHCNE_DISKEVENTS) &&
  1834. !(lEvent & (SHCNE_INTERRUPT | SHCNE_UPDATEDIR)))
  1835. {
  1836. _ResetRelatedInterrupts(pidl);
  1837. if (pidlExtra)
  1838. _ResetRelatedInterrupts(pidlExtra);
  1839. }
  1840. }
  1841. // note make sure the internal events go first.
  1842. if (lEvent)
  1843. NotifyShellInternals(lEvent, uFlags, pidl, pidlExtra, dwEventTime);
  1844. //
  1845. // then the registered events
  1846. //
  1847. if (uFlags & (SHCNF_FLUSH))
  1848. {
  1849. if (uFlags & SHCNF_FLUSHNOWAIT)
  1850. {
  1851. SetFlush(FLUSH_HARD);
  1852. }
  1853. else
  1854. _Flush(TRUE);
  1855. }
  1856. }
  1857. LRESULT CChangeNotify::_OnNotifyEvent(HANDLE hChange, DWORD dwProcId)
  1858. {
  1859. CHANGELOCK *pcl = _SHChangeNotification_Lock(hChange, dwProcId);
  1860. if (pcl)
  1861. {
  1862. NotifyEvent(pcl->pce->lEvent,
  1863. pcl->pce->uFlags,
  1864. pcl->pidlMain,
  1865. pcl->pidlExtra,
  1866. pcl->pce->dwEventTime);
  1867. SHChangeNotification_Unlock(pcl);
  1868. SHChangeNotification_Destroy(hChange, dwProcId);
  1869. }
  1870. return TRUE;
  1871. }
  1872. void CInterruptSource::Suspend(BOOL fSuspend)
  1873. {
  1874. if (fSuspend)
  1875. {
  1876. if (!_cSuspend)
  1877. _Reset(FALSE);
  1878. _cSuspend++;
  1879. }
  1880. else if (_cSuspend)
  1881. _cSuspend--;
  1882. }
  1883. BOOL CChangeNotify::_SuspendResume(BOOL fSuspend, BOOL fRecursive, LPCITEMIDLIST pidl)
  1884. {
  1885. if (_ptreeInterrupts)
  1886. {
  1887. CInterruptSource *pintc;
  1888. if (!fRecursive)
  1889. {
  1890. if (SUCCEEDED(_ptreeInterrupts->MatchOne(IDLDATAF_MATCH_EXACT, pidl, (INT_PTR*)&pintc, NULL)))
  1891. {
  1892. pintc->Suspend(fSuspend);
  1893. }
  1894. }
  1895. else
  1896. {
  1897. CIDLMatchMany *pmany;
  1898. if (SUCCEEDED(_ptreeInterrupts->MatchMany(IDLDATAF_MATCH_RECURSIVE, pidl, &pmany)))
  1899. {
  1900. while (S_OK == pmany->Next((INT_PTR *)&pintc, NULL))
  1901. {
  1902. pintc->Suspend(fSuspend);
  1903. }
  1904. delete pmany;
  1905. }
  1906. }
  1907. }
  1908. return TRUE;
  1909. }
  1910. #define SCNSUSPEND_SUSPEND 1
  1911. #define SCNSUSPEND_RECURSIVE 2
  1912. LRESULT CChangeNotify::_OnSuspendResume(HANDLE hChange, DWORD dwProcId)
  1913. {
  1914. BOOL fRet = FALSE;
  1915. CHANGELOCK *pcl = _SHChangeNotification_Lock(hChange, dwProcId);
  1916. if (pcl)
  1917. {
  1918. fRet = _SuspendResume(pcl->pce->uFlags & SCNSUSPEND_SUSPEND, pcl->pce->uFlags & SCNSUSPEND_RECURSIVE, pcl->pidlMain);
  1919. SHChangeNotification_Unlock((HANDLE)pcl);
  1920. }
  1921. return fRet;
  1922. }
  1923. BOOL CInterruptSource::SuspendDevice(BOOL fSuspend, HDEVNOTIFY hPNP)
  1924. {
  1925. BOOL fRet = FALSE;
  1926. if (hPNP)
  1927. {
  1928. if (fSuspend && _hPNP == hPNP)
  1929. {
  1930. _hSuspended = _hPNP;
  1931. Suspend(fSuspend);
  1932. _Reset(TRUE);
  1933. fRet = TRUE;
  1934. }
  1935. else if (!fSuspend && _hSuspended == hPNP)
  1936. {
  1937. _hSuspended = NULL;
  1938. Suspend(fSuspend);
  1939. fRet = TRUE;
  1940. }
  1941. }
  1942. else if (_hPNP)
  1943. {
  1944. // NULL means we are shutting down and should close all handles.
  1945. UnregisterDeviceNotification(_hPNP);
  1946. _hPNP = NULL;
  1947. }
  1948. return fRet;
  1949. }
  1950. // __HandleDevice
  1951. void CChangeNotify::_OnDeviceBroadcast(ULONG_PTR code, DEV_BROADCAST_HANDLE *pbhnd)
  1952. {
  1953. if (IsWindowVisible(GetShellWindow()) && pbhnd
  1954. && (pbhnd->dbch_devicetype == DBT_DEVTYP_HANDLE && pbhnd->dbch_hdevnotify))
  1955. {
  1956. BOOL fSuspend;
  1957. switch (code)
  1958. {
  1959. // When PnP is finished messing with the drive (either successfully
  1960. // or unsuccessfully), resume notifications on that drive.
  1961. case DBT_DEVICEREMOVECOMPLETE:
  1962. case DBT_DEVICEQUERYREMOVEFAILED:
  1963. fSuspend = FALSE;
  1964. break;
  1965. // When PnP is starting to mess with the drive, suspend notifications
  1966. // so it can do its thing
  1967. case DBT_DEVICEQUERYREMOVE:
  1968. // This will wait on another thread to exit if this hdevnotify
  1969. // was registered for a Sniffing Dialog
  1970. CSniffDrive::HandleNotif(pbhnd->dbch_hdevnotify);
  1971. fSuspend = TRUE;
  1972. break;
  1973. case DBT_CUSTOMEVENT:
  1974. if (GUID_IO_VOLUME_LOCK == pbhnd->dbch_eventguid)
  1975. {
  1976. TraceMsg(TF_MOUNTPOINT, "GUID_IO_VOLUME_LOCK: Suspending!");
  1977. fSuspend = TRUE;
  1978. }
  1979. else
  1980. {
  1981. if (GUID_IO_VOLUME_LOCK_FAILED == pbhnd->dbch_eventguid)
  1982. {
  1983. TraceMsg(TF_MOUNTPOINT, "GUID_IO_VOLUME_LOCK_FAILED: Resuming!");
  1984. fSuspend = FALSE;
  1985. }
  1986. else
  1987. {
  1988. if (GUID_IO_VOLUME_UNLOCK == pbhnd->dbch_eventguid)
  1989. {
  1990. TraceMsg(TF_MOUNTPOINT, "GUID_IO_VOLUME_UNLOCK: Resuming!");
  1991. fSuspend = FALSE;
  1992. }
  1993. }
  1994. }
  1995. break;
  1996. default:
  1997. // we dont handle anything else here
  1998. return;
  1999. }
  2000. CLinkedWalk<CInterruptSource> lw(&_listInterrupts);
  2001. while (lw.Step())
  2002. {
  2003. // returns true if found
  2004. if (lw.That()->SuspendDevice(fSuspend, pbhnd->dbch_hdevnotify))
  2005. break;
  2006. }
  2007. }
  2008. }
  2009. void CChangeNotify::_FreshenClients(void)
  2010. {
  2011. CLinkedWalk<CRegisteredClient> lw(&_listClients);
  2012. while (lw.Step())
  2013. {
  2014. if (lw.That()->_fDeadClient || !IsWindow(lw.That()->_hwnd))
  2015. {
  2016. if (_DeregisterClient(lw.That()))
  2017. {
  2018. lw.Delete();
  2019. }
  2020. }
  2021. }
  2022. }
  2023. void CChangeNotify::_FreshenUp(void)
  2024. {
  2025. ASSERT(!_cFlushing);
  2026. KillTimer(g_hwndSCN, IDT_SCN_FRESHENTREES);
  2027. if (_ptreeClients)
  2028. _ptreeClients->Freshen();
  2029. if (_ptreeInterrupts)
  2030. _ptreeInterrupts->Freshen();
  2031. _FreshenAliases();
  2032. _FreshenClients();
  2033. }
  2034. LRESULT CChangeNotify::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2035. {
  2036. LRESULT lRes = 0;
  2037. ASSERT(g_pscn);
  2038. switch (uMsg)
  2039. {
  2040. case SCNM_REGISTERCLIENT:
  2041. lRes = g_pscn->_OnChangeRegistration((HANDLE)wParam, (DWORD)lParam);
  2042. break;
  2043. case SCNM_DEREGISTERCLIENT:
  2044. lRes = g_pscn->_DeregisterClientByID((ULONG)wParam);
  2045. break;
  2046. case SCNM_DEREGISTERWINDOW:
  2047. lRes = g_pscn->_DeregisterClientsByWindow((HWND)wParam);
  2048. break;
  2049. case SCNM_NOTIFYEVENT:
  2050. lRes = g_pscn->_OnNotifyEvent((HANDLE)wParam, (DWORD)lParam);
  2051. break;
  2052. case SCNM_SUSPENDRESUME:
  2053. lRes = g_pscn->_OnSuspendResume((HANDLE)wParam, (DWORD)lParam);
  2054. break;
  2055. case WM_TIMER:
  2056. if (wParam == IDT_SCN_FRESHENTREES)
  2057. {
  2058. g_pscn->_FreshenUp();
  2059. break;
  2060. }
  2061. // Fall through to SCNM_FLUSHEVENTS
  2062. case SCNM_FLUSHEVENTS:
  2063. g_pscn->_Flush(FALSE);
  2064. break;
  2065. case SCNM_AUTOPLAYDRIVE:
  2066. CMountPoint::DoAutorunPrompt(wParam);
  2067. break;
  2068. case WM_DEVICECHANGE:
  2069. g_pscn->_OnDeviceBroadcast(wParam, (DEV_BROADCAST_HANDLE *)lParam);
  2070. break;
  2071. default:
  2072. lRes = DefWindowProc(hwnd, uMsg, wParam, lParam);
  2073. break;
  2074. }
  2075. return lRes;
  2076. }
  2077. // thread setup routine, executed before SHCreateThread() returns
  2078. DWORD WINAPI CChangeNotify::ThreadStartUp(void *pv)
  2079. {
  2080. g_pscn = new CChangeNotify();
  2081. if (g_pscn)
  2082. {
  2083. g_hwndSCN = SHCreateWorkerWindow(CChangeNotify::WndProc, NULL, 0, 0, NULL, g_pscn);
  2084. CSniffDrive::InitNotifyWindow(g_hwndSCN);
  2085. InitAliasFolderTable();
  2086. }
  2087. return 0;
  2088. }
  2089. // now we create the window
  2090. BOOL SCNInitialize()
  2091. {
  2092. EnterCriticalSection(&g_csSCN);
  2093. if (!IsWindow(g_hwndSCN))
  2094. {
  2095. SHCreateThread(CChangeNotify::ThreadProc, NULL, CTF_COINIT, CChangeNotify::ThreadStartUp);
  2096. }
  2097. LeaveCriticalSection(&g_csSCN);
  2098. return g_hwndSCN ? TRUE : FALSE; // ThreadStartUp is executed sync
  2099. }
  2100. BOOL _IsImpersonating()
  2101. {
  2102. HANDLE hToken;
  2103. if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken))
  2104. {
  2105. CloseHandle(hToken);
  2106. return TRUE;
  2107. }
  2108. return FALSE;
  2109. }
  2110. STDAPI_(HWND) _SCNGetWindow(BOOL fUseDesktop, BOOL fNeedsFallback)
  2111. {
  2112. // if explorer is trashed
  2113. // then this hwnd can go bad
  2114. // get a new copy from the desktop
  2115. if (!g_hwndSCN || !IsWindow(g_hwndSCN))
  2116. {
  2117. HWND hwndDesktop = fUseDesktop ? GetShellWindow() : NULL;
  2118. if (hwndDesktop)
  2119. {
  2120. HWND hwndSCN = (HWND) SendMessage(hwndDesktop, CWM_GETSCNWINDOW, 0, 0);
  2121. if (_IsImpersonating())
  2122. return hwndSCN;
  2123. else
  2124. g_hwndSCN = hwndSCN;
  2125. }
  2126. else if (fNeedsFallback && SHIsCurrentThreadInteractive())
  2127. {
  2128. // there is no desktop.
  2129. // so we create a private desktop
  2130. // this will create the thread and window
  2131. // and set
  2132. SCNInitialize();
  2133. }
  2134. }
  2135. return g_hwndSCN;
  2136. }
  2137. STDAPI_(HWND) SCNGetWindow(BOOL fUseDesktop)
  2138. {
  2139. return _SCNGetWindow(fUseDesktop, TRUE);
  2140. }
  2141. HANDLE SHChangeRegistration_Create(ULONG ulID,
  2142. HWND hwnd, UINT uMsg,
  2143. DWORD fSources, LONG lEvents,
  2144. BOOL fRecursive, LPCITEMIDLIST pidl,
  2145. DWORD dwProcId)
  2146. {
  2147. UINT uidlSize = ILGetSize(pidl);
  2148. HANDLE hReg = SHAllocShared(NULL, sizeof(CHANGEREGISTER) + uidlSize, dwProcId);
  2149. if (hReg)
  2150. {
  2151. CHANGEREGISTER *pcr = (CHANGEREGISTER *) SHLockSharedEx(hReg, dwProcId, TRUE);
  2152. if (pcr)
  2153. {
  2154. pcr->dwSig = CHANGEREGISTER_SIG;
  2155. pcr->ulID = ulID;
  2156. pcr->ulHwnd = PtrToUlong(hwnd);
  2157. pcr->uMsg = uMsg;
  2158. pcr->fSources = fSources;
  2159. pcr->lEvents = lEvents;
  2160. pcr->fRecursive = fRecursive;
  2161. pcr->uidlRegister = 0;
  2162. if (pidl)
  2163. {
  2164. pcr->uidlRegister = sizeof(CHANGEREGISTER);
  2165. memcpy((pcr + 1), pidl, uidlSize);
  2166. }
  2167. SHUnlockShared(pcr);
  2168. }
  2169. else
  2170. {
  2171. SHFreeShared(hReg, dwProcId);
  2172. hReg = NULL;
  2173. }
  2174. }
  2175. return hReg;
  2176. }
  2177. typedef struct
  2178. {
  2179. HWND hwnd;
  2180. UINT wMsg;
  2181. } NOTIFY_PROXY_DATA;
  2182. #define WM_CHANGENOTIFYMSG WM_USER + 1
  2183. LRESULT CALLBACK _HiddenNotifyWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
  2184. {
  2185. LRESULT lRes = FALSE;
  2186. NOTIFY_PROXY_DATA *pData = (NOTIFY_PROXY_DATA *) GetWindowLongPtr( hWnd, 0 );
  2187. switch (iMessage)
  2188. {
  2189. case WM_NCDESTROY:
  2190. ASSERT(pData != NULL );
  2191. // clear it so it won't be in use....
  2192. SetWindowLongPtr( hWnd, 0, (LONG_PTR)NULL );
  2193. // free the memory ...
  2194. LocalFree( pData );
  2195. break;
  2196. case WM_CHANGENOTIFYMSG :
  2197. if (pData)
  2198. {
  2199. // lock and break the info structure ....
  2200. LPITEMIDLIST *ppidl;
  2201. LONG lEvent;
  2202. HANDLE hLock = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
  2203. if (hLock)
  2204. {
  2205. // pass on to the old style client. ...
  2206. lRes = SendMessage( pData->hwnd, pData->wMsg, (WPARAM) ppidl, (LPARAM) lEvent );
  2207. // new notifications ......
  2208. SHChangeNotification_Unlock(hLock);
  2209. }
  2210. }
  2211. break;
  2212. default:
  2213. lRes = DefWindowProc(hWnd, iMessage, wParam, lParam);
  2214. break;
  2215. }
  2216. return lRes;
  2217. }
  2218. HWND _CreateProxyWindow(HWND hwnd, UINT wMsg)
  2219. {
  2220. HWND hwndRet = NULL;
  2221. // This is an old style notification, we need to create a hidden
  2222. // proxy type of window to properly handle the messages...
  2223. NOTIFY_PROXY_DATA *pnpd = (NOTIFY_PROXY_DATA *)LocalAlloc(LPTR, sizeof(*pnpd));
  2224. if (pnpd)
  2225. {
  2226. pnpd->hwnd = hwnd;
  2227. pnpd->wMsg = wMsg;
  2228. hwndRet = SHCreateWorkerWindow(_HiddenNotifyWndProc, NULL, 0, 0, NULL, pnpd);
  2229. if (!hwndRet)
  2230. LocalFree(pnpd);
  2231. }
  2232. return hwndRet;
  2233. }
  2234. //--------------------------------------------------------------------------
  2235. //
  2236. // Returns a positive integer registration ID, or 0 if out of memory or if
  2237. // invalid parameters were passed in.
  2238. //
  2239. // If the hwnd is != NULL we do a PostMessage(hwnd, wMsg, ...) when a
  2240. // relevant FS event takes place, otherwise if fsncb is != NULL we call it.
  2241. //
  2242. STDAPI_(ULONG) SHChangeNotifyRegister(HWND hwnd,
  2243. int fSources, LONG fEvents,
  2244. UINT wMsg, int cEntries,
  2245. SHChangeNotifyEntry *pfsne)
  2246. {
  2247. ULONG ulID = 0;
  2248. BOOL fResult = FALSE;
  2249. HWND hwndSCN = SCNGetWindow(TRUE);
  2250. if (hwndSCN)
  2251. {
  2252. if (!(fSources & SHCNRF_NewDelivery))
  2253. {
  2254. // Now setup to use the proxy window instead
  2255. hwnd = _CreateProxyWindow(hwnd, wMsg);
  2256. wMsg = WM_CHANGENOTIFYMSG;
  2257. }
  2258. if ((fSources & SHCNRF_RecursiveInterrupt) && !(fSources & SHCNRF_InterruptLevel))
  2259. {
  2260. // bad caller, they asked for recursive interrupt events, but not interrupt events
  2261. ASSERTMSG(FALSE, "SHChangeNotifyRegister: caller passed SHCNRF_RecursiveInterrupt but NOT SHCNRF_InterruptLevel !!");
  2262. // clear the flag
  2263. fSources = fSources & (~SHCNRF_RecursiveInterrupt);
  2264. }
  2265. // This same assert is CRegisteredClient::Init, caled by SCNM_REGISTERCLIENT message below
  2266. ASSERT(fSources & (SHCNRF_InterruptLevel | SHCNRF_ShellLevel));
  2267. // NOTE - if we have more than one registration entry here,
  2268. // we only support Deregister'ing the last one
  2269. for (int i = 0; i < cEntries; i++)
  2270. {
  2271. DWORD dwProcId;
  2272. GetWindowThreadProcessId(hwndSCN, &dwProcId);
  2273. HANDLE hChangeRegistration = SHChangeRegistration_Create(
  2274. ulID, hwnd, wMsg,
  2275. fSources, fEvents,
  2276. pfsne[i].fRecursive, pfsne[i].pidl,
  2277. dwProcId);
  2278. if (hChangeRegistration)
  2279. {
  2280. CHANGEREGISTER * pcr;
  2281. //
  2282. // Transmit the change regsitration
  2283. //
  2284. SendMessage(hwndSCN, SCNM_REGISTERCLIENT,
  2285. (WPARAM)hChangeRegistration, (LPARAM)dwProcId);
  2286. //
  2287. // Now get back the ulID value, for further registrations and
  2288. // for returning to the calling function...
  2289. //
  2290. pcr = (CHANGEREGISTER *)SHLockSharedEx(hChangeRegistration, dwProcId, FALSE);
  2291. if (pcr)
  2292. {
  2293. ulID = pcr->ulID;
  2294. SHUnlockShared(pcr);
  2295. }
  2296. else
  2297. {
  2298. ASSERT(0 == ulID); // Error condition initialized above
  2299. }
  2300. SHFreeShared(hChangeRegistration, dwProcId);
  2301. }
  2302. if ((ulID == 0) && !(fSources & SHCNRF_NewDelivery))
  2303. {
  2304. // this is our proxy window
  2305. DestroyWindow(hwnd);
  2306. break;
  2307. }
  2308. }
  2309. }
  2310. return ulID;
  2311. }
  2312. //--------------------------------------------------------------------------
  2313. //
  2314. // Returns TRUE if we found and removed the specified Client, otherwise
  2315. // returns FALSE.
  2316. //
  2317. STDAPI_(BOOL) SHChangeNotifyDeregister(ULONG ulID)
  2318. {
  2319. BOOL fResult = FALSE;
  2320. HWND hwnd = _SCNGetWindow(TRUE, FALSE);
  2321. if (hwnd)
  2322. {
  2323. //
  2324. // Transmit the change registration
  2325. //
  2326. fResult = (BOOL) SendMessage(hwnd, SCNM_DEREGISTERCLIENT, ulID, 0);
  2327. }
  2328. return fResult;
  2329. }
  2330. // send the notify to the desktop... telling it to put it in the queue.
  2331. // if we are in the desktop's process, we can handle it directly ourselves.
  2332. // the one exception is flush. we want the desktop to be one serializing flush so
  2333. // we send in that case as well
  2334. void SHChangeNotifyTransmit(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime)
  2335. {
  2336. HWND hwndSCN = _SCNGetWindow(TRUE, FALSE);
  2337. if (hwndSCN)
  2338. {
  2339. DWORD dwProcId;
  2340. GetWindowThreadProcessId(hwndSCN, &dwProcId);
  2341. HANDLE hChange = SHChangeNotification_Create(lEvent, uFlags, pidl, pidlExtra, dwProcId, dwEventTime);
  2342. if (hChange)
  2343. {
  2344. BOOL fFlushNow = ((uFlags & (SHCNF_FLUSH | SHCNF_FLUSHNOWAIT)) == SHCNF_FLUSH);
  2345. // Flush but not flush no wait
  2346. if (fFlushNow)
  2347. {
  2348. SendMessage(hwndSCN, SCNM_NOTIFYEVENT,
  2349. (WPARAM)hChange, (LPARAM)dwProcId);
  2350. }
  2351. else
  2352. {
  2353. SendNotifyMessage(hwndSCN, SCNM_NOTIFYEVENT,
  2354. (WPARAM)hChange, (LPARAM)dwProcId);
  2355. }
  2356. }
  2357. }
  2358. }
  2359. void FreeSpacePidlToPath(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  2360. {
  2361. TCHAR szPath1[MAX_PATH];
  2362. if (SHGetPathFromIDList(pidl1, szPath1))
  2363. {
  2364. TCHAR szPath2[MAX_PATH];
  2365. szPath2[0] = 0;
  2366. if (pidl2)
  2367. {
  2368. SHGetPathFromIDList(pidl2, szPath2);
  2369. }
  2370. SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATH, szPath1, szPath2[0] ? szPath2 : NULL);
  2371. }
  2372. }
  2373. STDAPI_(void) SHChangeNotify(LONG lEvent, UINT uFlags, const void * dwItem1, const void * dwItem2)
  2374. {
  2375. if (!_SCNGetWindow(TRUE, FALSE))
  2376. return;
  2377. LPCITEMIDLIST pidl = NULL;
  2378. LPCITEMIDLIST pidlExtra = NULL;
  2379. LPITEMIDLIST pidlFree = NULL;
  2380. LPITEMIDLIST pidlExtraFree = NULL;
  2381. UINT uType = uFlags & SHCNF_TYPE;
  2382. SHChangeDWORDAsIDList dwidl;
  2383. BOOL fPrinter = FALSE;
  2384. BOOL fPrintJob = FALSE;
  2385. DWORD dwEventTime = GetTickCount();
  2386. // first setup anything the flags request
  2387. switch (uType)
  2388. {
  2389. case SHCNF_PRINTJOBA:
  2390. fPrintJob = TRUE;
  2391. // fall through
  2392. case SHCNF_PRINTERA:
  2393. fPrinter = TRUE;
  2394. // fall through
  2395. case SHCNF_PATHA:
  2396. {
  2397. TCHAR szPath1[MAX_PATH], szPath2[MAX_PATH];
  2398. LPCVOID pvItem1 = NULL;
  2399. LPCVOID pvItem2 = NULL;
  2400. if (dwItem1)
  2401. {
  2402. SHAnsiToTChar((LPSTR)dwItem1, szPath1, ARRAYSIZE(szPath1));
  2403. pvItem1 = szPath1;
  2404. }
  2405. if (dwItem2)
  2406. {
  2407. if (fPrintJob)
  2408. pvItem2 = dwItem2; // SHCNF_PRINTJOB_DATA needs no conversion
  2409. else
  2410. {
  2411. SHAnsiToTChar((LPSTR)dwItem2, szPath2, ARRAYSIZE(szPath2));
  2412. pvItem2 = szPath2;
  2413. }
  2414. }
  2415. SHChangeNotify(lEvent, (fPrintJob ? SHCNF_PRINTJOB : (fPrinter ? SHCNF_PRINTER : SHCNF_PATH)),
  2416. pvItem1, pvItem2);
  2417. goto Cleanup; // Let the recursive version do all the work
  2418. }
  2419. break;
  2420. case SHCNF_PATH:
  2421. if (lEvent == SHCNE_FREESPACE)
  2422. {
  2423. DWORD dwItem = 0;
  2424. int idDrive = PathGetDriveNumber((LPCTSTR)dwItem1);
  2425. if (idDrive != -1)
  2426. dwItem = (1 << idDrive);
  2427. if (dwItem2)
  2428. {
  2429. idDrive = PathGetDriveNumber((LPCTSTR)dwItem2);
  2430. if (idDrive != -1)
  2431. dwItem |= (1 << idDrive);
  2432. }
  2433. dwItem1 = (LPCVOID)ULongToPtr( dwItem );
  2434. if (dwItem1)
  2435. goto DoDWORD;
  2436. goto Cleanup;
  2437. }
  2438. else
  2439. {
  2440. if (dwItem1)
  2441. {
  2442. pidl = pidlFree = SHSimpleIDListFromPath((LPCTSTR)dwItem1);
  2443. if (!pidl)
  2444. goto Cleanup;
  2445. if (dwItem2)
  2446. {
  2447. pidlExtra = pidlExtraFree = SHSimpleIDListFromPath((LPCTSTR)dwItem2);
  2448. if (!pidlExtra)
  2449. goto Cleanup;
  2450. }
  2451. }
  2452. }
  2453. break;
  2454. case SHCNF_PRINTER:
  2455. if (dwItem1)
  2456. {
  2457. TraceMsg(TF_SHELLCHANGENOTIFY, "SHChangeNotify: SHCNF_PRINTER %s", (LPTSTR)dwItem1);
  2458. if (FAILED(ParsePrinterName((LPCTSTR)dwItem1, &pidlFree)))
  2459. {
  2460. goto Cleanup;
  2461. }
  2462. pidl = pidlFree;
  2463. if (dwItem2)
  2464. {
  2465. if (FAILED(ParsePrinterName((LPCTSTR)dwItem2, &pidlExtraFree)))
  2466. {
  2467. goto Cleanup;
  2468. }
  2469. pidlExtra = pidlExtraFree;
  2470. }
  2471. }
  2472. break;
  2473. case SHCNF_PRINTJOB:
  2474. if (dwItem1)
  2475. {
  2476. #ifdef DEBUG
  2477. switch (lEvent)
  2478. {
  2479. case SHCNE_CREATE:
  2480. TraceMsg(TF_SHELLCHANGENOTIFY, "SHChangeNotify: SHCNE_CREATE SHCNF_PRINTJOB %s", (LPTSTR)dwItem1);
  2481. break;
  2482. case SHCNE_DELETE:
  2483. TraceMsg(TF_SHELLCHANGENOTIFY, "SHChangeNotify: SHCNE_DELETE SHCNF_PRINTJOB %s", (LPTSTR)dwItem1);
  2484. break;
  2485. case SHCNE_UPDATEITEM:
  2486. TraceMsg(TF_SHELLCHANGENOTIFY, "SHChangeNotify: SHCNE_UPDATEITEM SHCNF_PRINTJOB %s", (LPTSTR)dwItem1);
  2487. break;
  2488. default:
  2489. TraceMsg(TF_SHELLCHANGENOTIFY, "SHChangeNotify: SHCNE_? SHCNF_PRINTJOB %s", (LPTSTR)dwItem1);
  2490. break;
  2491. }
  2492. #endif
  2493. pidl = pidlFree = Printjob_GetPidl((LPCTSTR)dwItem1, (LPSHCNF_PRINTJOB_DATA)dwItem2);
  2494. if (!pidl)
  2495. goto Cleanup;
  2496. }
  2497. else
  2498. {
  2499. // Caller goofed.
  2500. goto Cleanup;
  2501. }
  2502. break;
  2503. case SHCNF_DWORD:
  2504. DoDWORD:
  2505. ASSERT(lEvent & SHCNE_GLOBALEVENTS);
  2506. dwidl.cb = sizeof(dwidl) - sizeof(dwidl.cbZero);
  2507. dwidl.dwItem1 = PtrToUlong(dwItem1);
  2508. dwidl.dwItem2 = PtrToUlong(dwItem2);
  2509. dwidl.cbZero = 0;
  2510. pidl = (LPCITEMIDLIST)&dwidl;
  2511. pidlExtra = NULL;
  2512. break;
  2513. case 0:
  2514. if (lEvent == SHCNE_FREESPACE) {
  2515. // convert this to paths.
  2516. FreeSpacePidlToPath((LPCITEMIDLIST)dwItem1, (LPCITEMIDLIST)dwItem2);
  2517. goto Cleanup;
  2518. }
  2519. pidl = (LPCITEMIDLIST)dwItem1;
  2520. pidlExtra = (LPCITEMIDLIST)dwItem2;
  2521. break;
  2522. default:
  2523. TraceMsg(TF_ERROR, "SHChangeNotify: Unrecognized uFlags 0x%X", uFlags);
  2524. return;
  2525. }
  2526. if (lEvent && !(lEvent & SHCNE_ASSOCCHANGED) && !pidl)
  2527. {
  2528. // Caller goofed. SHChangeNotifyTransmit & clients assume pidl is
  2529. // non-NULL if lEvent is non-zero (except in the SHCNE_ASSOCCHANGED case),
  2530. // and they will crash if we try to send this bogus event. So throw out
  2531. // this event and rip.
  2532. RIP(FALSE);
  2533. goto Cleanup;
  2534. }
  2535. SHChangeNotifyTransmit(lEvent, uFlags, pidl, pidlExtra, dwEventTime);
  2536. Cleanup:
  2537. if (pidlFree)
  2538. ILFree(pidlFree);
  2539. if (pidlExtraFree)
  2540. ILFree(pidlExtraFree);
  2541. }
  2542. // SHChangeNotifySuspendResume
  2543. //
  2544. // Suspends or resumes filesystem notifications on a path. If bRecursive
  2545. // is set, disable/enables them for all child paths as well.
  2546. STDAPI_(BOOL) SHChangeNotifySuspendResume(BOOL bSuspend,
  2547. LPITEMIDLIST pidlSuspend,
  2548. BOOL bRecursive,
  2549. DWORD dwReserved)
  2550. {
  2551. BOOL fRet = FALSE;
  2552. HWND hwndSCN = _SCNGetWindow(TRUE, FALSE);
  2553. if (hwndSCN)
  2554. {
  2555. HANDLE hChange;
  2556. DWORD dwProcId;
  2557. UINT uiFlags = bSuspend ? SCNSUSPEND_SUSPEND : 0;
  2558. if (bRecursive)
  2559. uiFlags |= SCNSUSPEND_RECURSIVE;
  2560. GetWindowThreadProcessId(hwndSCN, &dwProcId);
  2561. // overloading the structure semantics here a little bit.
  2562. // our two flags
  2563. hChange = SHChangeNotification_Create(0, uiFlags, pidlSuspend, NULL, dwProcId, 0);
  2564. if (hChange)
  2565. {
  2566. // Transmit to SCN
  2567. fRet = (BOOL)SendMessage(hwndSCN, SCNM_SUSPENDRESUME, (WPARAM)hChange, (LPARAM)dwProcId);
  2568. SHChangeNotification_Destroy(hChange, dwProcId);
  2569. }
  2570. }
  2571. return fRet;
  2572. }
  2573. STDAPI_(void) SHChangeNotifyTerminate(BOOL bLastTerm, BOOL bProcessShutdown)
  2574. {
  2575. if (g_pscn)
  2576. {
  2577. PostThreadMessage(GetWindowThreadProcessId(g_hwndSCN, NULL), SCNM_TERMINATE, 0, 0);
  2578. }
  2579. }
  2580. // this deregisters anything that this window might have been registered in
  2581. STDAPI_(void) SHChangeNotifyDeregisterWindow(HWND hwnd)
  2582. {
  2583. HWND hwndSCN = _SCNGetWindow(TRUE, FALSE);
  2584. if (hwndSCN)
  2585. {
  2586. SendMessage(hwndSCN, SCNM_DEREGISTERWINDOW, (WPARAM)hwnd, 0);
  2587. }
  2588. }
  2589. //--------------------------------------------------------------------------
  2590. // We changed the way that the SHChangeNotifyRegister function worked, so
  2591. // to prevent people from calling the old function, we stub it out here.
  2592. // The change we made would have broken everbody because we changed the
  2593. // lparam and wparam for the notification messages which are sent to the
  2594. // registered window.
  2595. //
  2596. STDAPI_(ULONG) NTSHChangeNotifyRegister(HWND hwnd,
  2597. int fSources, LONG fEvents,
  2598. UINT wMsg, int cEntries,
  2599. SHChangeNotifyEntry *pfsne)
  2600. {
  2601. return SHChangeNotifyRegister(hwnd, fSources | SHCNRF_NewDelivery , fEvents, wMsg, cEntries, pfsne);
  2602. }
  2603. STDAPI_(BOOL) NTSHChangeNotifyDeregister(ULONG ulID)
  2604. {
  2605. return SHChangeNotifyDeregister(ulID);
  2606. }
  2607. // NOTE: There is a copy of these functions in shdocvw util.cpp for browser only mode supprt.
  2608. // NOTE: functionality changes should also be reflected there.
  2609. STDAPI_(void) SHUpdateImageA( LPCSTR pszHashItem, int iIndex, UINT uFlags, int iImageIndex )
  2610. {
  2611. WCHAR szWHash[MAX_PATH];
  2612. SHAnsiToUnicode(pszHashItem, szWHash, ARRAYSIZE(szWHash));
  2613. SHUpdateImageW(szWHash, iIndex, uFlags, iImageIndex);
  2614. }
  2615. STDAPI_(void) SHUpdateImageW( LPCWSTR pszHashItem, int iIndex, UINT uFlags, int iImageIndex )
  2616. {
  2617. SHChangeUpdateImageIDList rgPidl;
  2618. SHChangeDWORDAsIDList rgDWord;
  2619. int cLen = MAX_PATH - (lstrlenW( pszHashItem ) + 1);
  2620. cLen *= sizeof( WCHAR );
  2621. if ( cLen < 0 )
  2622. {
  2623. cLen = 0;
  2624. }
  2625. // make sure we send a valid index
  2626. if ( iImageIndex == -1 )
  2627. {
  2628. iImageIndex = II_DOCUMENT;
  2629. }
  2630. rgPidl.dwProcessID = GetCurrentProcessId();
  2631. rgPidl.iIconIndex = iIndex;
  2632. rgPidl.iCurIndex = iImageIndex;
  2633. rgPidl.uFlags = uFlags;
  2634. StrCpyNW(rgPidl.szName, pszHashItem, ARRAYSIZE(rgPidl.szName));
  2635. rgPidl.cb = (USHORT)(sizeof( rgPidl ) - cLen);
  2636. _ILNext( (LPITEMIDLIST) &rgPidl )->mkid.cb = 0;
  2637. rgDWord.cb = sizeof( rgDWord) - sizeof(USHORT);
  2638. rgDWord.dwItem1 = iImageIndex;
  2639. rgDWord.dwItem2 = 0;
  2640. rgDWord.cbZero = 0;
  2641. // pump it as an extended event
  2642. SHChangeNotify(SHCNE_UPDATEIMAGE, SHCNF_IDLIST, &rgDWord, &rgPidl);
  2643. }
  2644. // REVIEW: pretty poor implementation of handling updateimage, requiring the caller
  2645. // to handle the pidl case instead of passing both pidls down here.
  2646. //
  2647. STDAPI_(int) SHHandleUpdateImage( LPCITEMIDLIST pidlExtra )
  2648. {
  2649. SHChangeUpdateImageIDList * pUs = (SHChangeUpdateImageIDList*) pidlExtra;
  2650. if ( !pUs )
  2651. {
  2652. return -1;
  2653. }
  2654. // if in the same process, or an old style notification
  2655. if ( pUs->dwProcessID == GetCurrentProcessId())
  2656. {
  2657. return *(int UNALIGNED *)((BYTE *)&pUs->iCurIndex);
  2658. }
  2659. else
  2660. {
  2661. WCHAR szBuffer[MAX_PATH];
  2662. int iIconIndex = *(int UNALIGNED *)((BYTE *)&pUs->iIconIndex);
  2663. UINT uFlags = *(UINT UNALIGNED *)((BYTE *)&pUs->uFlags);
  2664. ualstrcpynW(szBuffer, pUs->szName, ARRAYSIZE(szBuffer));
  2665. // we are in a different process, look up the hash in our index to get the right one...
  2666. return SHLookupIconIndexW( szBuffer, iIconIndex, uFlags );
  2667. }
  2668. }
  2669. //
  2670. // NOTE: these are OLD APIs, new clients should use new APIs
  2671. //
  2672. // REVIEW: BobDay - SHChangeNotifyUpdateEntryList doesn't appear to be
  2673. // called by anybody and since we've change the notification message
  2674. // structure, anybody who calls it needs to be identified and fixed.
  2675. //
  2676. BOOL WINAPI SHChangeNotifyUpdateEntryList(ULONG ulID, int iUpdateType,
  2677. int cEntries, SHChangeNotifyEntry *pfsne)
  2678. {
  2679. ASSERT(FALSE);
  2680. return FALSE;
  2681. }
  2682. void SHChangeNotifyReceive(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra)
  2683. {
  2684. ASSERT(FALSE);
  2685. }
  2686. BOOL WINAPI SHChangeRegistrationReceive(HANDLE hChangeRegistration, DWORD dwProcId)
  2687. {
  2688. ASSERT(FALSE);
  2689. return FALSE;
  2690. }