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.

3103 lines
89 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. if (InterlockedDecrement(&_cRef))
  734. return _cRef;
  735. delete this;
  736. return 0;
  737. }
  738. BOOL CNotifyEvent::Init(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra)
  739. {
  740. if (pidl)
  741. this->pidl = ILClone(pidl);
  742. if (pidlExtra)
  743. this->pidlExtra = ILClone(pidlExtra);
  744. return ((!pidl || this->pidl) && (!pidlExtra || this->pidlExtra));
  745. }
  746. CNotifyEvent *CNotifyEvent::Create(LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime, UINT uEventFlags)
  747. {
  748. CNotifyEvent *p = new CNotifyEvent(lEvent, dwEventTime, uEventFlags);
  749. if (p)
  750. {
  751. if (!p->Init(pidl, pidlExtra))
  752. {
  753. // we failed here
  754. p->Release();
  755. p = NULL;
  756. }
  757. }
  758. return p;
  759. }
  760. CCollapsingClient::CCollapsingClient()
  761. {
  762. }
  763. CCollapsingClient::~CCollapsingClient()
  764. {
  765. ILFree(_pidl);
  766. if (_dpaPendingEvents)
  767. {
  768. int iCount = _dpaPendingEvents.GetPtrCount();
  769. while (iCount--)
  770. {
  771. CNotifyEvent *pne = _dpaPendingEvents.FastGetPtr(iCount);
  772. // to parallel our UsingEvent() call
  773. pne->Release();
  774. }
  775. _dpaPendingEvents.Destroy();
  776. }
  777. }
  778. ULONG g_ulNextID = 1;
  779. CRegisteredClient::CRegisteredClient()
  780. {
  781. //
  782. // Skip ID 0, as this is our error value.
  783. //
  784. _ulID = g_ulNextID;
  785. if (!++g_ulNextID)
  786. g_ulNextID = 1;
  787. }
  788. CRegisteredClient::~CRegisteredClient()
  789. {
  790. TraceMsg(TF_SHELLCHANGENOTIFY, "SCN::~CRegisteredClient() [0x%X] id = %d", this, _ulID);
  791. }
  792. BOOL CRegisteredClient::Init(HWND hwnd, int fSources, LONG fEvents, UINT wMsg, SHChangeNotifyEntry *pfsne)
  793. {
  794. // need one or the other
  795. ASSERT(fSources & (SHCNRF_InterruptLevel | SHCNRF_ShellLevel));
  796. _hwnd = hwnd;
  797. GetWindowThreadProcessId(hwnd, &_dwProcId);
  798. _fSources = fSources;
  799. _fInterrupt = fSources & SHCNRF_InterruptLevel;
  800. _fEvents = fEvents;
  801. _wMsg = wMsg;
  802. LPITEMIDLIST pidlNew;
  803. if (pfsne->pidl)
  804. pidlNew = ILClone(pfsne->pidl);
  805. else
  806. pidlNew = SHCloneSpecialIDList(NULL, CSIDL_DESKTOP, FALSE);
  807. BOOL fRet = CCollapsingClient::Init(pidlNew, pfsne->fRecursive);
  808. ILFree(pidlNew);
  809. return fRet;
  810. }
  811. BOOL CRegisteredClient::_WantsEvent(LONG lEvent)
  812. {
  813. if (!_fDeadClient && (lEvent & _fEvents))
  814. {
  815. //
  816. // if this event was generated by an interrupt, and the
  817. // client has interrupt notification turned off, we dont want it
  818. //
  819. if (lEvent & SHCNE_INTERRUPT)
  820. {
  821. if (!(_fSources & SHCNRF_InterruptLevel))
  822. {
  823. return FALSE;
  824. }
  825. }
  826. else if (!(_fSources & SHCNRF_ShellLevel))
  827. {
  828. //
  829. // This event was generated by the shell, and the
  830. // client has shell notification turned off, so
  831. // we skip it.
  832. //
  833. return FALSE;
  834. }
  835. return TRUE;
  836. }
  837. return FALSE;
  838. }
  839. BOOL CCollapsingClient::_CanCollapse(LONG lEvent)
  840. {
  841. return (!_CheckUpdatingSelf()
  842. && (lEvent & SHCNE_DISKEVENTS)
  843. && !(lEvent & SHCNE_GLOBALEVENTS)
  844. && (_dpaPendingEvents.GetPtrCount() >= EVENT_OVERFLOW));
  845. }
  846. STDAPI_(BOOL) ILIsEqualEx(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, BOOL fMatchDepth, LPARAM lParam);
  847. //
  848. // checks for null so we dont assert in ILIsEqual
  849. //
  850. BOOL ILIsEqualOrBothNull(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, BOOL fMemCmpOnly)
  851. {
  852. if (!pidl1 || !pidl2)
  853. {
  854. return (pidl1 == pidl2);
  855. }
  856. if (!fMemCmpOnly)
  857. return ILIsEqualEx(pidl1, pidl2, TRUE, SHCIDS_CANONICALONLY);
  858. else
  859. {
  860. UINT cb1 = ILGetSize(pidl1);
  861. return (cb1 == ILGetSize(pidl2) && 0 == memcmp(pidl1, pidl2, cb1));
  862. }
  863. }
  864. #define SHCNE_ELIMINATE_DUPE_EVENTS (SHCNE_ATTRIBUTES | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM | SHCNE_UPDATEIMAGE | SHCNE_FREESPACE)
  865. BOOL CCollapsingClient::_IsDupe(CNotifyEvent *pne)
  866. {
  867. BOOL fRet = FALSE;
  868. if (pne->lEvent & SHCNE_ELIMINATE_DUPE_EVENTS)
  869. {
  870. // look for duplicates starting with the last one
  871. for (int i = _dpaPendingEvents.GetPtrCount() - 1; !fRet && i >= 0; i--)
  872. {
  873. CNotifyEvent *pneMaybe = _dpaPendingEvents.FastGetPtr(i);
  874. if (pne == pneMaybe)
  875. fRet = TRUE;
  876. else if ((pneMaybe->lEvent == pne->lEvent)
  877. && ILIsEqualOrBothNull(pne->pidl, pneMaybe->pidl, (pne->lEvent & SHCNE_GLOBALEVENTS))
  878. && ILIsEqualOrBothNull(pne->pidlExtra, pneMaybe->pidlExtra, (pneMaybe->lEvent & SHCNE_GLOBALEVENTS)))
  879. fRet = TRUE;
  880. }
  881. }
  882. return fRet;
  883. }
  884. BOOL CCollapsingClient::_AddEvent(CNotifyEvent *pneOld, BOOL fFromExtra)
  885. {
  886. CNotifyEvent *pne = pneOld;
  887. pne->AddRef();
  888. BOOL fCollapse = _CanCollapse(pne->lEvent);
  889. if (fCollapse)
  890. {
  891. //
  892. // If we get too many messages in the queue at any given time,
  893. // we set the last message in the cue to be an UPDATEDIR that will
  894. // stand for all messages that we cant fit because the queue is full.
  895. //
  896. BOOL fAddSelf = TRUE;
  897. if (_fRecursive && _dpaPendingEvents.GetPtrCount() < (EVENT_OVERFLOW *2))
  898. {
  899. BOOL fFreeUpdate = FALSE;
  900. LPITEMIDLIST pidlUpdate = fFromExtra ? pne->pidlExtra : pne->pidl;
  901. DWORD dwAttrs = SFGAO_FOLDER;
  902. SHGetNameAndFlags(pidlUpdate, 0, NULL, 0, &dwAttrs);
  903. if (!(dwAttrs & SFGAO_FOLDER))
  904. {
  905. pidlUpdate = ILCloneParent(pidlUpdate);
  906. fFreeUpdate = TRUE;
  907. }
  908. if (pidlUpdate)
  909. {
  910. if (ILGetSize(pidlUpdate) > ILGetSize(_pidl))
  911. {
  912. pne->Release();
  913. // then we should add this folder to the update list
  914. pne = g_pscn->GetEvent(SHCNE_UPDATEDIR, pidlUpdate, NULL, pne->dwEventTime, 0);
  915. if (pne)
  916. {
  917. fAddSelf = FALSE;
  918. }
  919. }
  920. if (fFreeUpdate)
  921. ILFree(pidlUpdate);
  922. }
  923. }
  924. if (fAddSelf && pne)
  925. {
  926. pne->Release();
  927. pne = g_pscn->GetEvent(SHCNE_UPDATEDIR, _pidl, NULL, pne->dwEventTime, 0);
  928. }
  929. }
  930. if (pne)
  931. {
  932. if (!_IsDupe(pne))
  933. {
  934. // if this is one of our special collapsed
  935. // events then we force it in even if we are full
  936. if ((fCollapse || _dpaPendingEvents.GetPtrCount() < EVENT_OVERFLOW)
  937. && _dpaPendingEvents.AppendPtr(pne) != -1)
  938. {
  939. pne->AddRef();
  940. g_pscn->SetFlush(FLUSH_SOFT);
  941. if (!_fUpdatingSelf && (pne->lEvent & SHCNE_UPDATEDIR) && ILIsEqualEx(_pidl, pne->pidl, TRUE, SHCIDS_CANONICALONLY))
  942. {
  943. _fUpdatingSelf = TRUE;
  944. _iUpdatingSelfIndex = _dpaPendingEvents.GetPtrCount() - 1;
  945. }
  946. }
  947. // if we are getting filesystem updates
  948. // always pretend that we overflowed
  949. // this is because UPDATEDIR's are the
  950. // most expensive thing we do.
  951. if (pne->lEvent & SHCNE_INTERRUPT)
  952. {
  953. TraceMsg(TF_SHELLCHANGENOTIFY, "SCN [0x%X]->_AddEvent adding interrupt", this);
  954. _cEvents += EVENT_OVERFLOW;
  955. }
  956. // count all events even if they
  957. // they werent added.
  958. _cEvents++;
  959. }
  960. pne->Release();
  961. }
  962. return TRUE;
  963. }
  964. void CCollapsingClient::Notify(CNotifyEvent *pne, BOOL fFromExtra)
  965. {
  966. if (_WantsEvent(pne->lEvent))
  967. {
  968. _AddEvent(pne, fFromExtra);
  969. }
  970. }
  971. //--------------------------------------------------------------------------
  972. // Notifies hCallbackEvent when all the notification packets for
  973. // all clients in this process have been handled.
  974. //
  975. // This function is primarily called from the FSNotifyThreadProc thread,
  976. // but in flush cases, it can be called from the desktop thread
  977. //
  978. void CALLBACK _DispatchCallbackNoRef(HWND hwnd, UINT uiMsg,
  979. DWORD_PTR dwParam, LRESULT result)
  980. {
  981. MSGEVENT *pme = (MSGEVENT *)dwParam;
  982. SHChangeNotification_Destroy(pme->hChange, pme->dwProcId);
  983. delete pme;
  984. }
  985. void CALLBACK _DispatchCallback(HWND hwnd, UINT uiMsg,
  986. DWORD_PTR hChange, LRESULT result)
  987. {
  988. _DispatchCallbackNoRef(hwnd, uiMsg, hChange, result);
  989. if (EVAL(g_pscn))
  990. g_pscn->PendingCallbacks(FALSE);
  991. }
  992. void CChangeNotify::PendingCallbacks(BOOL fAdd)
  993. {
  994. if (fAdd)
  995. {
  996. _cCallbacks++;
  997. ASSERT(_cCallbacks != 0);
  998. //
  999. // callback count must be non-zero, we just incremented it.
  1000. // Put the event into the reset/false state.
  1001. //
  1002. if (!_hCallbackEvent)
  1003. {
  1004. _hCallbackEvent = CreateEvent(NULL, TRUE, FALSE, TEXT("Shell_NotificationCallbacksOutstanding"));
  1005. }
  1006. else
  1007. {
  1008. ResetEvent(_hCallbackEvent);
  1009. }
  1010. }
  1011. else
  1012. {
  1013. //
  1014. // PERF: Waits like this happen on flush, but that really cares about flushing that thread
  1015. // only, and this hCallbackEvent is per-process. So that thread may be stuck
  1016. // waiting for some dead app to respond. Fortunately the wait is only 30 seconds,
  1017. // but some wedged window could really make the system crawl...
  1018. //
  1019. ASSERT(_cCallbacks != 0);
  1020. _cCallbacks--;
  1021. if (!_cCallbacks && _hCallbackEvent)
  1022. {
  1023. // we just got the last of our callbacks
  1024. // signal incase somebody is waiting
  1025. SetEvent(_hCallbackEvent);
  1026. }
  1027. }
  1028. }
  1029. BOOL CCollapsingClient::Flush(BOOL fNeedsCallbackEvent)
  1030. {
  1031. BOOL fRet = FALSE;
  1032. if (fNeedsCallbackEvent || _cEvents < EVENT_OVERFLOW)
  1033. {
  1034. TraceMsg(TF_SHELLCHANGENOTIFY, "SCN [0x%X]->Flush is completing", this);
  1035. fRet = _Flush(fNeedsCallbackEvent);
  1036. }
  1037. else
  1038. {
  1039. TraceMsg(TF_SHELLCHANGENOTIFY, "SCN [0x%X]->Flush is deferred", this);
  1040. g_pscn->SetFlush(FLUSH_OVERFLOW);
  1041. }
  1042. _cEvents = 0;
  1043. return fRet;
  1044. }
  1045. void CRegisteredClient::_SendNotification(CNotifyEvent *pne, BOOL fNeedsCallbackEvent, SENDASYNCPROC pfncb)
  1046. {
  1047. // we could possibly reuse one in some cases
  1048. MSGEVENT * pme = pne->GetNotification(_dwProcId);
  1049. if (pme)
  1050. {
  1051. if (fNeedsCallbackEvent)
  1052. {
  1053. g_pscn->PendingCallbacks(TRUE);
  1054. }
  1055. if (!SendMessageCallback(_hwnd, _wMsg,
  1056. (WPARAM)pme->hChange,
  1057. (LPARAM)_dwProcId,
  1058. pfncb,
  1059. (DWORD_PTR)pme))
  1060. {
  1061. pfncb(_hwnd, _wMsg, (DWORD_PTR)pme, 0);
  1062. TraceMsg(TF_WARNING, "(_SHChangeNotifyHandleClientEvents) SendMessageCB timed out");
  1063. // if the hwnd is bad, the process probably died,
  1064. // remove the window from future notifications.
  1065. if (!IsWindow(_hwnd))
  1066. {
  1067. _fDeadClient = TRUE;
  1068. // we failed to Flush
  1069. }
  1070. }
  1071. }
  1072. }
  1073. BOOL CCollapsingClient::_Flush(BOOL fNeedsCallbackEvent)
  1074. {
  1075. if (fNeedsCallbackEvent && _hwnd)
  1076. {
  1077. DWORD_PTR dwResult = 0;
  1078. fNeedsCallbackEvent = (0 != SendMessageTimeout(_hwnd, WM_NULL, 0, 0, SMTO_ABORTIFHUNG, 0, &dwResult));
  1079. }
  1080. SENDASYNCPROC pfncb = fNeedsCallbackEvent ? _DispatchCallback : _DispatchCallbackNoRef;
  1081. BOOL fProcessedAny = FALSE;
  1082. // as long as there are events keep pulling them out
  1083. while (_dpaPendingEvents.GetPtrCount())
  1084. {
  1085. //
  1086. // 2000JUL3 - ZekeL - remove each one from our dpa so that if we reenter
  1087. // a flush during the sendmessage, we wont reprocess the event
  1088. // this also allows for an event to be added to the dpa while
  1089. // we proccessing and still be flushed on this pass.
  1090. //
  1091. CNotifyEvent *pne = _dpaPendingEvents.DeletePtr(0);
  1092. if (pne)
  1093. {
  1094. fProcessedAny = TRUE;
  1095. // we never send this if we are dead
  1096. if (_IsValidClient())
  1097. {
  1098. //
  1099. // if we are about to refresh this client (_fUpdatingSelf)
  1100. // only send if we are looking at the UPDATEDIR of _pidl
  1101. // or if this event is not a disk event.
  1102. //
  1103. if (!_CheckUpdatingSelf()
  1104. || (0 == _iUpdatingSelfIndex)
  1105. || !(pne->lEvent & SHCNE_DISKEVENTS))
  1106. {
  1107. BOOL fPreCall = BOOLIFY(_fUpdatingSelf);
  1108. _SendNotification(pne, fNeedsCallbackEvent, pfncb);
  1109. if (_fUpdatingSelf && !fPreCall)
  1110. {
  1111. // we were re-entered while sending this notification and
  1112. // during the re-entered call we collapsed notifications.
  1113. // the _iUpdatingSelfIndex value was set without knowing
  1114. // that we were going to decrement it after unwinding.
  1115. // account for that now:
  1116. _iUpdatingSelfIndex++;
  1117. }
  1118. }
  1119. #ifdef DEBUG
  1120. if (_fUpdatingSelf && 0 == _iUpdatingSelfIndex)
  1121. {
  1122. // RIP because fault injection
  1123. // can make this fail
  1124. if (!ILIsEqual(_pidl, pne->pidl))
  1125. TraceMsg(TF_WARNING, "CCollapsingClient::_Flush() maybe mismatched _fUpdatingSelf");
  1126. }
  1127. #endif // DEBUG
  1128. }
  1129. _iUpdatingSelfIndex--;
  1130. pne->Release();
  1131. }
  1132. }
  1133. _fUpdatingSelf = FALSE;
  1134. return fProcessedAny;
  1135. }
  1136. HRESULT CChangeNotify::RemoveClient(LPCITEMIDLIST pidl, BOOL fInterrupt, CCollapsingClient *pclient)
  1137. {
  1138. HRESULT hr = S_OK;
  1139. // remove this boy from the tree
  1140. if (_ptreeClients)
  1141. {
  1142. hr = _ptreeClients->RemoveData(pidl, (INT_PTR)pclient);
  1143. if (fInterrupt)
  1144. ReleaseInterruptSource(pidl);
  1145. }
  1146. return hr;
  1147. }
  1148. BOOL CChangeNotify::AddClient(IDLDATAF flags, LPCITEMIDLIST pidl, BOOL *pfInterrupt, BOOL fRecursive, CCollapsingClient *pclient)
  1149. {
  1150. BOOL fRet = FALSE;
  1151. if (_InitTree(&_ptreeClients))
  1152. {
  1153. ASSERT(pclient);
  1154. if (SUCCEEDED(_ptreeClients->AddData(flags, pidl, (INT_PTR)pclient)))
  1155. {
  1156. fRet = TRUE;
  1157. // set up the interrupt events if desired
  1158. if (pfInterrupt && *pfInterrupt)
  1159. {
  1160. *pfInterrupt = AddInterruptSource(pidl, fRecursive);
  1161. }
  1162. }
  1163. }
  1164. return fRet;
  1165. }
  1166. LPITEMIDLIST _ILCloneInterruptID(LPCITEMIDLIST pidl)
  1167. {
  1168. LPITEMIDLIST pidlRet = NULL;
  1169. if (pidl)
  1170. {
  1171. TCHAR sz[MAX_PATH];
  1172. if (SHGetPathFromIDList(pidl, sz))
  1173. {
  1174. WIN32_FIND_DATA fd = {0};
  1175. fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
  1176. SHSimpleIDListFromFindData(sz, &fd, &pidlRet);
  1177. }
  1178. }
  1179. else // NULL is special for desktop
  1180. pidlRet = SHCloneSpecialIDList(NULL, CSIDL_DESKTOPDIRECTORY, FALSE);
  1181. return pidlRet;
  1182. }
  1183. CInterruptSource *CChangeNotify::_InsertInterruptSource(LPCITEMIDLIST pidl, BOOL fRecursive)
  1184. {
  1185. CLinkedNode<CInterruptSource> *p = new CLinkedNode<CInterruptSource>;
  1186. if (p)
  1187. {
  1188. IDLDATAF flags = fRecursive ? IDLDATAF_MATCH_RECURSIVE : IDLDATAF_MATCH_IMMEDIATE;
  1189. if (p->that.Init(pidl, fRecursive)
  1190. && _listInterrupts.Insert(p))
  1191. {
  1192. if (SUCCEEDED(_ptreeInterrupts->AddData(flags, p->that.pidl, (INT_PTR)&p->that)))
  1193. {
  1194. return &p->that;
  1195. }
  1196. else
  1197. {
  1198. _listInterrupts.Remove(p);
  1199. delete p;
  1200. }
  1201. }
  1202. else
  1203. delete p;
  1204. }
  1205. return NULL;
  1206. }
  1207. BOOL CChangeNotify::AddInterruptSource(LPCITEMIDLIST pidlClient, BOOL fRecursive)
  1208. {
  1209. if (_InitTree(&_ptreeInterrupts))
  1210. {
  1211. LPITEMIDLIST pidl = _ILCloneInterruptID(pidlClient);
  1212. if (pidl)
  1213. {
  1214. CInterruptSource *pintc = NULL;
  1215. if (FAILED(_ptreeInterrupts->MatchOne(IDLDATAF_MATCH_EXACT, pidl, (INT_PTR*)&pintc, NULL)))
  1216. {
  1217. pintc = _InsertInterruptSource(pidl, fRecursive);
  1218. }
  1219. ILFree(pidl);
  1220. if (pintc)
  1221. {
  1222. pintc->cClients++;
  1223. return TRUE;
  1224. }
  1225. }
  1226. }
  1227. return FALSE;
  1228. }
  1229. void CChangeNotify::ReleaseInterruptSource(LPCITEMIDLIST pidlClient)
  1230. {
  1231. if (_ptreeInterrupts)
  1232. {
  1233. LPITEMIDLIST pidl = _ILCloneInterruptID(pidlClient);
  1234. if (pidl)
  1235. {
  1236. CInterruptSource *pintc;
  1237. if (SUCCEEDED(_ptreeInterrupts->MatchOne(IDLDATAF_MATCH_EXACT, pidl, (INT_PTR*)&pintc, NULL)))
  1238. {
  1239. if (--(pintc->cClients) == 0)
  1240. {
  1241. // if RemoveData fails, we have to leak the client so the tree doesnt point to freed memory.
  1242. if (SUCCEEDED(_ptreeInterrupts->RemoveData(pidl, (INT_PTR)pintc)))
  1243. {
  1244. CLinkedWalk<CInterruptSource> lw(&_listInterrupts);
  1245. while (lw.Step())
  1246. {
  1247. if (lw.That() == pintc)
  1248. {
  1249. lw.Delete();
  1250. break;
  1251. }
  1252. }
  1253. }
  1254. }
  1255. }
  1256. ILFree(pidl);
  1257. }
  1258. }
  1259. }
  1260. void CChangeNotify::_ActivateAliases(LPCITEMIDLIST pidl, BOOL fActivate)
  1261. {
  1262. if (_ptreeAliases)
  1263. {
  1264. CIDLMatchMany *pmany;
  1265. if (SUCCEEDED(_ptreeAliases->MatchMany(IDLDATAF_MATCH_RECURSIVE, pidl, &pmany)))
  1266. {
  1267. CAnyAlias *paa;
  1268. while (S_OK == pmany->Next((INT_PTR *)&paa, NULL))
  1269. {
  1270. paa->Activate(fActivate);
  1271. }
  1272. delete pmany;
  1273. }
  1274. }
  1275. }
  1276. ULONG CChangeNotify::_RegisterClient(HWND hwnd, int fSources, LONG fEvents, UINT wMsg, SHChangeNotifyEntry *pfsne)
  1277. {
  1278. ULONG ulRet = 0;
  1279. CLinkedNode<CRegisteredClient> *p = new CLinkedNode<CRegisteredClient>;
  1280. if (p)
  1281. {
  1282. if (p->that.Init(hwnd, fSources, fEvents, wMsg, pfsne))
  1283. {
  1284. IDLDATAF flags = IDLDATAF_MATCH_IMMEDIATE;
  1285. if (!pfsne->pidl || pfsne->fRecursive)
  1286. flags = IDLDATAF_MATCH_RECURSIVE;
  1287. if (_listClients.Insert(p)
  1288. && AddClient( flags,
  1289. pfsne->pidl,
  1290. &(p->that._fInterrupt),
  1291. pfsne->fRecursive && (fSources & SHCNRF_RecursiveInterrupt),
  1292. SAFECAST(&p->that, CCollapsingClient *)))
  1293. {
  1294. #ifdef DEBUG
  1295. TCHAR szName[MAX_PATH];
  1296. SHGetNameAndFlags(p->that._pidl, 0, szName, ARRAYSIZE(szName), NULL);
  1297. TraceMsg(TF_SHELLCHANGENOTIFY, "SCN::RegCli() added %s [0x%X] id = %d", szName, p, p->that._ulID);
  1298. #endif
  1299. _ActivateAliases(pfsne->pidl, TRUE);
  1300. ulRet = p->that._ulID;
  1301. }
  1302. }
  1303. if (!ulRet)
  1304. {
  1305. _listClients.Remove(p);
  1306. delete p;
  1307. }
  1308. }
  1309. return ulRet;
  1310. }
  1311. BOOL CChangeNotify::_InitTree(CIDLTree**pptree)
  1312. {
  1313. if (!*pptree)
  1314. {
  1315. CIDLTree::Create(pptree);
  1316. }
  1317. return *pptree != NULL;
  1318. }
  1319. CNotifyEvent *CChangeNotify::GetEvent(LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime, UINT uEventFlags)
  1320. {
  1321. return CNotifyEvent::Create(lEvent, pidl, pidlExtra, dwEventTime, uEventFlags);
  1322. }
  1323. BOOL CChangeNotify::_DeregisterClient(CRegisteredClient *pclient)
  1324. {
  1325. TraceMsg(TF_SHELLCHANGENOTIFY, "SCN::RegCli() removing [0x%X] id = %d", pclient, pclient->_ulID);
  1326. if (SUCCEEDED(RemoveClient(pclient->_pidl, pclient->_fInterrupt, SAFECAST(pclient, CCollapsingClient *))))
  1327. {
  1328. _ActivateAliases(pclient->_pidl, FALSE);
  1329. return TRUE;
  1330. }
  1331. return FALSE;
  1332. }
  1333. BOOL CChangeNotify::_DeregisterClientByID(ULONG ulID)
  1334. {
  1335. BOOL fRet = FALSE;
  1336. CLinkedWalk <CRegisteredClient> lw(&_listClients);
  1337. while (lw.Step())
  1338. {
  1339. if (lw.That()->_ulID == ulID)
  1340. {
  1341. // if we are flushing,
  1342. // then this is coming in while
  1343. // we are in SendMessageTimeout()
  1344. if (!_cFlushing)
  1345. {
  1346. fRet = _DeregisterClient(lw.That());
  1347. if (fRet)
  1348. {
  1349. lw.Delete();
  1350. }
  1351. }
  1352. else
  1353. lw.That()->_fDeadClient = TRUE;
  1354. break;
  1355. }
  1356. }
  1357. return fRet;
  1358. }
  1359. BOOL CChangeNotify::_DeregisterClientsByWindow(HWND hwnd)
  1360. {
  1361. BOOL fRet = FALSE;
  1362. CLinkedWalk <CRegisteredClient> lw(&_listClients);
  1363. while (lw.Step())
  1364. {
  1365. if (lw.That()->_hwnd == hwnd)
  1366. {
  1367. // if we are flushing,
  1368. // then this is coming in while
  1369. // we are in SendMessageTimeout()
  1370. if (!_cFlushing)
  1371. {
  1372. fRet = _DeregisterClient(lw.That());
  1373. if (fRet)
  1374. {
  1375. lw.Delete();
  1376. }
  1377. }
  1378. else
  1379. lw.That()->_fDeadClient = TRUE;
  1380. }
  1381. }
  1382. return fRet;
  1383. }
  1384. void CChangeNotify::_AddGlobalEvent(CNotifyEvent *pne)
  1385. {
  1386. CLinkedWalk <CRegisteredClient> lw(&_listClients);
  1387. while (lw.Step())
  1388. {
  1389. lw.That()->Notify(pne, FALSE);
  1390. }
  1391. // this is the notify we get when a drive mapping is deleted
  1392. // when this happens we need to kill the aliases to that drive
  1393. if ((pne->lEvent == SHCNE_DRIVEREMOVED) && !(pne->uEventFlags & SHCNF_TRANSLATEDALIAS))
  1394. {
  1395. CLinkedWalk<CAnyAlias> lw(&_listAliases);
  1396. while (lw.Step())
  1397. {
  1398. lw.That()->Notify(pne, FALSE);
  1399. }
  1400. }
  1401. }
  1402. void CChangeNotify::_MatchAndNotify(LPCITEMIDLIST pidl, CNotifyEvent *pne, BOOL fFromExtra)
  1403. {
  1404. if (_ptreeClients)
  1405. {
  1406. CIDLMatchMany *pmany;
  1407. if (SUCCEEDED(_ptreeClients->MatchMany(IDLDATAF_MATCH_RECURSIVE, pidl, &pmany)))
  1408. {
  1409. CCollapsingClient *pclient;
  1410. while (S_OK == pmany->Next((INT_PTR *)&pclient, NULL))
  1411. {
  1412. pclient->Notify(pne, fFromExtra);
  1413. }
  1414. delete pmany;
  1415. }
  1416. }
  1417. }
  1418. BOOL CChangeNotify::_AddToClients(LONG lEvent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime, UINT uEventFlags)
  1419. {
  1420. BOOL bOnlyUpdateDirs = TRUE;
  1421. CNotifyEvent *pne = GetEvent(lEvent, pidl, pidlExtra, dwEventTime, uEventFlags);
  1422. if (pne)
  1423. {
  1424. if (lEvent & SHCNE_GLOBALEVENTS)
  1425. {
  1426. _AddGlobalEvent(pne);
  1427. }
  1428. else
  1429. {
  1430. _MatchAndNotify(pidl, pne, FALSE);
  1431. if (pidlExtra)
  1432. _MatchAndNotify(pidlExtra, pne, TRUE);
  1433. }
  1434. pne->Release();
  1435. }
  1436. return bOnlyUpdateDirs;
  1437. }
  1438. BOOL CChangeNotify::_HandleMessages(void)
  1439. {
  1440. MSG msg;
  1441. // There was some message put in our queue, so we need to dispose
  1442. // of it
  1443. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  1444. {
  1445. if (msg.hwnd)
  1446. {
  1447. TranslateMessage(&msg);
  1448. DispatchMessage(&msg);
  1449. }
  1450. else
  1451. {
  1452. switch (msg.message)
  1453. {
  1454. case SCNM_TERMINATE:
  1455. DestroyWindow(g_hwndSCN);
  1456. g_hwndSCN = NULL;
  1457. return TRUE;
  1458. break;
  1459. default:
  1460. TraceMsg(TF_SHELLCHANGENOTIFY, "SCN thread proc: eating unknown message %#lx", msg.message);
  1461. break;
  1462. }
  1463. }
  1464. }
  1465. return FALSE;
  1466. }
  1467. CInterruptSource::~CInterruptSource()
  1468. {
  1469. _Reset(TRUE);
  1470. ILFree(pidl);
  1471. }
  1472. BOOL CInterruptSource::Init(LPCITEMIDLIST pidl, BOOL fRecursive)
  1473. {
  1474. this->pidl = ILClone(pidl);
  1475. _fRecursive = fRecursive;
  1476. return (this->pidl != NULL);
  1477. }
  1478. BOOL CInterruptSource::Flush(void)
  1479. {
  1480. if (FS_SIGNAL == _ssSignal)
  1481. {
  1482. g_pscn->NotifyEvent(SHCNE_UPDATEDIR | SHCNE_INTERRUPT, SHCNF_IDLIST, pidl, NULL, GetTickCount());
  1483. }
  1484. _ssSignal = NO_SIGNAL;
  1485. return TRUE;
  1486. }
  1487. void CInterruptSource::_Reset(BOOL fDeviceNotify)
  1488. {
  1489. if (_hEvent && _hEvent != INVALID_HANDLE_VALUE)
  1490. {
  1491. FindCloseChangeNotification(_hEvent);
  1492. _hEvent = NULL;
  1493. }
  1494. if (fDeviceNotify && _hPNP)
  1495. {
  1496. UnregisterDeviceNotification(_hPNP);
  1497. _hPNP = NULL;
  1498. }
  1499. }
  1500. void CInterruptSource::Reset(BOOL fSignal)
  1501. {
  1502. if (fSignal) // file system event
  1503. {
  1504. switch(_ssSignal)
  1505. {
  1506. case NO_SIGNAL: _ssSignal = FS_SIGNAL; break;
  1507. case SH_SIGNAL: _ssSignal = NO_SIGNAL; break;
  1508. }
  1509. if (!FindNextChangeNotification(_hEvent))
  1510. {
  1511. _Reset(FALSE);
  1512. // when we fail, we dont want
  1513. // to retry. which we will do
  1514. // in the case of _hEvent = NULL;
  1515. _hEvent = INVALID_HANDLE_VALUE;
  1516. }
  1517. }
  1518. else // shell event
  1519. {
  1520. switch(_ssSignal)
  1521. {
  1522. case NO_SIGNAL: _ssSignal = SH_SIGNAL; break;
  1523. case FS_SIGNAL: _ssSignal = NO_SIGNAL; break;
  1524. }
  1525. }
  1526. }
  1527. #define FFCN_INTERESTING_EVENTS (FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES)
  1528. BOOL CInterruptSource::GetEvent(HANDLE *phEvent)
  1529. {
  1530. if (_cSuspend == 0 && cClients)
  1531. {
  1532. // create this here so that it will be owned by our global thread
  1533. if (!_hEvent)
  1534. {
  1535. TCHAR szPath[MAX_PATH];
  1536. if (SHGetPathFromIDList(pidl, szPath))
  1537. {
  1538. _hEvent = FindFirstChangeNotification(szPath, _fRecursive, FFCN_INTERESTING_EVENTS);
  1539. if (_hEvent != INVALID_HANDLE_VALUE)
  1540. {
  1541. // PERF optimization alert: RegisterDeviceNotification is being used for removable drives
  1542. // to ensure that the FindFirstChangeNotification call will not prevent the disk
  1543. // from being ejected or dismounted. However, RegisterDeviceNotification is a very expensive
  1544. // call to make at startup as it brings a bunch of DLLs in the address space. Besides,
  1545. // we really don't need to call this for the system drive since it needs to remain
  1546. // mounted at all times. - FabriceD
  1547. // Exclude FIXED drives too
  1548. int iDrive = PathGetDriveNumber(szPath);
  1549. int nType = DRIVE_UNKNOWN;
  1550. if (iDrive != -1)
  1551. {
  1552. nType = DriveType(iDrive);
  1553. }
  1554. // PERF: Exclude the system drive from the RegisterDeviceNotification calls.
  1555. TCHAR chDrive = *szPath;
  1556. if ((!GetEnvironmentVariable(TEXT("SystemDrive"), szPath, ARRAYSIZE(szPath)) || *szPath != chDrive) &&
  1557. nType != DRIVE_FIXED)
  1558. {
  1559. // DO WE NEED TO UnRegister() first?
  1560. DEV_BROADCAST_HANDLE dbh;
  1561. ZeroMemory(&dbh, sizeof(dbh));
  1562. dbh.dbch_size = sizeof(dbh);
  1563. dbh.dbch_devicetype = DBT_DEVTYP_HANDLE;
  1564. dbh.dbch_handle = _hEvent;
  1565. _hPNP = RegisterDeviceNotification(g_hwndSCN, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE);
  1566. }
  1567. }
  1568. }
  1569. else
  1570. _hEvent = INVALID_HANDLE_VALUE;
  1571. }
  1572. if (_hEvent != INVALID_HANDLE_VALUE)
  1573. {
  1574. *phEvent = _hEvent;
  1575. return TRUE;
  1576. }
  1577. }
  1578. return FALSE;
  1579. }
  1580. void CChangeNotify::_SignalInterrupt(HANDLE hEvent)
  1581. {
  1582. CLinkedWalk<CInterruptSource> lw(&_listInterrupts);
  1583. while (lw.Step())
  1584. {
  1585. // searching for valid clients
  1586. HANDLE h;
  1587. if (lw.That()->GetEvent(&h) && h == hEvent)
  1588. {
  1589. g_pscn->SetFlush(FLUSH_INTERRUPT);
  1590. lw.That()->Reset(TRUE);
  1591. break;
  1592. }
  1593. }
  1594. }
  1595. DWORD CChangeNotify::_GetInterruptEvents(HANDLE *ahEvents, DWORD cEventsSize)
  1596. {
  1597. DWORD cEvents = 0;
  1598. CLinkedWalk<CInterruptSource> lw(&_listInterrupts);
  1599. while (cEvents < cEventsSize && lw.Step())
  1600. {
  1601. // go through and find all the valid
  1602. // clients that need waiting on
  1603. if (lw.That()->GetEvent(&ahEvents[cEvents]))
  1604. {
  1605. // lw.That()->Reset(FALSE);
  1606. cEvents++;
  1607. }
  1608. }
  1609. return cEvents;
  1610. }
  1611. void CChangeNotify::_MessagePump(void)
  1612. {
  1613. DWORD cFails = 0;
  1614. while (TRUE)
  1615. {
  1616. HANDLE ahEvents[MAXIMUM_WAIT_OBJECTS - 1];
  1617. DWORD cEvents = _GetInterruptEvents(ahEvents, ARRAYSIZE(ahEvents));
  1618. // maybe cache the events?
  1619. // NEED to handle pending Events with a Timer
  1620. DWORD dwWaitResult = MsgWaitForMultipleObjectsEx(cEvents, ahEvents,
  1621. INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);
  1622. if (dwWaitResult != (DWORD)-1)
  1623. {
  1624. if (dwWaitResult != WAIT_IO_COMPLETION)
  1625. {
  1626. dwWaitResult -= WAIT_OBJECT_0;
  1627. if (dwWaitResult == cEvents)
  1628. {
  1629. // there is a message
  1630. if (_HandleMessages())
  1631. break;
  1632. }
  1633. else if (dwWaitResult < cEvents)
  1634. {
  1635. _SignalInterrupt(ahEvents[dwWaitResult]);
  1636. }
  1637. }
  1638. cFails = 0;
  1639. }
  1640. else
  1641. {
  1642. // there was some kind of error
  1643. TraceMsg(TF_ERROR, "SCNotify WaitForMulti() failed with %d", GetLastError());
  1644. // if MWFM() fails over and over, we give up.
  1645. if (++cFails > 10)
  1646. {
  1647. TraceMsg(TF_ERROR, "SCNotify WaitForMulti() bailing out");
  1648. break;
  1649. }
  1650. }
  1651. }
  1652. }
  1653. void SCNUninitialize(void)
  1654. {
  1655. if (g_pscn)
  1656. {
  1657. if (IsWindow(g_hwndSCN))
  1658. DestroyWindow(g_hwndSCN);
  1659. g_hwndSCN = NULL;
  1660. delete g_pscn;
  1661. g_pscn = NULL;
  1662. }
  1663. }
  1664. // the real thread proc, runs after CChangeNotify::ThreadStartUp runs sync
  1665. DWORD WINAPI CChangeNotify::ThreadProc(void *pv)
  1666. {
  1667. if (g_pscn)
  1668. {
  1669. CMountPoint::RegisterForHardwareNotifications();
  1670. #ifdef RESTARTSCN
  1671. __try
  1672. #endif
  1673. {
  1674. g_pscn->_MessagePump();
  1675. }
  1676. #ifdef RESTARTSCN
  1677. __except (EXCEPTION_EXECUTE_HANDLER)
  1678. {
  1679. ASSERT(FALSE);
  1680. }
  1681. #endif
  1682. }
  1683. SCNUninitialize();
  1684. return 0;
  1685. }
  1686. BOOL CChangeNotify::_OnChangeRegistration(HANDLE hChangeRegistration, DWORD dwProcId)
  1687. {
  1688. BOOL fResult = FALSE;
  1689. CHANGEREGISTER *pcr = (CHANGEREGISTER *)SHLockSharedEx(hChangeRegistration, dwProcId, TRUE);
  1690. if (pcr)
  1691. {
  1692. SHChangeNotifyEntry fsne;
  1693. fsne.pidl = NULL;
  1694. fsne.fRecursive = pcr->fRecursive;
  1695. if (pcr->uidlRegister)
  1696. fsne.pidl = _ILSkip(pcr, pcr->uidlRegister);
  1697. pcr->ulID = _RegisterClient((HWND)ULongToPtr(pcr->ulHwnd), pcr->fSources,
  1698. pcr->lEvents, pcr->uMsg, &fsne);
  1699. fResult = TRUE;
  1700. SHUnlockShared(pcr);
  1701. }
  1702. return fResult;
  1703. }
  1704. void CChangeNotify::_ResetRelatedInterrupts(LPCITEMIDLIST pidl)
  1705. {
  1706. if (_ptreeInterrupts)
  1707. {
  1708. // we need to match whoever listens on this pidl
  1709. CIDLMatchMany *pmany;
  1710. if (SUCCEEDED(_ptreeInterrupts->MatchMany(IDLDATAF_MATCH_RECURSIVE, pidl, &pmany)))
  1711. {
  1712. CInterruptSource *pintc;
  1713. while (S_OK == pmany->Next((INT_PTR *)&pintc, NULL))
  1714. {
  1715. // we might need WFSO(pintc->GetEvent()) here first
  1716. // if this is already signaled,
  1717. // we need to unsignal
  1718. pintc->Reset(FALSE);
  1719. }
  1720. delete pmany;
  1721. }
  1722. }
  1723. }
  1724. void CChangeNotify::_FlushInterrupts(void)
  1725. {
  1726. CLinkedWalk<CInterruptSource> lw(&_listInterrupts);
  1727. while (lw.Step())
  1728. {
  1729. lw.That()->Flush();
  1730. }
  1731. }
  1732. #define CALLBACK_TIMEOUT 30000 // 30 seconds
  1733. void CChangeNotify::_WaitForCallbacks(void)
  1734. {
  1735. while (_cCallbacks)
  1736. {
  1737. MSG msg;
  1738. DWORD dwWaitResult = MsgWaitForMultipleObjects(1, &_hCallbackEvent, FALSE,
  1739. CALLBACK_TIMEOUT, QS_SENDMESSAGE);
  1740. TraceMsg(TF_SHELLCHANGENOTIFY, "FSN_WaitForCallbacks returned 0x%X", dwWaitResult);
  1741. if (dwWaitResult == WAIT_OBJECT_0) break; // Event completed
  1742. if (dwWaitResult == WAIT_TIMEOUT) break; // Ran out of time
  1743. if (dwWaitResult == WAIT_OBJECT_0+1)
  1744. {
  1745. //
  1746. // Some message came in, reset message event, deliver callbacks, etc.
  1747. //
  1748. PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); // we need to do this to flush callbacks
  1749. }
  1750. }
  1751. if (_hCallbackEvent)
  1752. {
  1753. CloseHandle(_hCallbackEvent);
  1754. _hCallbackEvent = NULL;
  1755. }
  1756. }
  1757. void CChangeNotify::SetFlush(int idt)
  1758. {
  1759. switch (idt)
  1760. {
  1761. case FLUSH_OVERFLOW:
  1762. case FLUSH_SOFT:
  1763. SetTimer(g_hwndSCN, IDT_SCN_FLUSHEVENTS, 500, NULL);
  1764. break;
  1765. case FLUSH_HARD:
  1766. PostMessage(g_hwndSCN, SCNM_FLUSHEVENTS, 0, 0);
  1767. break;
  1768. case FLUSH_INTERRUPT:
  1769. SetTimer(g_hwndSCN, IDT_SCN_FLUSHEVENTS, 1000, NULL);
  1770. break;
  1771. }
  1772. }
  1773. void CChangeNotify::_Flush(BOOL fShouldWait)
  1774. {
  1775. _cFlushing++;
  1776. KillTimer(g_hwndSCN, IDT_SCN_FLUSHEVENTS);
  1777. // flush any pending interrupt events
  1778. _FlushInterrupts();
  1779. int iNumLoops = 0;
  1780. BOOL fProcessedAny;
  1781. do
  1782. {
  1783. fProcessedAny = FALSE;
  1784. CLinkedWalk<CAnyAlias> lwAliases(&_listAliases);
  1785. while (lwAliases.Step())
  1786. {
  1787. if (lwAliases.That()->Flush(TRUE))
  1788. {
  1789. fProcessedAny = TRUE;
  1790. }
  1791. }
  1792. iNumLoops++;
  1793. // in free builds bail out if there's a loop so we don't spin the thread.
  1794. // but this is pretty bad so assert anyway (the most people would usually have
  1795. // is 2 -- a folder shortcut to something on the desktop / mydocs)
  1796. ASSERTMSG(iNumLoops < 10, "we're in an alias loop, we're screwed");
  1797. } while (fProcessedAny && (iNumLoops < 10));
  1798. CLinkedWalk<CRegisteredClient> lwRegistered(&_listClients);
  1799. while (lwRegistered.Step())
  1800. {
  1801. lwRegistered.That()->Flush(fShouldWait);
  1802. }
  1803. if (fShouldWait)
  1804. {
  1805. // now wait for all the callbacks to empty out
  1806. _WaitForCallbacks();
  1807. }
  1808. _cFlushing--;
  1809. // wait until we have 10 seconds of free time
  1810. SetTimer(g_hwndSCN, IDT_SCN_FRESHENTREES, 10000, NULL);
  1811. }
  1812. BOOL IsILShared(LPCITEMIDLIST pidl, BOOL fUpdateCache)
  1813. {
  1814. TCHAR szTemp[MAXPATHLEN];
  1815. SHGetPathFromIDList(pidl, szTemp);
  1816. return IsShared(szTemp, fUpdateCache);
  1817. }
  1818. void CChangeNotify::NotifyEvent(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime)
  1819. {
  1820. if (!(uFlags & SHCNF_ONLYNOTIFYINTERNALS) && lEvent)
  1821. {
  1822. /// now do the actual generating of the event
  1823. if (lEvent & (SHCNE_NETSHARE | SHCNE_NETUNSHARE))
  1824. {
  1825. // Update the cache.
  1826. IsILShared(pidl, TRUE);
  1827. }
  1828. _AddToClients(lEvent, pidl, pidlExtra, dwEventTime, uFlags);
  1829. // remove any shell generated events for the file system
  1830. if ((lEvent & SHCNE_DISKEVENTS) &&
  1831. !(lEvent & (SHCNE_INTERRUPT | SHCNE_UPDATEDIR)))
  1832. {
  1833. _ResetRelatedInterrupts(pidl);
  1834. if (pidlExtra)
  1835. _ResetRelatedInterrupts(pidlExtra);
  1836. }
  1837. }
  1838. // note make sure the internal events go first.
  1839. if (lEvent)
  1840. NotifyShellInternals(lEvent, uFlags, pidl, pidlExtra, dwEventTime);
  1841. //
  1842. // then the registered events
  1843. //
  1844. if (uFlags & (SHCNF_FLUSH))
  1845. {
  1846. if (uFlags & SHCNF_FLUSHNOWAIT)
  1847. {
  1848. SetFlush(FLUSH_HARD);
  1849. }
  1850. else
  1851. _Flush(TRUE);
  1852. }
  1853. }
  1854. LRESULT CChangeNotify::_OnNotifyEvent(HANDLE hChange, DWORD dwProcId)
  1855. {
  1856. CHANGELOCK *pcl = _SHChangeNotification_Lock(hChange, dwProcId);
  1857. if (pcl)
  1858. {
  1859. NotifyEvent(pcl->pce->lEvent,
  1860. pcl->pce->uFlags,
  1861. pcl->pidlMain,
  1862. pcl->pidlExtra,
  1863. pcl->pce->dwEventTime);
  1864. SHChangeNotification_Unlock(pcl);
  1865. SHChangeNotification_Destroy(hChange, dwProcId);
  1866. }
  1867. return TRUE;
  1868. }
  1869. void CInterruptSource::Suspend(BOOL fSuspend)
  1870. {
  1871. if (fSuspend)
  1872. {
  1873. if (!_cSuspend)
  1874. _Reset(FALSE);
  1875. _cSuspend++;
  1876. }
  1877. else if (_cSuspend)
  1878. _cSuspend--;
  1879. }
  1880. BOOL CChangeNotify::_SuspendResume(BOOL fSuspend, BOOL fRecursive, LPCITEMIDLIST pidl)
  1881. {
  1882. if (_ptreeInterrupts)
  1883. {
  1884. CInterruptSource *pintc;
  1885. if (!fRecursive)
  1886. {
  1887. if (SUCCEEDED(_ptreeInterrupts->MatchOne(IDLDATAF_MATCH_EXACT, pidl, (INT_PTR*)&pintc, NULL)))
  1888. {
  1889. pintc->Suspend(fSuspend);
  1890. }
  1891. }
  1892. else
  1893. {
  1894. CIDLMatchMany *pmany;
  1895. if (SUCCEEDED(_ptreeInterrupts->MatchMany(IDLDATAF_MATCH_RECURSIVE, pidl, &pmany)))
  1896. {
  1897. while (S_OK == pmany->Next((INT_PTR *)&pintc, NULL))
  1898. {
  1899. pintc->Suspend(fSuspend);
  1900. }
  1901. delete pmany;
  1902. }
  1903. }
  1904. }
  1905. return TRUE;
  1906. }
  1907. #define SCNSUSPEND_SUSPEND 1
  1908. #define SCNSUSPEND_RECURSIVE 2
  1909. LRESULT CChangeNotify::_OnSuspendResume(HANDLE hChange, DWORD dwProcId)
  1910. {
  1911. BOOL fRet = FALSE;
  1912. CHANGELOCK *pcl = _SHChangeNotification_Lock(hChange, dwProcId);
  1913. if (pcl)
  1914. {
  1915. fRet = _SuspendResume(pcl->pce->uFlags & SCNSUSPEND_SUSPEND, pcl->pce->uFlags & SCNSUSPEND_RECURSIVE, pcl->pidlMain);
  1916. SHChangeNotification_Unlock((HANDLE)pcl);
  1917. }
  1918. return fRet;
  1919. }
  1920. BOOL CInterruptSource::SuspendDevice(BOOL fSuspend, HDEVNOTIFY hPNP)
  1921. {
  1922. BOOL fRet = FALSE;
  1923. if (hPNP)
  1924. {
  1925. if (fSuspend && _hPNP == hPNP)
  1926. {
  1927. _hSuspended = _hPNP;
  1928. Suspend(fSuspend);
  1929. _Reset(TRUE);
  1930. fRet = TRUE;
  1931. }
  1932. else if (!fSuspend && _hSuspended == hPNP)
  1933. {
  1934. _hSuspended = NULL;
  1935. Suspend(fSuspend);
  1936. fRet = TRUE;
  1937. }
  1938. }
  1939. else if (_hPNP)
  1940. {
  1941. // NULL means we are shutting down and should close all handles.
  1942. UnregisterDeviceNotification(_hPNP);
  1943. _hPNP = NULL;
  1944. }
  1945. return fRet;
  1946. }
  1947. // __HandleDevice
  1948. void CChangeNotify::_OnDeviceBroadcast(ULONG_PTR code, DEV_BROADCAST_HANDLE *pbhnd)
  1949. {
  1950. if (IsWindowVisible(GetShellWindow()) && pbhnd
  1951. && (pbhnd->dbch_devicetype == DBT_DEVTYP_HANDLE && pbhnd->dbch_hdevnotify))
  1952. {
  1953. BOOL fSuspend;
  1954. switch (code)
  1955. {
  1956. // When PnP is finished messing with the drive (either successfully
  1957. // or unsuccessfully), resume notifications on that drive.
  1958. case DBT_DEVICEREMOVECOMPLETE:
  1959. case DBT_DEVICEQUERYREMOVEFAILED:
  1960. fSuspend = FALSE;
  1961. break;
  1962. // When PnP is starting to mess with the drive, suspend notifications
  1963. // so it can do its thing
  1964. case DBT_DEVICEQUERYREMOVE:
  1965. // This will wait on another thread to exit if this hdevnotify
  1966. // was registered for a Sniffing Dialog
  1967. CSniffDrive::HandleNotif(pbhnd->dbch_hdevnotify);
  1968. fSuspend = TRUE;
  1969. break;
  1970. case DBT_CUSTOMEVENT:
  1971. if (GUID_IO_VOLUME_LOCK == pbhnd->dbch_eventguid)
  1972. {
  1973. TraceMsg(TF_MOUNTPOINT, "GUID_IO_VOLUME_LOCK: Suspending!");
  1974. fSuspend = TRUE;
  1975. }
  1976. else
  1977. {
  1978. if (GUID_IO_VOLUME_LOCK_FAILED == pbhnd->dbch_eventguid)
  1979. {
  1980. TraceMsg(TF_MOUNTPOINT, "GUID_IO_VOLUME_LOCK_FAILED: Resuming!");
  1981. fSuspend = FALSE;
  1982. }
  1983. else
  1984. {
  1985. if (GUID_IO_VOLUME_UNLOCK == pbhnd->dbch_eventguid)
  1986. {
  1987. TraceMsg(TF_MOUNTPOINT, "GUID_IO_VOLUME_UNLOCK: Resuming!");
  1988. fSuspend = FALSE;
  1989. }
  1990. }
  1991. }
  1992. break;
  1993. default:
  1994. // we dont handle anything else here
  1995. return;
  1996. }
  1997. CLinkedWalk<CInterruptSource> lw(&_listInterrupts);
  1998. while (lw.Step())
  1999. {
  2000. // returns true if found
  2001. if (lw.That()->SuspendDevice(fSuspend, pbhnd->dbch_hdevnotify))
  2002. break;
  2003. }
  2004. }
  2005. }
  2006. void CChangeNotify::_FreshenClients(void)
  2007. {
  2008. CLinkedWalk<CRegisteredClient> lw(&_listClients);
  2009. while (lw.Step())
  2010. {
  2011. if (lw.That()->_fDeadClient || !IsWindow(lw.That()->_hwnd))
  2012. {
  2013. if (_DeregisterClient(lw.That()))
  2014. {
  2015. lw.Delete();
  2016. }
  2017. }
  2018. }
  2019. }
  2020. void CChangeNotify::_FreshenUp(void)
  2021. {
  2022. ASSERT(!_cFlushing);
  2023. KillTimer(g_hwndSCN, IDT_SCN_FRESHENTREES);
  2024. if (_ptreeClients)
  2025. _ptreeClients->Freshen();
  2026. if (_ptreeInterrupts)
  2027. _ptreeInterrupts->Freshen();
  2028. _FreshenAliases();
  2029. _FreshenClients();
  2030. }
  2031. LRESULT CChangeNotify::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2032. {
  2033. LRESULT lRes = 0;
  2034. ASSERT(g_pscn);
  2035. switch (uMsg)
  2036. {
  2037. case SCNM_REGISTERCLIENT:
  2038. lRes = g_pscn->_OnChangeRegistration((HANDLE)wParam, (DWORD)lParam);
  2039. break;
  2040. case SCNM_DEREGISTERCLIENT:
  2041. lRes = g_pscn->_DeregisterClientByID((ULONG)wParam);
  2042. break;
  2043. case SCNM_DEREGISTERWINDOW:
  2044. lRes = g_pscn->_DeregisterClientsByWindow((HWND)wParam);
  2045. break;
  2046. case SCNM_NOTIFYEVENT:
  2047. lRes = g_pscn->_OnNotifyEvent((HANDLE)wParam, (DWORD)lParam);
  2048. break;
  2049. case SCNM_SUSPENDRESUME:
  2050. lRes = g_pscn->_OnSuspendResume((HANDLE)wParam, (DWORD)lParam);
  2051. break;
  2052. case WM_TIMER:
  2053. if (wParam == IDT_SCN_FRESHENTREES)
  2054. {
  2055. g_pscn->_FreshenUp();
  2056. break;
  2057. }
  2058. // Fall through to SCNM_FLUSHEVENTS
  2059. case SCNM_FLUSHEVENTS:
  2060. g_pscn->_Flush(FALSE);
  2061. break;
  2062. case SCNM_AUTOPLAYDRIVE:
  2063. CMountPoint::DoAutorunPrompt(wParam);
  2064. break;
  2065. case WM_DEVICECHANGE:
  2066. g_pscn->_OnDeviceBroadcast(wParam, (DEV_BROADCAST_HANDLE *)lParam);
  2067. break;
  2068. default:
  2069. lRes = DefWindowProc(hwnd, uMsg, wParam, lParam);
  2070. break;
  2071. }
  2072. return lRes;
  2073. }
  2074. // thread setup routine, executed before SHCreateThread() returns
  2075. DWORD WINAPI CChangeNotify::ThreadStartUp(void *pv)
  2076. {
  2077. g_pscn = new CChangeNotify();
  2078. if (g_pscn)
  2079. {
  2080. g_hwndSCN = SHCreateWorkerWindow(CChangeNotify::WndProc, NULL, 0, 0, NULL, g_pscn);
  2081. CSniffDrive::InitNotifyWindow(g_hwndSCN);
  2082. InitAliasFolderTable();
  2083. }
  2084. return 0;
  2085. }
  2086. // now we create the window
  2087. BOOL SCNInitialize()
  2088. {
  2089. EnterCriticalSection(&g_csSCN);
  2090. if (!IsWindow(g_hwndSCN))
  2091. {
  2092. SHCreateThread(CChangeNotify::ThreadProc, NULL, CTF_COINIT, CChangeNotify::ThreadStartUp);
  2093. }
  2094. LeaveCriticalSection(&g_csSCN);
  2095. return g_hwndSCN ? TRUE : FALSE; // ThreadStartUp is executed sync
  2096. }
  2097. BOOL _IsImpersonating()
  2098. {
  2099. HANDLE hToken;
  2100. if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken))
  2101. {
  2102. CloseHandle(hToken);
  2103. return TRUE;
  2104. }
  2105. return FALSE;
  2106. }
  2107. STDAPI_(HWND) _SCNGetWindow(BOOL fUseDesktop, BOOL fNeedsFallback)
  2108. {
  2109. // if explorer is trashed
  2110. // then this hwnd can go bad
  2111. // get a new copy from the desktop
  2112. if (!g_hwndSCN || !IsWindow(g_hwndSCN))
  2113. {
  2114. HWND hwndDesktop = fUseDesktop ? GetShellWindow() : NULL;
  2115. if (hwndDesktop)
  2116. {
  2117. HWND hwndSCN = (HWND) SendMessage(hwndDesktop, CWM_GETSCNWINDOW, 0, 0);
  2118. if (_IsImpersonating())
  2119. return hwndSCN;
  2120. else
  2121. g_hwndSCN = hwndSCN;
  2122. }
  2123. else if (fNeedsFallback && SHIsCurrentThreadInteractive())
  2124. {
  2125. // there is no desktop.
  2126. // so we create a private desktop
  2127. // this will create the thread and window
  2128. // and set
  2129. SCNInitialize();
  2130. }
  2131. }
  2132. return g_hwndSCN;
  2133. }
  2134. STDAPI_(HWND) SCNGetWindow(BOOL fUseDesktop)
  2135. {
  2136. return _SCNGetWindow(fUseDesktop, TRUE);
  2137. }
  2138. HANDLE SHChangeRegistration_Create(ULONG ulID,
  2139. HWND hwnd, UINT uMsg,
  2140. DWORD fSources, LONG lEvents,
  2141. BOOL fRecursive, LPCITEMIDLIST pidl,
  2142. DWORD dwProcId)
  2143. {
  2144. UINT uidlSize = ILGetSize(pidl);
  2145. HANDLE hReg = SHAllocShared(NULL, sizeof(CHANGEREGISTER) + uidlSize, dwProcId);
  2146. if (hReg)
  2147. {
  2148. CHANGEREGISTER *pcr = (CHANGEREGISTER *) SHLockSharedEx(hReg, dwProcId, TRUE);
  2149. if (pcr)
  2150. {
  2151. pcr->dwSig = CHANGEREGISTER_SIG;
  2152. pcr->ulID = ulID;
  2153. pcr->ulHwnd = PtrToUlong(hwnd);
  2154. pcr->uMsg = uMsg;
  2155. pcr->fSources = fSources;
  2156. pcr->lEvents = lEvents;
  2157. pcr->fRecursive = fRecursive;
  2158. pcr->uidlRegister = 0;
  2159. if (pidl)
  2160. {
  2161. pcr->uidlRegister = sizeof(CHANGEREGISTER);
  2162. memcpy((pcr + 1), pidl, uidlSize);
  2163. }
  2164. SHUnlockShared(pcr);
  2165. }
  2166. else
  2167. {
  2168. SHFreeShared(hReg, dwProcId);
  2169. hReg = NULL;
  2170. }
  2171. }
  2172. return hReg;
  2173. }
  2174. typedef struct
  2175. {
  2176. HWND hwnd;
  2177. UINT wMsg;
  2178. } NOTIFY_PROXY_DATA;
  2179. #define WM_CHANGENOTIFYMSG WM_USER + 1
  2180. LRESULT CALLBACK _HiddenNotifyWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
  2181. {
  2182. LRESULT lRes = FALSE;
  2183. NOTIFY_PROXY_DATA *pData = (NOTIFY_PROXY_DATA *) GetWindowLongPtr( hWnd, 0 );
  2184. switch (iMessage)
  2185. {
  2186. case WM_NCDESTROY:
  2187. ASSERT(pData != NULL );
  2188. // clear it so it won't be in use....
  2189. SetWindowLongPtr( hWnd, 0, (LONG_PTR)NULL );
  2190. // free the memory ...
  2191. LocalFree( pData );
  2192. break;
  2193. case WM_CHANGENOTIFYMSG :
  2194. if (pData)
  2195. {
  2196. // lock and break the info structure ....
  2197. LPITEMIDLIST *ppidl;
  2198. LONG lEvent;
  2199. HANDLE hLock = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
  2200. if (hLock)
  2201. {
  2202. // pass on to the old style client. ...
  2203. lRes = SendMessage( pData->hwnd, pData->wMsg, (WPARAM) ppidl, (LPARAM) lEvent );
  2204. // new notifications ......
  2205. SHChangeNotification_Unlock(hLock);
  2206. }
  2207. }
  2208. break;
  2209. default:
  2210. lRes = DefWindowProc(hWnd, iMessage, wParam, lParam);
  2211. break;
  2212. }
  2213. return lRes;
  2214. }
  2215. HWND _CreateProxyWindow(HWND hwnd, UINT wMsg)
  2216. {
  2217. HWND hwndRet = NULL;
  2218. // This is an old style notification, we need to create a hidden
  2219. // proxy type of window to properly handle the messages...
  2220. NOTIFY_PROXY_DATA *pnpd = (NOTIFY_PROXY_DATA *)LocalAlloc(LPTR, sizeof(*pnpd));
  2221. if (pnpd)
  2222. {
  2223. pnpd->hwnd = hwnd;
  2224. pnpd->wMsg = wMsg;
  2225. hwndRet = SHCreateWorkerWindow(_HiddenNotifyWndProc, NULL, 0, 0, NULL, pnpd);
  2226. if (!hwndRet)
  2227. LocalFree(pnpd);
  2228. }
  2229. return hwndRet;
  2230. }
  2231. //--------------------------------------------------------------------------
  2232. //
  2233. // Returns a positive integer registration ID, or 0 if out of memory or if
  2234. // invalid parameters were passed in.
  2235. //
  2236. // If the hwnd is != NULL we do a PostMessage(hwnd, wMsg, ...) when a
  2237. // relevant FS event takes place, otherwise if fsncb is != NULL we call it.
  2238. //
  2239. STDAPI_(ULONG) SHChangeNotifyRegister(HWND hwnd,
  2240. int fSources, LONG fEvents,
  2241. UINT wMsg, int cEntries,
  2242. SHChangeNotifyEntry *pfsne)
  2243. {
  2244. ULONG ulID = 0;
  2245. BOOL fResult = FALSE;
  2246. HWND hwndSCN = SCNGetWindow(TRUE);
  2247. if (hwndSCN)
  2248. {
  2249. if (!(fSources & SHCNRF_NewDelivery))
  2250. {
  2251. // Now setup to use the proxy window instead
  2252. hwnd = _CreateProxyWindow(hwnd, wMsg);
  2253. wMsg = WM_CHANGENOTIFYMSG;
  2254. }
  2255. if ((fSources & SHCNRF_RecursiveInterrupt) && !(fSources & SHCNRF_InterruptLevel))
  2256. {
  2257. // bad caller, they asked for recursive interrupt events, but not interrupt events
  2258. ASSERTMSG(FALSE, "SHChangeNotifyRegister: caller passed SHCNRF_RecursiveInterrupt but NOT SHCNRF_InterruptLevel !!");
  2259. // clear the flag
  2260. fSources = fSources & (~SHCNRF_RecursiveInterrupt);
  2261. }
  2262. // This same assert is CRegisteredClient::Init, caled by SCNM_REGISTERCLIENT message below
  2263. ASSERT(fSources & (SHCNRF_InterruptLevel | SHCNRF_ShellLevel));
  2264. // NOTE - if we have more than one registration entry here,
  2265. // we only support Deregister'ing the last one
  2266. for (int i = 0; i < cEntries; i++)
  2267. {
  2268. DWORD dwProcId;
  2269. GetWindowThreadProcessId(hwndSCN, &dwProcId);
  2270. HANDLE hChangeRegistration = SHChangeRegistration_Create(
  2271. ulID, hwnd, wMsg,
  2272. fSources, fEvents,
  2273. pfsne[i].fRecursive, pfsne[i].pidl,
  2274. dwProcId);
  2275. if (hChangeRegistration)
  2276. {
  2277. CHANGEREGISTER * pcr;
  2278. //
  2279. // Transmit the change regsitration
  2280. //
  2281. SendMessage(hwndSCN, SCNM_REGISTERCLIENT,
  2282. (WPARAM)hChangeRegistration, (LPARAM)dwProcId);
  2283. //
  2284. // Now get back the ulID value, for further registrations and
  2285. // for returning to the calling function...
  2286. //
  2287. pcr = (CHANGEREGISTER *)SHLockSharedEx(hChangeRegistration, dwProcId, FALSE);
  2288. if (pcr)
  2289. {
  2290. ulID = pcr->ulID;
  2291. SHUnlockShared(pcr);
  2292. }
  2293. else
  2294. {
  2295. ASSERT(0 == ulID); // Error condition initialized above
  2296. }
  2297. SHFreeShared(hChangeRegistration, dwProcId);
  2298. }
  2299. if ((ulID == 0) && !(fSources & SHCNRF_NewDelivery))
  2300. {
  2301. // this is our proxy window
  2302. DestroyWindow(hwnd);
  2303. break;
  2304. }
  2305. }
  2306. }
  2307. return ulID;
  2308. }
  2309. //--------------------------------------------------------------------------
  2310. //
  2311. // Returns TRUE if we found and removed the specified Client, otherwise
  2312. // returns FALSE.
  2313. //
  2314. STDAPI_(BOOL) SHChangeNotifyDeregister(ULONG ulID)
  2315. {
  2316. BOOL fResult = FALSE;
  2317. HWND hwnd = _SCNGetWindow(TRUE, FALSE);
  2318. if (hwnd)
  2319. {
  2320. //
  2321. // Transmit the change registration
  2322. //
  2323. fResult = (BOOL) SendMessage(hwnd, SCNM_DEREGISTERCLIENT, ulID, 0);
  2324. }
  2325. return fResult;
  2326. }
  2327. // send the notify to the desktop... telling it to put it in the queue.
  2328. // if we are in the desktop's process, we can handle it directly ourselves.
  2329. // the one exception is flush. we want the desktop to be one serializing flush so
  2330. // we send in that case as well
  2331. void SHChangeNotifyTransmit(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra, DWORD dwEventTime)
  2332. {
  2333. HWND hwndSCN = _SCNGetWindow(TRUE, FALSE);
  2334. if (hwndSCN)
  2335. {
  2336. DWORD dwProcId;
  2337. GetWindowThreadProcessId(hwndSCN, &dwProcId);
  2338. HANDLE hChange = SHChangeNotification_Create(lEvent, uFlags, pidl, pidlExtra, dwProcId, dwEventTime);
  2339. if (hChange)
  2340. {
  2341. BOOL fFlushNow = ((uFlags & (SHCNF_FLUSH | SHCNF_FLUSHNOWAIT)) == SHCNF_FLUSH);
  2342. // Flush but not flush no wait
  2343. if (fFlushNow)
  2344. {
  2345. SendMessage(hwndSCN, SCNM_NOTIFYEVENT,
  2346. (WPARAM)hChange, (LPARAM)dwProcId);
  2347. }
  2348. else
  2349. {
  2350. SendNotifyMessage(hwndSCN, SCNM_NOTIFYEVENT,
  2351. (WPARAM)hChange, (LPARAM)dwProcId);
  2352. }
  2353. }
  2354. }
  2355. }
  2356. void FreeSpacePidlToPath(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  2357. {
  2358. TCHAR szPath1[MAX_PATH];
  2359. if (SHGetPathFromIDList(pidl1, szPath1))
  2360. {
  2361. TCHAR szPath2[MAX_PATH];
  2362. szPath2[0] = 0;
  2363. if (pidl2)
  2364. {
  2365. SHGetPathFromIDList(pidl2, szPath2);
  2366. }
  2367. SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATH, szPath1, szPath2[0] ? szPath2 : NULL);
  2368. }
  2369. }
  2370. STDAPI_(void) SHChangeNotify(LONG lEvent, UINT uFlags, const void * dwItem1, const void * dwItem2)
  2371. {
  2372. if (!_SCNGetWindow(TRUE, FALSE))
  2373. return;
  2374. LPCITEMIDLIST pidl = NULL;
  2375. LPCITEMIDLIST pidlExtra = NULL;
  2376. LPITEMIDLIST pidlFree = NULL;
  2377. LPITEMIDLIST pidlExtraFree = NULL;
  2378. UINT uType = uFlags & SHCNF_TYPE;
  2379. SHChangeDWORDAsIDList dwidl;
  2380. BOOL fPrinter = FALSE;
  2381. BOOL fPrintJob = FALSE;
  2382. DWORD dwEventTime = GetTickCount();
  2383. // first setup anything the flags request
  2384. switch (uType)
  2385. {
  2386. case SHCNF_PRINTJOBA:
  2387. fPrintJob = TRUE;
  2388. // fall through
  2389. case SHCNF_PRINTERA:
  2390. fPrinter = TRUE;
  2391. // fall through
  2392. case SHCNF_PATHA:
  2393. {
  2394. TCHAR szPath1[MAX_PATH], szPath2[MAX_PATH];
  2395. LPCVOID pvItem1 = NULL;
  2396. LPCVOID pvItem2 = NULL;
  2397. if (dwItem1)
  2398. {
  2399. SHAnsiToTChar((LPSTR)dwItem1, szPath1, ARRAYSIZE(szPath1));
  2400. pvItem1 = szPath1;
  2401. }
  2402. if (dwItem2)
  2403. {
  2404. if (fPrintJob)
  2405. pvItem2 = dwItem2; // SHCNF_PRINTJOB_DATA needs no conversion
  2406. else
  2407. {
  2408. SHAnsiToTChar((LPSTR)dwItem2, szPath2, ARRAYSIZE(szPath2));
  2409. pvItem2 = szPath2;
  2410. }
  2411. }
  2412. SHChangeNotify(lEvent, (fPrintJob ? SHCNF_PRINTJOB : (fPrinter ? SHCNF_PRINTER : SHCNF_PATH)),
  2413. pvItem1, pvItem2);
  2414. goto Cleanup; // Let the recursive version do all the work
  2415. }
  2416. break;
  2417. case SHCNF_PATH:
  2418. if (lEvent == SHCNE_FREESPACE)
  2419. {
  2420. DWORD dwItem = 0;
  2421. int idDrive = PathGetDriveNumber((LPCTSTR)dwItem1);
  2422. if (idDrive != -1)
  2423. dwItem = (1 << idDrive);
  2424. if (dwItem2)
  2425. {
  2426. idDrive = PathGetDriveNumber((LPCTSTR)dwItem2);
  2427. if (idDrive != -1)
  2428. dwItem |= (1 << idDrive);
  2429. }
  2430. dwItem1 = (LPCVOID)ULongToPtr( dwItem );
  2431. if (dwItem1)
  2432. goto DoDWORD;
  2433. goto Cleanup;
  2434. }
  2435. else
  2436. {
  2437. if (dwItem1)
  2438. {
  2439. pidl = pidlFree = SHSimpleIDListFromPath((LPCTSTR)dwItem1);
  2440. if (!pidl)
  2441. goto Cleanup;
  2442. if (dwItem2)
  2443. {
  2444. pidlExtra = pidlExtraFree = SHSimpleIDListFromPath((LPCTSTR)dwItem2);
  2445. if (!pidlExtra)
  2446. goto Cleanup;
  2447. }
  2448. }
  2449. }
  2450. break;
  2451. case SHCNF_PRINTER:
  2452. if (dwItem1)
  2453. {
  2454. TraceMsg(TF_SHELLCHANGENOTIFY, "SHChangeNotify: SHCNF_PRINTER %s", (LPTSTR)dwItem1);
  2455. if (FAILED(ParsePrinterName((LPCTSTR)dwItem1, &pidlFree)))
  2456. {
  2457. goto Cleanup;
  2458. }
  2459. pidl = pidlFree;
  2460. if (dwItem2)
  2461. {
  2462. if (FAILED(ParsePrinterName((LPCTSTR)dwItem2, &pidlExtraFree)))
  2463. {
  2464. goto Cleanup;
  2465. }
  2466. pidlExtra = pidlExtraFree;
  2467. }
  2468. }
  2469. break;
  2470. case SHCNF_PRINTJOB:
  2471. if (dwItem1)
  2472. {
  2473. #ifdef DEBUG
  2474. switch (lEvent)
  2475. {
  2476. case SHCNE_CREATE:
  2477. TraceMsg(TF_SHELLCHANGENOTIFY, "SHChangeNotify: SHCNE_CREATE SHCNF_PRINTJOB %s", (LPTSTR)dwItem1);
  2478. break;
  2479. case SHCNE_DELETE:
  2480. TraceMsg(TF_SHELLCHANGENOTIFY, "SHChangeNotify: SHCNE_DELETE SHCNF_PRINTJOB %s", (LPTSTR)dwItem1);
  2481. break;
  2482. case SHCNE_UPDATEITEM:
  2483. TraceMsg(TF_SHELLCHANGENOTIFY, "SHChangeNotify: SHCNE_UPDATEITEM SHCNF_PRINTJOB %s", (LPTSTR)dwItem1);
  2484. break;
  2485. default:
  2486. TraceMsg(TF_SHELLCHANGENOTIFY, "SHChangeNotify: SHCNE_? SHCNF_PRINTJOB %s", (LPTSTR)dwItem1);
  2487. break;
  2488. }
  2489. #endif
  2490. pidl = pidlFree = Printjob_GetPidl((LPCTSTR)dwItem1, (LPSHCNF_PRINTJOB_DATA)dwItem2);
  2491. if (!pidl)
  2492. goto Cleanup;
  2493. }
  2494. else
  2495. {
  2496. // Caller goofed.
  2497. goto Cleanup;
  2498. }
  2499. break;
  2500. case SHCNF_DWORD:
  2501. DoDWORD:
  2502. ASSERT(lEvent & SHCNE_GLOBALEVENTS);
  2503. dwidl.cb = sizeof(dwidl) - sizeof(dwidl.cbZero);
  2504. dwidl.dwItem1 = PtrToUlong(dwItem1);
  2505. dwidl.dwItem2 = PtrToUlong(dwItem2);
  2506. dwidl.cbZero = 0;
  2507. pidl = (LPCITEMIDLIST)&dwidl;
  2508. pidlExtra = NULL;
  2509. break;
  2510. case 0:
  2511. if (lEvent == SHCNE_FREESPACE) {
  2512. // convert this to paths.
  2513. FreeSpacePidlToPath((LPCITEMIDLIST)dwItem1, (LPCITEMIDLIST)dwItem2);
  2514. goto Cleanup;
  2515. }
  2516. pidl = (LPCITEMIDLIST)dwItem1;
  2517. pidlExtra = (LPCITEMIDLIST)dwItem2;
  2518. break;
  2519. default:
  2520. TraceMsg(TF_ERROR, "SHChangeNotify: Unrecognized uFlags 0x%X", uFlags);
  2521. return;
  2522. }
  2523. if (lEvent && !(lEvent & SHCNE_ASSOCCHANGED) && !pidl)
  2524. {
  2525. // Caller goofed. SHChangeNotifyTransmit & clients assume pidl is
  2526. // non-NULL if lEvent is non-zero (except in the SHCNE_ASSOCCHANGED case),
  2527. // and they will crash if we try to send this bogus event. So throw out
  2528. // this event and rip.
  2529. RIP(FALSE);
  2530. goto Cleanup;
  2531. }
  2532. SHChangeNotifyTransmit(lEvent, uFlags, pidl, pidlExtra, dwEventTime);
  2533. Cleanup:
  2534. if (pidlFree)
  2535. ILFree(pidlFree);
  2536. if (pidlExtraFree)
  2537. ILFree(pidlExtraFree);
  2538. }
  2539. // SHChangeNotifySuspendResume
  2540. //
  2541. // Suspends or resumes filesystem notifications on a path. If bRecursive
  2542. // is set, disable/enables them for all child paths as well.
  2543. STDAPI_(BOOL) SHChangeNotifySuspendResume(BOOL bSuspend,
  2544. LPITEMIDLIST pidlSuspend,
  2545. BOOL bRecursive,
  2546. DWORD dwReserved)
  2547. {
  2548. BOOL fRet = FALSE;
  2549. HWND hwndSCN = _SCNGetWindow(TRUE, FALSE);
  2550. if (hwndSCN)
  2551. {
  2552. HANDLE hChange;
  2553. DWORD dwProcId;
  2554. UINT uiFlags = bSuspend ? SCNSUSPEND_SUSPEND : 0;
  2555. if (bRecursive)
  2556. uiFlags |= SCNSUSPEND_RECURSIVE;
  2557. GetWindowThreadProcessId(hwndSCN, &dwProcId);
  2558. // overloading the structure semantics here a little bit.
  2559. // our two flags
  2560. hChange = SHChangeNotification_Create(0, uiFlags, pidlSuspend, NULL, dwProcId, 0);
  2561. if (hChange)
  2562. {
  2563. // Transmit to SCN
  2564. fRet = (BOOL)SendMessage(hwndSCN, SCNM_SUSPENDRESUME, (WPARAM)hChange, (LPARAM)dwProcId);
  2565. SHChangeNotification_Destroy(hChange, dwProcId);
  2566. }
  2567. }
  2568. return fRet;
  2569. }
  2570. STDAPI_(void) SHChangeNotifyTerminate(BOOL bLastTerm, BOOL bProcessShutdown)
  2571. {
  2572. if (g_pscn)
  2573. {
  2574. PostThreadMessage(GetWindowThreadProcessId(g_hwndSCN, NULL), SCNM_TERMINATE, 0, 0);
  2575. }
  2576. }
  2577. // this deregisters anything that this window might have been registered in
  2578. STDAPI_(void) SHChangeNotifyDeregisterWindow(HWND hwnd)
  2579. {
  2580. HWND hwndSCN = _SCNGetWindow(TRUE, FALSE);
  2581. if (hwndSCN)
  2582. {
  2583. SendMessage(hwndSCN, SCNM_DEREGISTERWINDOW, (WPARAM)hwnd, 0);
  2584. }
  2585. }
  2586. //--------------------------------------------------------------------------
  2587. // We changed the way that the SHChangeNotifyRegister function worked, so
  2588. // to prevent people from calling the old function, we stub it out here.
  2589. // The change we made would have broken everbody because we changed the
  2590. // lparam and wparam for the notification messages which are sent to the
  2591. // registered window.
  2592. //
  2593. STDAPI_(ULONG) NTSHChangeNotifyRegister(HWND hwnd,
  2594. int fSources, LONG fEvents,
  2595. UINT wMsg, int cEntries,
  2596. SHChangeNotifyEntry *pfsne)
  2597. {
  2598. return SHChangeNotifyRegister(hwnd, fSources | SHCNRF_NewDelivery , fEvents, wMsg, cEntries, pfsne);
  2599. }
  2600. STDAPI_(BOOL) NTSHChangeNotifyDeregister(ULONG ulID)
  2601. {
  2602. return SHChangeNotifyDeregister(ulID);
  2603. }
  2604. // NOTE: There is a copy of these functions in shdocvw util.cpp for browser only mode supprt.
  2605. // NOTE: functionality changes should also be reflected there.
  2606. STDAPI_(void) SHUpdateImageA( LPCSTR pszHashItem, int iIndex, UINT uFlags, int iImageIndex )
  2607. {
  2608. WCHAR szWHash[MAX_PATH];
  2609. SHAnsiToUnicode(pszHashItem, szWHash, ARRAYSIZE(szWHash));
  2610. SHUpdateImageW(szWHash, iIndex, uFlags, iImageIndex);
  2611. }
  2612. STDAPI_(void) SHUpdateImageW( LPCWSTR pszHashItem, int iIndex, UINT uFlags, int iImageIndex )
  2613. {
  2614. SHChangeUpdateImageIDList rgPidl;
  2615. SHChangeDWORDAsIDList rgDWord;
  2616. int cLen = MAX_PATH - (lstrlenW( pszHashItem ) + 1);
  2617. cLen *= sizeof( WCHAR );
  2618. if ( cLen < 0 )
  2619. {
  2620. cLen = 0;
  2621. }
  2622. // make sure we send a valid index
  2623. if ( iImageIndex == -1 )
  2624. {
  2625. iImageIndex = II_DOCUMENT;
  2626. }
  2627. rgPidl.dwProcessID = GetCurrentProcessId();
  2628. rgPidl.iIconIndex = iIndex;
  2629. rgPidl.iCurIndex = iImageIndex;
  2630. rgPidl.uFlags = uFlags;
  2631. StrCpyNW( rgPidl.szName, pszHashItem, MAX_PATH );
  2632. rgPidl.cb = (USHORT)(sizeof( rgPidl ) - cLen);
  2633. _ILNext( (LPITEMIDLIST) &rgPidl )->mkid.cb = 0;
  2634. rgDWord.cb = sizeof( rgDWord) - sizeof(USHORT);
  2635. rgDWord.dwItem1 = iImageIndex;
  2636. rgDWord.dwItem2 = 0;
  2637. rgDWord.cbZero = 0;
  2638. // pump it as an extended event
  2639. SHChangeNotify(SHCNE_UPDATEIMAGE, SHCNF_IDLIST, &rgDWord, &rgPidl);
  2640. }
  2641. // REVIEW: pretty poor implementation of handling updateimage, requiring the caller
  2642. // to handle the pidl case instead of passing both pidls down here.
  2643. //
  2644. STDAPI_(int) SHHandleUpdateImage( LPCITEMIDLIST pidlExtra )
  2645. {
  2646. SHChangeUpdateImageIDList * pUs = (SHChangeUpdateImageIDList*) pidlExtra;
  2647. if ( !pUs )
  2648. {
  2649. return -1;
  2650. }
  2651. // if in the same process, or an old style notification
  2652. if ( pUs->dwProcessID == GetCurrentProcessId())
  2653. {
  2654. return *(int UNALIGNED *)((BYTE *)&pUs->iCurIndex);
  2655. }
  2656. else
  2657. {
  2658. WCHAR szBuffer[MAX_PATH];
  2659. int iIconIndex = *(int UNALIGNED *)((BYTE *)&pUs->iIconIndex);
  2660. UINT uFlags = *(UINT UNALIGNED *)((BYTE *)&pUs->uFlags);
  2661. ualstrcpyW( szBuffer, pUs->szName );
  2662. // we are in a different process, look up the hash in our index to get the right one...
  2663. return SHLookupIconIndexW( szBuffer, iIconIndex, uFlags );
  2664. }
  2665. }
  2666. //
  2667. // NOTE: these are OLD APIs, new clients should use new APIs
  2668. //
  2669. // REVIEW: BobDay - SHChangeNotifyUpdateEntryList doesn't appear to be
  2670. // called by anybody and since we've change the notification message
  2671. // structure, anybody who calls it needs to be identified and fixed.
  2672. //
  2673. BOOL WINAPI SHChangeNotifyUpdateEntryList(ULONG ulID, int iUpdateType,
  2674. int cEntries, SHChangeNotifyEntry *pfsne)
  2675. {
  2676. ASSERT(FALSE);
  2677. return FALSE;
  2678. }
  2679. void SHChangeNotifyReceive(LONG lEvent, UINT uFlags, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlExtra)
  2680. {
  2681. ASSERT(FALSE);
  2682. }
  2683. BOOL WINAPI SHChangeRegistrationReceive(HANDLE hChangeRegistration, DWORD dwProcId)
  2684. {
  2685. ASSERT(FALSE);
  2686. return FALSE;
  2687. }