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.

1868 lines
57 KiB

  1. #include "shellprv.h"
  2. #include <brfcasep.h>
  3. #include "filefldr.h"
  4. #include "brfcase.h"
  5. #include "datautil.h"
  6. #include "prop.h"
  7. #include "ids.h"
  8. #include "defview.h" // for WM_DSV_FSNOTIFY
  9. #include "basefvcb.h"
  10. #include "views.h"
  11. #define MAX_NAME 32
  12. #define HACK_IGNORETYPE 0x04000000
  13. // Values for CBriefcase::_FindNextState
  14. #define FNS_UNDETERMINED 1
  15. #define FNS_STALE 2
  16. #define FNS_DELETED 3
  17. typedef struct
  18. {
  19. TCHAR szOrigin[MAX_PATH];
  20. TCHAR szStatus[MAX_NAME];
  21. BOOL bDetermined:1;
  22. BOOL bUpToDate:1;
  23. BOOL bDeleted:1;
  24. } BRFINFO;
  25. typedef struct
  26. {
  27. LPITEMIDLIST pidl; // Indexed value
  28. BRFINFO bi;
  29. } BRFINFOHDR;
  30. class CBriefcaseViewCB;
  31. class CBriefcase : public CFSFolder
  32. {
  33. friend CBriefcaseViewCB;
  34. public:
  35. CBriefcase(IUnknown *punkOuter);
  36. STDMETHODIMP Init(); // initialize the critical section
  37. // IShellFolder
  38. STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  39. STDMETHODIMP CreateViewObject (HWND hwndOwner, REFIID riid, void **ppv);
  40. STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG *rgfInOut);
  41. STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppv);
  42. // IShellFolder2
  43. STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
  44. STDMETHOD (MapColumnToSCID)(UINT iColumn, SHCOLUMNID *pscid);
  45. private:
  46. ~CBriefcase();
  47. static DWORD CALLBACK _CalcDetailsThreadProc(void *pv);
  48. DWORD _CalcDetailsThread();
  49. void _EnterCS();
  50. void _LeaveCS();
  51. static int CALLBACK _CompareIDCallBack(void *pv1, void *pv2, LPARAM lParam);
  52. BOOL _CreateDetailsThread();
  53. BOOL _InitDetailsInfoAndThread(IBriefcaseStg *pbrfstg, HWND hwndMain, HANDLE hMutexDelay);
  54. void _Free();
  55. void _Reset();
  56. BOOL _FindCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi);
  57. BOOL _DeleteCachedName(LPCITEMIDLIST pidl);
  58. BOOL _FindNextState(UINT uState, BRFINFOHDR *pbihdrOut);
  59. void _CalcCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi);
  60. void _CachedNameIsStale(LPCITEMIDLIST pidl, BOOL bDeleted);
  61. void _AllNamesAreStale();
  62. BOOL _AddCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi);
  63. HRESULT _CreateView(HWND hwnd, IShellView **ppsv);
  64. HWND _hwndMain; // evil, view related state
  65. IBriefcaseStg *_pbrfstg; // evil, view related state
  66. // accessed by background thread
  67. HDPA _hdpa;
  68. int _idpaStaleCur;
  69. int _idpaUndeterminedCur;
  70. int _idpaDeletedCur;
  71. HANDLE _hSemPending; // Pending semaphore
  72. CRITICAL_SECTION _cs;
  73. BOOL _fcsInit;
  74. HANDLE _hEventDie;
  75. HANDLE _hThreadCalcDetails;
  76. HANDLE _hMutexDelay; // alias given out by the _pbrfstg
  77. BOOL _bFreePending;
  78. #ifdef DEBUG
  79. UINT _cUndetermined;
  80. UINT _cStale;
  81. UINT _cDeleted;
  82. UINT _cCSRef;
  83. #endif
  84. };
  85. class CBriefcaseViewCB : public CBaseShellFolderViewCB
  86. {
  87. public:
  88. CBriefcaseViewCB(CBriefcase *pfolder);
  89. HRESULT _InitStgForDetails();
  90. STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
  91. private:
  92. ~CBriefcaseViewCB();
  93. LPCITEMIDLIST _FolderPidl() { return _pfolder->_pidl; }
  94. HRESULT OnWINDOWCREATED(DWORD pv, HWND hwndView);
  95. HRESULT OnWINDOWDESTROY(DWORD pv, HWND wP);
  96. HRESULT OnMergeMenu(DWORD pv, QCMINFO*lP);
  97. HRESULT OnINVOKECOMMAND(DWORD pv, UINT wP);
  98. HRESULT OnGetHelpOrTooltipText(BOOL bHelp, UINT wPl, UINT cch, LPTSTR psz);
  99. HRESULT OnINITMENUPOPUP(DWORD pv, UINT wPl, UINT wPh, HMENU lP);
  100. HRESULT OnGETBUTTONINFO(DWORD pv, TBINFO* ptbinfo);
  101. HRESULT OnGETBUTTONS(DWORD pv, UINT wPl, UINT wPh, TBBUTTON*lP);
  102. HRESULT OnSELCHANGE(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA*lP);
  103. HRESULT OnQUERYFSNOTIFY(DWORD pv, SHChangeNotifyEntry*lP);
  104. HRESULT OnFSNOTIFY(DWORD pv, LPCITEMIDLIST*wP, LPARAM lP);
  105. HRESULT OnQUERYCOPYHOOK(DWORD pv);
  106. HRESULT OnNOTIFYCOPYHOOK(DWORD pv, COPYHOOKINFO*lP);
  107. HRESULT OnINSERTITEM(DWORD pv, LPCITEMIDLIST wP);
  108. HRESULT OnDEFVIEWMODE(DWORD pv, FOLDERVIEWMODE *lP);
  109. HRESULT OnSupportsIdentity(DWORD pv);
  110. HRESULT OnGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA * phtd);
  111. HRESULT OnDELAYWINDOWCREATE(DWORD pv, HWND hwnd);
  112. HRESULT _GetSelectedObjects(IDataObject **ppdtobj);
  113. HRESULT _HandleFSNotifyForDefView(LPARAM lEvent, LPCITEMIDLIST * ppidl, LPTSTR pszBuf);
  114. int _GetSelectedCount();
  115. CBriefcase *_pfolder;
  116. IBriefcaseStg *_pbrfstg;
  117. LPITEMIDLIST _pidlRoot; // Root of briefcase
  118. HANDLE _hMutexDelay;
  119. ULONG _uSCNRExtra; // Extra SHChangeNotifyRegister for our pidl...
  120. TCHAR _szDBName[MAX_PATH];
  121. // Web View implementation
  122. HRESULT OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData);
  123. HRESULT OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData);
  124. HRESULT OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks);
  125. public:
  126. static HRESULT _OnUpdate(IUnknown* pv,IShellItemArray *psiItemArray, IBindCtx *pbc);
  127. };
  128. CBriefcase::CBriefcase(IUnknown *punkOuter) : CFSFolder(punkOuter)
  129. {
  130. _clsidBind = CLSID_BriefcaseFolder; // in CFSFolder
  131. _fcsInit = FALSE;
  132. }
  133. CBriefcase::~CBriefcase()
  134. {
  135. if (_fcsInit)
  136. {
  137. DeleteCriticalSection(&_cs);
  138. }
  139. }
  140. STDMETHODIMP CBriefcase::Init()
  141. {
  142. _fcsInit = InitializeCriticalSectionAndSpinCount(&_cs, 0);
  143. return _fcsInit ? S_OK : E_FAIL;
  144. }
  145. enum
  146. {
  147. ICOL_BRIEFCASE_NAME = 0,
  148. ICOL_BRIEFCASE_ORIGIN,
  149. ICOL_BRIEFCASE_STATUS,
  150. ICOL_BRIEFCASE_SIZE,
  151. ICOL_BRIEFCASE_TYPE,
  152. ICOL_BRIEFCASE_MODIFIED,
  153. };
  154. const COLUMN_INFO s_briefcase_cols[] =
  155. {
  156. DEFINE_COL_STR_ENTRY(SCID_NAME, 30, IDS_NAME_COL),
  157. DEFINE_COL_STR_ENTRY(SCID_SYNCCOPYIN, 24, IDS_SYNCCOPYIN_COL),
  158. DEFINE_COL_STR_ENTRY(SCID_STATUS, 18, IDS_STATUS_COL),
  159. DEFINE_COL_SIZE_ENTRY(SCID_SIZE, IDS_SIZE_COL),
  160. DEFINE_COL_STR_ENTRY(SCID_TYPE, 18, IDS_TYPE_COL),
  161. DEFINE_COL_STR_ENTRY(SCID_WRITETIME, 18, IDS_MODIFIED_COL),
  162. };
  163. #ifdef DEBUG
  164. #define _AssertInCS() ASSERT(0 < (this)->_cCSRef)
  165. #define _AssertNotInCS() ASSERT(0 == (this)->_cCSRef)
  166. #else
  167. #define _AssertInCS()
  168. #define _AssertNotInCS()
  169. #endif
  170. void CBriefcase::_EnterCS()
  171. {
  172. ASSERT(_fcsInit);
  173. EnterCriticalSection(&_cs);
  174. #ifdef DEBUG
  175. _cCSRef++;
  176. #endif
  177. }
  178. void CBriefcase::_LeaveCS()
  179. {
  180. ASSERT(_fcsInit);
  181. _AssertInCS();
  182. #ifdef DEBUG
  183. _cCSRef--;
  184. #endif
  185. LeaveCriticalSection(&_cs);
  186. }
  187. //---------------------------------------------------------------------------
  188. // Brfview functions: Expensive cache stuff
  189. //---------------------------------------------------------------------------
  190. // Comparison function for the DPA list
  191. int CALLBACK CBriefcase::_CompareIDCallBack(void *pv1, void *pv2, LPARAM lParam)
  192. {
  193. BRFINFOHDR *pbihdr1 = (BRFINFOHDR *)pv1;
  194. BRFINFOHDR *pbihdr2 = (BRFINFOHDR *)pv2;
  195. CBriefcase *pfolder = (CBriefcase *)lParam;
  196. HRESULT hr = pfolder->CompareIDs(HACK_IGNORETYPE, pbihdr1->pidl, pbihdr2->pidl);
  197. ASSERT(SUCCEEDED(hr));
  198. return (short)SCODE_CODE(GetScode(hr)); // (the short cast is important!)
  199. }
  200. // Create the secondary thread for the expensive cache
  201. BOOL CBriefcase::_CreateDetailsThread()
  202. {
  203. BOOL bRet = FALSE;
  204. // The semaphore is used to determine whether anything
  205. // needs to be refreshed in the cache.
  206. _hSemPending = CreateSemaphore(NULL, 0, MAXLONG, NULL);
  207. if (_hSemPending)
  208. {
  209. #ifdef DEBUG
  210. _cStale = 0;
  211. _cUndetermined = 0;
  212. _cDeleted = 0;
  213. #endif
  214. ASSERT(NULL == _hEventDie);
  215. _hEventDie = CreateEvent(NULL, FALSE, FALSE, NULL);
  216. if (_hEventDie)
  217. {
  218. // Create the thread that will calculate expensive data
  219. DWORD idThread;
  220. _hThreadCalcDetails = CreateThread(NULL, 0, _CalcDetailsThreadProc, this, CREATE_SUSPENDED, &idThread);
  221. if (_hThreadCalcDetails)
  222. {
  223. ResumeThread(_hThreadCalcDetails);
  224. bRet = TRUE;
  225. }
  226. else
  227. {
  228. CloseHandle(_hEventDie);
  229. _hEventDie = NULL;
  230. CloseHandle(_hSemPending);
  231. _hSemPending = NULL;
  232. }
  233. }
  234. else
  235. {
  236. CloseHandle(_hSemPending);
  237. _hSemPending = NULL;
  238. }
  239. }
  240. return bRet;
  241. }
  242. // view callback inits the folder with data it needs to run the GetDetailsOf() stuff
  243. // on a background thread
  244. BOOL CBriefcase::_InitDetailsInfoAndThread(IBriefcaseStg *pbrfstg, HWND hwndMain, HANDLE hMutexDelay)
  245. {
  246. BOOL bRet = FALSE;
  247. ASSERT(pbrfstg && hwndMain && hMutexDelay); // from the init call
  248. _EnterCS();
  249. {
  250. if (_hdpa)
  251. {
  252. bRet = TRUE;
  253. }
  254. else
  255. {
  256. _hwndMain = hwndMain;
  257. _hMutexDelay = hMutexDelay;
  258. _idpaStaleCur = 0;
  259. _idpaUndeterminedCur = 0;
  260. _idpaDeletedCur = 0;
  261. _hdpa = DPA_Create(8);
  262. if (_hdpa)
  263. {
  264. bRet = _CreateDetailsThread();
  265. if (bRet)
  266. {
  267. ASSERT(NULL == _pbrfstg);
  268. _pbrfstg = pbrfstg;
  269. pbrfstg->AddRef();
  270. }
  271. else
  272. {
  273. // Failed
  274. DPA_Destroy(_hdpa);
  275. _hdpa = NULL;
  276. }
  277. }
  278. }
  279. }
  280. _LeaveCS();
  281. return bRet;
  282. }
  283. // Clean up the cache of expensive data
  284. void CBriefcase::_Free()
  285. {
  286. _EnterCS();
  287. {
  288. if (_hEventDie)
  289. {
  290. if (_hThreadCalcDetails)
  291. {
  292. HANDLE hThread = _hThreadCalcDetails;
  293. SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST);
  294. // Signal the secondary thread to end
  295. SetEvent(_hEventDie);
  296. // Make sure we are not in the critical section when
  297. // we wait for the secondary thread to exit. Without
  298. // this check, hitting F5 twice in a row could deadlock.
  299. _LeaveCS();
  300. {
  301. // Wait for the threads to exit
  302. _AssertNotInCS();
  303. WaitForSendMessageThread(hThread, INFINITE);
  304. }
  305. _EnterCS();
  306. DebugMsg(DM_TRACE, TEXT("Briefcase Secondary thread ended"));
  307. CloseHandle(_hThreadCalcDetails);
  308. _hThreadCalcDetails = NULL;
  309. }
  310. CloseHandle(_hEventDie);
  311. _hEventDie = NULL;
  312. }
  313. if (_hdpa)
  314. {
  315. int idpa = DPA_GetPtrCount(_hdpa);
  316. while (--idpa >= 0)
  317. {
  318. BRFINFOHDR *pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(_hdpa, idpa);
  319. ILFree(pbihdr->pidl);
  320. LocalFree((HLOCAL)pbihdr);
  321. }
  322. DPA_Destroy(_hdpa);
  323. _hdpa = NULL;
  324. }
  325. if (_hSemPending)
  326. {
  327. CloseHandle(_hSemPending);
  328. _hSemPending = NULL;
  329. }
  330. if (_pbrfstg)
  331. {
  332. _pbrfstg->Release();
  333. _pbrfstg = NULL;
  334. _hMutexDelay = NULL; // invalidate our alias
  335. }
  336. }
  337. _LeaveCS();
  338. }
  339. // Resets the expensive data cache
  340. void CBriefcase::_Reset()
  341. {
  342. _AssertNotInCS();
  343. _EnterCS();
  344. {
  345. IBriefcaseStg *pbrfstg = _pbrfstg;
  346. if (!_bFreePending && pbrfstg)
  347. {
  348. HWND hwndMain = _hwndMain;
  349. HANDLE hMutex = _hMutexDelay;
  350. pbrfstg->AddRef();
  351. // Since we won't be in the critical section when we
  352. // wait for the paint thread to exit, set this flag to
  353. // avoid nasty re-entrant calls.
  354. _bFreePending = TRUE;
  355. // Reset by freeing and reinitializing.
  356. _LeaveCS();
  357. {
  358. _Free();
  359. // whacky re-init of ourselevs
  360. _InitDetailsInfoAndThread(pbrfstg, hwndMain, hMutex);
  361. }
  362. _EnterCS();
  363. _bFreePending = FALSE;
  364. pbrfstg->Release();
  365. }
  366. }
  367. _LeaveCS();
  368. }
  369. // Finds a cached name structure and returns a copy of it in *pbi.
  370. BOOL CBriefcase::_FindCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi)
  371. {
  372. BOOL bRet = FALSE;
  373. _EnterCS();
  374. {
  375. if (_hdpa)
  376. {
  377. BRFINFOHDR bihdr = {0};
  378. bihdr.pidl = (LPITEMIDLIST)pidl; // const -> non const
  379. int idpa = DPA_Search(_hdpa, &bihdr, 0, _CompareIDCallBack, (LPARAM)this, DPAS_SORTED);
  380. if (DPA_ERR != idpa)
  381. {
  382. // Yes
  383. BRFINFOHDR *pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(_hdpa, idpa);
  384. ASSERT(pbihdr);
  385. *pbi = pbihdr->bi;
  386. bRet = TRUE;
  387. }
  388. }
  389. }
  390. _LeaveCS();
  391. return bRet;
  392. }
  393. // Deletes a cached name structure.
  394. BOOL CBriefcase::_DeleteCachedName(LPCITEMIDLIST pidl)
  395. {
  396. BOOL bRet = FALSE;
  397. _EnterCS();
  398. {
  399. if (_hdpa)
  400. {
  401. BRFINFOHDR bihdr = {0};
  402. bihdr.pidl = (LPITEMIDLIST)pidl; // const -> non const
  403. int idpa = DPA_Search(_hdpa, &bihdr, 0, _CompareIDCallBack, (LPARAM)this, DPAS_SORTED);
  404. if (DPA_ERR != idpa)
  405. {
  406. #ifdef DEBUG
  407. BRFINFOHDR *pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(_hdpa, idpa);
  408. ASSERT(pbihdr);
  409. _cDeleted--;
  410. if (!pbihdr->bi.bDetermined)
  411. _cUndetermined--;
  412. else if (!pbihdr->bi.bUpToDate)
  413. _cStale--;
  414. #endif
  415. // Keep index pointers current
  416. if (_idpaStaleCur >= idpa)
  417. _idpaStaleCur--;
  418. if (_idpaUndeterminedCur >= idpa)
  419. _idpaUndeterminedCur--;
  420. if (_idpaDeletedCur >= idpa)
  421. _idpaDeletedCur--;
  422. DPA_DeletePtr(_hdpa, idpa);
  423. bRet = TRUE;
  424. }
  425. }
  426. }
  427. _LeaveCS();
  428. return bRet;
  429. }
  430. // Finds the next cached name structure that matches the requested state.
  431. BOOL CBriefcase::_FindNextState(UINT uState, BRFINFOHDR *pbihdrOut)
  432. {
  433. BOOL bRet = FALSE;
  434. ASSERT(pbihdrOut);
  435. _EnterCS();
  436. {
  437. if (_hdpa)
  438. {
  439. HDPA hdpa = _hdpa;
  440. int idpaCur;
  441. int idpa;
  442. BRFINFOHDR *pbihdr;
  443. int cdpaMax = DPA_GetPtrCount(hdpa);
  444. switch (uState)
  445. {
  446. case FNS_UNDETERMINED:
  447. // Iterate thru the entire list starting at idpa. We roll this
  448. // loop out to be two loops: the first iterates the last portion
  449. // of the list, the second iterates the first portion if the former
  450. // failed to find anything.
  451. idpaCur = _idpaUndeterminedCur + 1;
  452. for (idpa = idpaCur; idpa < cdpaMax; idpa++)
  453. {
  454. pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa);
  455. if (!pbihdr->bi.bDetermined)
  456. {
  457. goto Found; // Found it
  458. }
  459. }
  460. ASSERT(idpaCur <= cdpaMax);
  461. for (idpa = 0; idpa < idpaCur; idpa++)
  462. {
  463. pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa);
  464. if (!pbihdr->bi.bDetermined)
  465. {
  466. goto Found; // Found it
  467. }
  468. }
  469. ASSERT(0 == _cUndetermined);
  470. break;
  471. case FNS_STALE:
  472. // Iterate thru the entire list starting at idpa. We roll this
  473. // loop out to be two loops: the first iterates the last portion
  474. // of the list, the second iterates the first portion if the former
  475. // failed to find anything.
  476. idpaCur = _idpaStaleCur + 1;
  477. for (idpa = idpaCur; idpa < cdpaMax; idpa++)
  478. {
  479. pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa);
  480. if (!pbihdr->bi.bUpToDate)
  481. {
  482. goto Found; // Found it
  483. }
  484. }
  485. ASSERT(idpaCur <= cdpaMax);
  486. for (idpa = 0; idpa < idpaCur; idpa++)
  487. {
  488. pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa);
  489. if (!pbihdr->bi.bUpToDate)
  490. {
  491. goto Found; // Found it
  492. }
  493. }
  494. ASSERT(0 == _cStale);
  495. break;
  496. case FNS_DELETED:
  497. // Iterate thru the entire list starting at idpa. We roll this
  498. // loop out to be two loops: the first iterates the last portion
  499. // of the list, the second iterates the first portion if the former
  500. // failed to find anything.
  501. idpaCur = _idpaDeletedCur + 1;
  502. for (idpa = idpaCur; idpa < cdpaMax; idpa++)
  503. {
  504. pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa);
  505. if (pbihdr->bi.bDeleted)
  506. {
  507. goto Found; // Found it
  508. }
  509. }
  510. ASSERT(idpaCur <= cdpaMax);
  511. for (idpa = 0; idpa < idpaCur; idpa++)
  512. {
  513. pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(hdpa, idpa);
  514. if (pbihdr->bi.bDeleted)
  515. {
  516. goto Found; // Found it
  517. }
  518. }
  519. ASSERT(0 == _cDeleted);
  520. break;
  521. default:
  522. ASSERT(0); // should never get here
  523. break;
  524. }
  525. goto Done;
  526. Found:
  527. ASSERT(0 <= idpa && idpa < cdpaMax);
  528. // Found the next item of the requested state
  529. switch (uState)
  530. {
  531. case FNS_UNDETERMINED:
  532. _idpaUndeterminedCur = idpa;
  533. break;
  534. case FNS_STALE:
  535. _idpaStaleCur = idpa;
  536. break;
  537. case FNS_DELETED:
  538. _idpaDeletedCur = idpa;
  539. break;
  540. }
  541. *pbihdrOut = *pbihdr;
  542. pbihdrOut->pidl = ILClone(pbihdr->pidl);
  543. if (pbihdrOut->pidl)
  544. bRet = TRUE;
  545. }
  546. Done:;
  547. }
  548. _LeaveCS();
  549. return bRet;
  550. }
  551. // Recalculates a cached name structure. This can be an expensive operation
  552. void CBriefcase::_CalcCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi)
  553. {
  554. _EnterCS();
  555. {
  556. if (_hdpa && _pbrfstg)
  557. {
  558. LPCIDFOLDER pidf = (LPCIDFOLDER)pidl;
  559. IBriefcaseStg *pbrfstg = _pbrfstg;
  560. pbrfstg->AddRef();
  561. // Make sure we're out of the critical section when we call
  562. // the expensive functions!
  563. _LeaveCS();
  564. {
  565. TCHAR szTmp[MAX_PATH];
  566. _CopyName(pidf, szTmp, ARRAYSIZE(szTmp));
  567. pbrfstg->GetExtraInfo(szTmp, GEI_ORIGIN, (WPARAM)ARRAYSIZE(pbi->szOrigin), (LPARAM)pbi->szOrigin);
  568. pbrfstg->GetExtraInfo(szTmp, GEI_STATUS, (WPARAM)ARRAYSIZE(pbi->szStatus), (LPARAM)pbi->szStatus);
  569. }
  570. _EnterCS();
  571. pbrfstg->Release();
  572. // Check again if we are valid
  573. if (_hdpa)
  574. {
  575. // Is the pidl still around so we can update it?
  576. BRFINFOHDR bihdr = {0};
  577. bihdr.pidl = (LPITEMIDLIST)pidf;
  578. int idpa = DPA_Search(_hdpa, &bihdr, 0, _CompareIDCallBack, (LPARAM)this, DPAS_SORTED);
  579. if (DPA_ERR != idpa)
  580. {
  581. // Yes; update it
  582. BRFINFOHDR * pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(_hdpa, idpa);
  583. ASSERT(!pbihdr->bi.bUpToDate || !pbihdr->bi.bDetermined)
  584. // This entry may have been marked for deletion while the
  585. // expensive calculations were in process above. Check for
  586. // it now.
  587. if (pbihdr->bi.bDeleted)
  588. {
  589. _DeleteCachedName(pidl);
  590. }
  591. else
  592. {
  593. pbihdr->bi = *pbi;
  594. pbihdr->bi.bUpToDate = TRUE;
  595. pbihdr->bi.bDetermined = TRUE;
  596. #ifdef DEBUG
  597. if (!pbi->bDetermined)
  598. _cUndetermined--;
  599. else if (!pbi->bUpToDate)
  600. _cStale--;
  601. else
  602. ASSERT(0);
  603. #endif
  604. }
  605. }
  606. }
  607. }
  608. _LeaveCS();
  609. }
  610. }
  611. // Finds a cached name structure and marks it stale
  612. // WARNING: pidl can be a fully qualified pidl that is comming through as a change notify
  613. void CBriefcase::_CachedNameIsStale(LPCITEMIDLIST pidl, BOOL bDeleted)
  614. {
  615. _EnterCS();
  616. {
  617. if (_hdpa)
  618. {
  619. BRFINFOHDR bihdr = {0};
  620. bihdr.pidl = ILFindLastID(pidl); // hope this is all ours
  621. int idpa = DPA_Search(_hdpa, &bihdr, 0, _CompareIDCallBack, (LPARAM)this, DPAS_SORTED);
  622. if (DPA_ERR != idpa)
  623. {
  624. // Yes; mark it stale
  625. BRFINFOHDR *pbihdr = (BRFINFOHDR *)DPA_FastGetPtr(_hdpa, idpa);
  626. // Is this cached name pending calculation yet?
  627. if (pbihdr->bi.bDetermined && pbihdr->bi.bUpToDate &&
  628. !pbihdr->bi.bDeleted)
  629. {
  630. // No; signal the calculation thread
  631. if (bDeleted)
  632. {
  633. pbihdr->bi.bDeleted = TRUE;
  634. #ifdef DEBUG
  635. _cDeleted++;
  636. #endif
  637. }
  638. else
  639. {
  640. pbihdr->bi.bUpToDate = FALSE;
  641. #ifdef DEBUG
  642. _cStale++;
  643. #endif
  644. }
  645. // Notify the calculating thread of an item that is pending
  646. // calculation
  647. ReleaseSemaphore(_hSemPending, 1, NULL);
  648. }
  649. else if (bDeleted)
  650. {
  651. // Yes; but mark for deletion anyway
  652. pbihdr->bi.bDeleted = TRUE;
  653. #ifdef DEBUG
  654. _cDeleted++;
  655. #endif
  656. }
  657. }
  658. }
  659. }
  660. _LeaveCS();
  661. }
  662. // Marks all cached name structures stale
  663. void CBriefcase::_AllNamesAreStale()
  664. {
  665. _EnterCS();
  666. {
  667. if (_pbrfstg)
  668. {
  669. UINT uFlags;
  670. // Dirty the briefcase storage cache
  671. _pbrfstg->Notify(NULL, NOE_DIRTYALL, &uFlags, NULL);
  672. }
  673. }
  674. _LeaveCS();
  675. // (It is important that we call CBriefcase::_Reset outside of the critical
  676. // section. Otherwise, we can deadlock when this function is called
  677. // while the secondary thread is calculating (hit F5 twice in a row).)
  678. // Clear the entire expensive data cache
  679. _Reset();
  680. }
  681. // Adds a new item with default values to the extra info list
  682. BOOL CBriefcase::_AddCachedName(LPCITEMIDLIST pidl, BRFINFO *pbi)
  683. {
  684. BOOL bRet = FALSE;
  685. ASSERT(_pbrfstg && _hwndMain && _hMutexDelay);
  686. _EnterCS();
  687. {
  688. if (_hdpa)
  689. {
  690. BRFINFOHDR * pbihdr = (BRFINFOHDR *)LocalAlloc(LPTR, sizeof(*pbihdr));
  691. if (pbihdr)
  692. {
  693. pbihdr->pidl = ILClone(pidl);
  694. if (pbihdr->pidl)
  695. {
  696. int idpa = DPA_AppendPtr(_hdpa, pbihdr);
  697. if (DPA_ERR != idpa)
  698. {
  699. pbihdr->bi.bUpToDate = FALSE;
  700. pbihdr->bi.bDetermined = FALSE;
  701. pbihdr->bi.bDeleted = FALSE;
  702. LoadString(HINST_THISDLL, IDS_DETAILSUNKNOWN, pbihdr->bi.szOrigin, ARRAYSIZE(pbihdr->bi.szOrigin));
  703. LoadString(HINST_THISDLL, IDS_DETAILSUNKNOWN, pbihdr->bi.szStatus, ARRAYSIZE(pbihdr->bi.szStatus));
  704. #ifdef DEBUG
  705. _cUndetermined++;
  706. #endif
  707. DPA_Sort(_hdpa, _CompareIDCallBack, (LPARAM)this);
  708. // Notify the calculating thread of an item that is pending
  709. // calculation
  710. ReleaseSemaphore(_hSemPending, 1, NULL);
  711. *pbi = pbihdr->bi;
  712. bRet = TRUE;
  713. }
  714. else
  715. {
  716. // Failed. Cleanup
  717. ILFree(pbihdr->pidl);
  718. LocalFree((HLOCAL)pbihdr);
  719. }
  720. }
  721. else
  722. {
  723. // Failed. Cleanup
  724. LocalFree((HLOCAL)pbihdr);
  725. }
  726. }
  727. }
  728. }
  729. _LeaveCS();
  730. return bRet;
  731. }
  732. DWORD CBriefcase::_CalcDetailsThread()
  733. {
  734. HANDLE rghObjPending[2] = {_hEventDie, _hSemPending};
  735. HANDLE rghObjDelay[2] = {_hEventDie, _hMutexDelay};
  736. while (TRUE)
  737. {
  738. // Wait for an end event or for a job to do
  739. DWORD dwRet = WaitForMultipleObjects(ARRAYSIZE(rghObjPending), rghObjPending, FALSE, INFINITE);
  740. if (WAIT_OBJECT_0 == dwRet)
  741. {
  742. // Exit thread
  743. break;
  744. }
  745. else
  746. {
  747. #ifdef DEBUG
  748. _EnterCS();
  749. {
  750. ASSERT(0 < _cUndetermined ||
  751. 0 < _cStale ||
  752. 0 < _cDeleted);
  753. }
  754. _LeaveCS();
  755. #endif
  756. // Now wait for an end event or for the delay-calculation mutex
  757. dwRet = WaitForMultipleObjects(ARRAYSIZE(rghObjDelay), rghObjDelay, FALSE, INFINITE);
  758. if (WAIT_OBJECT_0 == dwRet)
  759. {
  760. // Exit thread
  761. break;
  762. }
  763. else
  764. {
  765. // Address deleted entries first
  766. BRFINFOHDR bihdr;
  767. if (_FindNextState(FNS_DELETED, &bihdr))
  768. {
  769. _DeleteCachedName(bihdr.pidl);
  770. ILFree(bihdr.pidl);
  771. }
  772. // Calculate undetermined entries before stale entries
  773. // to fill the view as quickly as possible
  774. else if (_FindNextState(FNS_UNDETERMINED, &bihdr) ||
  775. _FindNextState(FNS_STALE, &bihdr))
  776. {
  777. _CalcCachedName(bihdr.pidl, &bihdr.bi);
  778. #if 1
  779. // ugly way
  780. ShellFolderView_RefreshObject(_hwndMain, &bihdr.pidl);
  781. #else
  782. // right way, but we don't have _punkSite, here, that is on another thread!
  783. IShellFolderView *psfv;
  784. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IShellFolderView, &psfv))))
  785. {
  786. UINT uScratch;
  787. psfv->RefreshObject(&bihdr.pidl, &uScratch);
  788. psfv->Release();
  789. }
  790. #endif
  791. ILFree(bihdr.pidl);
  792. }
  793. else
  794. {
  795. ASSERT(0); // Should never get here
  796. }
  797. ReleaseMutex(_hMutexDelay);
  798. }
  799. }
  800. }
  801. return 0;
  802. }
  803. DWORD CALLBACK CBriefcase::_CalcDetailsThreadProc(void *pv)
  804. {
  805. return ((CBriefcase *)pv)->_CalcDetailsThread();
  806. }
  807. // IShellFolder2::GetDetailsOf
  808. STDMETHODIMP CBriefcase::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails)
  809. {
  810. HRESULT hr = S_OK;
  811. TCHAR szTemp[MAX_PATH];
  812. LPCIDFOLDER pidf = _IsValidID(pidl);
  813. pDetails->str.uType = STRRET_CSTR;
  814. pDetails->str.cStr[0] = 0;
  815. if (!pidf)
  816. {
  817. hr = GetDetailsOfInfo(s_briefcase_cols, ARRAYSIZE(s_briefcase_cols), iColumn, pDetails);
  818. }
  819. else
  820. {
  821. switch (iColumn)
  822. {
  823. case ICOL_BRIEFCASE_NAME:
  824. _CopyName(pidf, szTemp, ARRAYSIZE(szTemp));
  825. hr = StringToStrRet(szTemp, &pDetails->str);
  826. break;
  827. case ICOL_BRIEFCASE_ORIGIN:
  828. case ICOL_BRIEFCASE_STATUS:
  829. // only works if the view callback has set us up for this
  830. if (_pbrfstg)
  831. {
  832. BRFINFO bi;
  833. // Did we find extra info for this file or
  834. // was the new item added to the extra info list?
  835. if (_FindCachedName(pidl, &bi) ||
  836. _AddCachedName(pidl, &bi))
  837. {
  838. LPTSTR psz = ICOL_BRIEFCASE_ORIGIN == iColumn ? bi.szOrigin : bi.szStatus;
  839. hr = StringToStrRet(psz, &pDetails->str);
  840. }
  841. }
  842. break;
  843. case ICOL_BRIEFCASE_SIZE:
  844. if (!_IsFolder(pidf))
  845. {
  846. StrFormatKBSize(pidf->dwSize, szTemp, ARRAYSIZE(szTemp));
  847. hr = StringToStrRet(szTemp, &pDetails->str);
  848. }
  849. break;
  850. case ICOL_BRIEFCASE_TYPE:
  851. _GetTypeNameBuf(pidf, szTemp, ARRAYSIZE(szTemp));
  852. hr = StringToStrRet(szTemp, &pDetails->str);
  853. break;
  854. case ICOL_BRIEFCASE_MODIFIED:
  855. DosTimeToDateTimeString(pidf->dateModified, pidf->timeModified, szTemp, ARRAYSIZE(szTemp), pDetails->fmt & LVCFMT_DIRECTION_MASK);
  856. hr = StringToStrRet(szTemp, &pDetails->str);
  857. break;
  858. }
  859. }
  860. return hr;
  861. }
  862. // IShellFolder2::MapColumnToSCID
  863. STDMETHODIMP CBriefcase::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
  864. {
  865. return MapColumnToSCIDImpl(s_briefcase_cols, ARRAYSIZE(s_briefcase_cols), iColumn, pscid);
  866. }
  867. // IShellFolder::CompareIDs
  868. STDMETHODIMP CBriefcase::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  869. {
  870. LPCIDFOLDER pidf1 = _IsValidID(pidl1);
  871. LPCIDFOLDER pidf2 = _IsValidID(pidl2);
  872. if (!pidf1 || !pidf2)
  873. {
  874. return E_INVALIDARG;
  875. }
  876. HRESULT hr = _CompareFolderness(pidf1, pidf2);
  877. if (hr != ResultFromShort(0))
  878. return hr;
  879. switch (lParam & SHCIDS_COLUMNMASK)
  880. {
  881. case ICOL_BRIEFCASE_SIZE:
  882. if (pidf1->dwSize < pidf2->dwSize)
  883. return ResultFromShort(-1);
  884. if (pidf1->dwSize > pidf2->dwSize)
  885. return ResultFromShort(1);
  886. goto DoDefault;
  887. case ICOL_BRIEFCASE_TYPE:
  888. hr = _CompareFileTypes(pidf1, pidf2);
  889. if (!hr)
  890. goto DoDefault;
  891. break;
  892. case ICOL_BRIEFCASE_MODIFIED:
  893. hr = _CompareModifiedDate(pidf1, pidf2);
  894. if (!hr)
  895. goto DoDefault;
  896. break;
  897. case ICOL_BRIEFCASE_NAME:
  898. // We need to treat this differently from others bacause
  899. // pidf1/2 might not be simple.
  900. hr = CFSFolder::_CompareNames(pidf1, pidf2, TRUE, FALSE);
  901. // REVIEW: (Possible performance gain with some extra code)
  902. // We should probably avoid bindings by walking down
  903. // the IDList here instead of calling this helper function.
  904. //
  905. if (hr == ResultFromShort(0))
  906. {
  907. hr = ILCompareRelIDs((IShellFolder *)this, pidl1, pidl2, lParam);
  908. }
  909. goto DoDefaultModification;
  910. case ICOL_BRIEFCASE_ORIGIN:
  911. case ICOL_BRIEFCASE_STATUS:
  912. {
  913. BRFINFO bi1, bi2;
  914. BOOL bVal1 = _FindCachedName(pidl1, &bi1);
  915. BOOL bVal2 = _FindCachedName(pidl2, &bi2);
  916. // Do we have this info in our cache?
  917. if (!bVal1 || !bVal2)
  918. {
  919. // No; one or both of them are missing. Have unknowns gravitate
  920. // to the bottom of the list.
  921. // (Don't bother adding them)
  922. if (!bVal1 && !bVal2)
  923. hr = ResultFromShort(0);
  924. else if (!bVal1)
  925. hr = ResultFromShort(1);
  926. else
  927. hr = ResultFromShort(-1);
  928. }
  929. else
  930. {
  931. // Found the info; do a comparison
  932. if (ICOL_BRIEFCASE_ORIGIN == (lParam & SHCIDS_COLUMNMASK))
  933. {
  934. hr = ResultFromShort(lstrcmp(bi1.szOrigin, bi2.szOrigin));
  935. }
  936. else
  937. {
  938. ASSERT(ICOL_BRIEFCASE_STATUS == (lParam & SHCIDS_COLUMNMASK));
  939. hr = ResultFromShort(lstrcmp(bi1.szStatus, bi2.szStatus));
  940. }
  941. }
  942. }
  943. break;
  944. default:
  945. DoDefault:
  946. // Sort it based on the primary (long) name -- ignore case.
  947. {
  948. TCHAR szName1[MAX_PATH], szName2[MAX_PATH];
  949. _CopyName(pidf1, szName1, ARRAYSIZE(szName1));
  950. _CopyName(pidf2, szName2, ARRAYSIZE(szName2));
  951. hr = ResultFromShort(lstrcmpi(szName1, szName2));
  952. }
  953. DoDefaultModification:
  954. if (hr == S_OK && (lParam & SHCIDS_ALLFIELDS))
  955. {
  956. // Must sort by modified date to pick up any file changes!
  957. hr = _CompareModifiedDate(pidf1, pidf2);
  958. if (!hr)
  959. hr = _CompareAttribs(pidf1, pidf2);
  960. }
  961. }
  962. return hr;
  963. }
  964. // This function creates an instance of IShellView.
  965. HRESULT CBriefcase::_CreateView(HWND hwnd, IShellView **ppsv)
  966. {
  967. *ppsv = NULL; // assume failure
  968. HRESULT hr;
  969. CBriefcaseViewCB *pvcb = new CBriefcaseViewCB(this);
  970. if (pvcb)
  971. {
  972. hr = pvcb->_InitStgForDetails();
  973. if (SUCCEEDED(hr))
  974. {
  975. SFV_CREATE sSFV = {0};
  976. hr = pvcb->QueryInterface(IID_PPV_ARG(IShellFolderViewCB, &sSFV.psfvcb));
  977. if (SUCCEEDED(hr))
  978. {
  979. sSFV.cbSize = sizeof(sSFV);
  980. sSFV.pshf = (IShellFolder *)this;
  981. hr = SHCreateShellFolderView(&sSFV, ppsv);
  982. }
  983. }
  984. pvcb->Release();
  985. }
  986. else
  987. {
  988. hr = E_OUTOFMEMORY;
  989. }
  990. return hr;
  991. }
  992. // IShellFolder::CreateViewObject
  993. STDMETHODIMP CBriefcase::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
  994. {
  995. HRESULT hr;
  996. if (IsEqualIID(riid, IID_IShellView))
  997. {
  998. hr = _CreateView(hwnd, (IShellView **)ppv);
  999. }
  1000. else
  1001. {
  1002. // delegate to base class
  1003. hr = CFSFolder::CreateViewObject(hwnd, riid, ppv);
  1004. }
  1005. ASSERT(FAILED(hr) ? (NULL == *ppv) : TRUE);
  1006. return hr;
  1007. }
  1008. // IShellFolder::GetAttributesOf
  1009. STDMETHODIMP CBriefcase::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG * prgfInOut)
  1010. {
  1011. // Validate this pidl?
  1012. if (*prgfInOut & SFGAO_VALIDATE)
  1013. {
  1014. // Yes; dirty the briefcase storage entry by sending an update
  1015. // notification
  1016. DebugMsg(DM_TRACE, TEXT("Briefcase: Receiving F5, dirty entire briefcase storage"));
  1017. _AllNamesAreStale();
  1018. }
  1019. // delegate to base
  1020. return CFSFolder::GetAttributesOf(cidl, apidl, prgfInOut);
  1021. }
  1022. // IShellFolder::GetUIObjectOf
  1023. STDMETHODIMP CBriefcase::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
  1024. REFIID riid, UINT *prgfInOut, void **ppv)
  1025. {
  1026. HRESULT hr;
  1027. if (cidl > 0 && IsEqualIID(riid, IID_IDataObject))
  1028. {
  1029. // Create an IDataObject interface instance with our
  1030. // own vtable because we support the CFSTR_BRIEFOBJECT clipboard format
  1031. hr = CBrfData_CreateDataObj(_pidl, cidl, (LPCITEMIDLIST *)apidl, (IDataObject **)ppv);
  1032. }
  1033. else
  1034. {
  1035. // delegate to base class
  1036. hr = CFSFolder::GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv);
  1037. }
  1038. return hr;
  1039. }
  1040. // CFSBrfFolder constructor
  1041. STDAPI CFSBrfFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  1042. {
  1043. HRESULT hr;
  1044. CBriefcase *pbf = new CBriefcase(punkOuter);
  1045. if (pbf)
  1046. {
  1047. hr = pbf->Init();
  1048. if (SUCCEEDED(hr))
  1049. {
  1050. hr = pbf->QueryInterface(riid, ppv);
  1051. }
  1052. pbf->Release();
  1053. }
  1054. else
  1055. {
  1056. *ppv = NULL;
  1057. hr = E_OUTOFMEMORY;
  1058. }
  1059. return hr;
  1060. }
  1061. const TBBUTTON c_tbBrfCase[] = {
  1062. { 0, FSIDM_UPDATEALL, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
  1063. { 1, FSIDM_UPDATESELECTION, 0, TBSTYLE_BUTTON, {0,0}, 0L, -1 },
  1064. { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP , {0,0}, 0L, -1 },
  1065. };
  1066. #define BRFVIEW_EVENTS \
  1067. SHCNE_DISKEVENTS | \
  1068. SHCNE_ASSOCCHANGED | \
  1069. SHCNE_GLOBALEVENTS
  1070. HRESULT CBriefcaseViewCB::OnWINDOWCREATED(DWORD pv, HWND hwndView)
  1071. {
  1072. SHChangeNotifyEntry fsne;
  1073. ASSERT(_pbrfstg && _hwndMain && _hMutexDelay); // from the init call
  1074. // view hands folder info needed to details (status, sync path)
  1075. _pfolder->_InitDetailsInfoAndThread(_pbrfstg, _hwndMain, _hMutexDelay);
  1076. // Register an extra SHChangeNotifyRegister for our pidl to try to catch things
  1077. // like UpdateDir
  1078. fsne.pidl = _FolderPidl();
  1079. fsne.fRecursive = FALSE;
  1080. _uSCNRExtra = SHChangeNotifyRegister(hwndView, SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel,
  1081. SHCNE_DISKEVENTS, WM_DSV_FSNOTIFY, 1, &fsne);
  1082. return S_OK;
  1083. }
  1084. HRESULT CBriefcaseViewCB::OnWINDOWDESTROY(DWORD pv, HWND wP)
  1085. {
  1086. _pfolder->_Free();
  1087. // need to release pbrfstg as well
  1088. if (_pbrfstg)
  1089. {
  1090. _pbrfstg->Release();
  1091. _pbrfstg = NULL;
  1092. _hMutexDelay = NULL; // invalidate our alias
  1093. }
  1094. if (_uSCNRExtra)
  1095. {
  1096. SHChangeNotifyDeregister(_uSCNRExtra);
  1097. _uSCNRExtra = 0;
  1098. }
  1099. return S_OK;
  1100. }
  1101. HRESULT CBriefcaseViewCB::OnMergeMenu(DWORD pv, QCMINFO *pinfo)
  1102. {
  1103. // Merge the briefcase menu onto the menu that CDefView created.
  1104. if (pinfo->hmenu)
  1105. {
  1106. HMENU hmSync = LoadMenu(HINST_THISDLL, MAKEINTRESOURCE(POPUP_BRIEFCASE));
  1107. if (hmSync)
  1108. {
  1109. Shell_MergeMenus(pinfo->hmenu, hmSync, pinfo->indexMenu,
  1110. pinfo->idCmdFirst, pinfo->idCmdLast, MM_SUBMENUSHAVEIDS);
  1111. DestroyMenu(hmSync);
  1112. }
  1113. }
  1114. return S_OK;
  1115. }
  1116. HRESULT CBriefcaseViewCB::_GetSelectedObjects(IDataObject **ppdtobj)
  1117. {
  1118. IFolderView *pfv;
  1119. HRESULT hr = IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv));
  1120. if (SUCCEEDED(hr))
  1121. {
  1122. hr = pfv->Items(SVGIO_SELECTION, IID_PPV_ARG(IDataObject, ppdtobj));
  1123. pfv->Release();
  1124. }
  1125. return hr;
  1126. }
  1127. HRESULT CBriefcaseViewCB::OnINVOKECOMMAND(DWORD pv, UINT uID)
  1128. {
  1129. IDataObject *pdtobj;
  1130. switch (uID)
  1131. {
  1132. case FSIDM_UPDATEALL:
  1133. // Update the entire briefcase
  1134. if (SUCCEEDED(SHGetUIObjectFromFullPIDL(_pidlRoot, NULL, IID_PPV_ARG(IDataObject, &pdtobj))))
  1135. {
  1136. _pbrfstg->UpdateObject(pdtobj, _hwndMain);
  1137. pdtobj->Release();
  1138. }
  1139. break;
  1140. case FSIDM_UPDATESELECTION:
  1141. // Update the selected objects
  1142. if (SUCCEEDED(_GetSelectedObjects(&pdtobj)))
  1143. {
  1144. _pbrfstg->UpdateObject(pdtobj, _hwndMain);
  1145. pdtobj->Release();
  1146. }
  1147. break;
  1148. case FSIDM_SPLIT:
  1149. // Split the selected objects
  1150. if (SUCCEEDED(_GetSelectedObjects(&pdtobj)))
  1151. {
  1152. _pbrfstg->ReleaseObject(pdtobj, _hwndMain);
  1153. pdtobj->Release();
  1154. }
  1155. break;
  1156. }
  1157. return S_OK;
  1158. }
  1159. HRESULT CBriefcaseViewCB::OnGetHelpOrTooltipText(BOOL bHelp, UINT wPl, UINT cch, LPTSTR psz)
  1160. {
  1161. LoadString(HINST_THISDLL, wPl + (bHelp ? IDS_MH_FSIDM_FIRST : IDS_TT_FSIDM_FIRST), psz, cch);
  1162. return S_OK;
  1163. }
  1164. int CBriefcaseViewCB::_GetSelectedCount()
  1165. {
  1166. int cItems = 0;
  1167. IFolderView *pfv;
  1168. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv))))
  1169. {
  1170. pfv->ItemCount(SVGIO_SELECTION, &cItems);
  1171. pfv->Release();
  1172. }
  1173. return cItems;
  1174. }
  1175. HRESULT CBriefcaseViewCB::OnINITMENUPOPUP(DWORD pv, UINT idCmdFirst, UINT nIndex, HMENU hmenu)
  1176. {
  1177. BOOL bEnabled = _GetSelectedCount() > 0;
  1178. EnableMenuItem(hmenu, idCmdFirst+FSIDM_UPDATESELECTION, bEnabled ? MF_ENABLED : MF_GRAYED);
  1179. EnableMenuItem(hmenu, idCmdFirst+FSIDM_SPLIT, bEnabled ? MF_ENABLED : MF_GRAYED);
  1180. return S_OK;
  1181. }
  1182. HRESULT CBriefcaseViewCB::OnGETBUTTONINFO(DWORD pv, TBINFO* ptbinfo)
  1183. {
  1184. ptbinfo->cbuttons = ARRAYSIZE(c_tbBrfCase);
  1185. ptbinfo->uFlags = TBIF_PREPEND;
  1186. return S_OK;
  1187. }
  1188. HRESULT CBriefcaseViewCB::OnGETBUTTONS(DWORD pv, UINT idCmdFirst, UINT wPh, TBBUTTON *ptbbutton)
  1189. {
  1190. IShellBrowser* psb;
  1191. HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb));
  1192. if (SUCCEEDED(hr))
  1193. {
  1194. LRESULT iBtnOffset;
  1195. TBADDBITMAP ab;
  1196. // add the toolbar button bitmap, get it's offset
  1197. ab.hInst = HINST_THISDLL;
  1198. ab.nID = IDB_BRF_TB_SMALL; // std bitmaps
  1199. psb->SendControlMsg(FCW_TOOLBAR, TB_ADDBITMAP, 2, (LPARAM)&ab, &iBtnOffset);
  1200. for (int i = 0; i < ARRAYSIZE(c_tbBrfCase); i++)
  1201. {
  1202. ptbbutton[i] = c_tbBrfCase[i];
  1203. if (!(c_tbBrfCase[i].fsStyle & TBSTYLE_SEP))
  1204. {
  1205. ptbbutton[i].idCommand += idCmdFirst;
  1206. ptbbutton[i].iBitmap += (int) iBtnOffset;
  1207. }
  1208. }
  1209. psb->Release();
  1210. }
  1211. return S_OK;
  1212. }
  1213. HRESULT CBriefcaseViewCB::OnSELCHANGE(DWORD pv, UINT idCmdFirst, UINT wPh, SFVM_SELCHANGE_DATA*lP)
  1214. {
  1215. IShellBrowser* psb;
  1216. HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb));
  1217. if (SUCCEEDED(hr))
  1218. {
  1219. psb->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON,
  1220. idCmdFirst + FSIDM_UPDATESELECTION,
  1221. (LPARAM)(_GetSelectedCount() > 0), NULL);
  1222. psb->Release();
  1223. }
  1224. return E_FAIL; // (we did not update the status area)
  1225. }
  1226. HRESULT CBriefcaseViewCB::OnQUERYFSNOTIFY(DWORD pv, SHChangeNotifyEntry *pfsne)
  1227. {
  1228. // Register to receive global events
  1229. pfsne->pidl = NULL;
  1230. pfsne->fRecursive = TRUE;
  1231. return NOERROR;
  1232. }
  1233. HRESULT CBriefcaseViewCB::_HandleFSNotifyForDefView(LPARAM lEvent, LPCITEMIDLIST * ppidl, LPTSTR pszBuf)
  1234. {
  1235. HRESULT hr;
  1236. switch (lEvent)
  1237. {
  1238. case SHCNE_RENAMEITEM:
  1239. case SHCNE_RENAMEFOLDER:
  1240. if (!ILIsParent(_FolderPidl(), ppidl[0], TRUE))
  1241. {
  1242. // move to this folder
  1243. hr = _HandleFSNotifyForDefView(SHCNE_CREATE, &ppidl[1], pszBuf);
  1244. }
  1245. else if (!ILIsParent(_FolderPidl(), ppidl[1], TRUE))
  1246. {
  1247. // move from this folder
  1248. hr = _HandleFSNotifyForDefView(SHCNE_DELETE, &ppidl[0], pszBuf);
  1249. }
  1250. else
  1251. {
  1252. // have the defview handle it
  1253. _pfolder->_CachedNameIsStale(ppidl[0], TRUE);
  1254. hr = NOERROR;
  1255. }
  1256. break;
  1257. case SHCNE_DELETE:
  1258. case SHCNE_RMDIR:
  1259. _pfolder->_CachedNameIsStale(ppidl[0], TRUE);
  1260. hr = NOERROR;
  1261. break;
  1262. default:
  1263. hr = NOERROR;
  1264. break;
  1265. }
  1266. return hr;
  1267. }
  1268. // Converts a shell change notify event to a briefcase storage event.
  1269. LONG NOEFromSHCNE(LPARAM lEvent)
  1270. {
  1271. switch (lEvent)
  1272. {
  1273. case SHCNE_RENAMEITEM: return NOE_RENAME;
  1274. case SHCNE_RENAMEFOLDER: return NOE_RENAMEFOLDER;
  1275. case SHCNE_CREATE: return NOE_CREATE;
  1276. case SHCNE_MKDIR: return NOE_CREATEFOLDER;
  1277. case SHCNE_DELETE: return NOE_DELETE;
  1278. case SHCNE_RMDIR: return NOE_DELETEFOLDER;
  1279. case SHCNE_UPDATEITEM: return NOE_DIRTY;
  1280. case SHCNE_UPDATEDIR: return NOE_DIRTYFOLDER;
  1281. default: return 0;
  1282. }
  1283. }
  1284. HRESULT CBriefcaseViewCB::OnFSNOTIFY(DWORD pv, LPCITEMIDLIST *ppidl, LPARAM lEvent)
  1285. {
  1286. HRESULT hr;
  1287. TCHAR szPath[MAX_PATH * 2];
  1288. // we are in the process of being freed, but changenotify's can still come in because we are not freed atomically
  1289. if (!_pbrfstg)
  1290. {
  1291. return S_FALSE;
  1292. }
  1293. if (lEvent == SHCNE_UPDATEIMAGE || lEvent == SHCNE_FREESPACE)
  1294. {
  1295. return S_FALSE;
  1296. }
  1297. if (ppidl && !ILIsEmpty(ppidl[0]) && SHGetPathFromIDList(ppidl[0], szPath))
  1298. {
  1299. UINT uFlags;
  1300. LONG lEventNOE;
  1301. if ((SHCNE_RENAMEFOLDER == lEvent) || (SHCNE_RENAMEITEM == lEvent))
  1302. {
  1303. ASSERT(ppidl[1]);
  1304. ASSERT(ARRAYSIZE(szPath) >= lstrlen(szPath)*2); // rough estimate
  1305. // Tack the new name after the old name, separated by the null
  1306. SHGetPathFromIDList(ppidl[1], &szPath[lstrlen(szPath)+1]);
  1307. }
  1308. // Tell the briefcase the path has potentially changed
  1309. lEventNOE = NOEFromSHCNE(lEvent);
  1310. _pbrfstg->Notify(szPath, lEventNOE, &uFlags, _hwndMain);
  1311. // Was this item marked?
  1312. if (uFlags & NF_ITEMMARKED)
  1313. {
  1314. // Yes; mark it stale in the expensive cache
  1315. _pfolder->_CachedNameIsStale(ppidl[0], FALSE);
  1316. }
  1317. // Does the window need to be refreshed?
  1318. if (uFlags & NF_REDRAWWINDOW)
  1319. {
  1320. // Yes
  1321. IShellView *psv;
  1322. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IShellView, &psv))))
  1323. {
  1324. psv->Refresh();
  1325. psv->Release();
  1326. }
  1327. }
  1328. // Did this event occur in this folder?
  1329. if (NULL == ppidl ||
  1330. ILIsParent(_FolderPidl(), ppidl[0], TRUE) ||
  1331. (((SHCNE_RENAMEITEM == lEvent) || (SHCNE_RENAMEFOLDER == lEvent)) && ILIsParent(_FolderPidl(), ppidl[1], TRUE)) ||
  1332. (SHCNE_UPDATEDIR == lEvent && ILIsEqual(_FolderPidl(), ppidl[0])))
  1333. {
  1334. // Yes; deal with it
  1335. hr = _HandleFSNotifyForDefView(lEvent, ppidl, szPath);
  1336. }
  1337. else
  1338. {
  1339. // No
  1340. hr = S_FALSE;
  1341. }
  1342. }
  1343. else
  1344. {
  1345. // ASSERT(0);
  1346. hr = S_FALSE;
  1347. }
  1348. return hr;
  1349. }
  1350. HRESULT CBriefcaseViewCB::OnQUERYCOPYHOOK(DWORD pv)
  1351. {
  1352. return S_OK;
  1353. }
  1354. HRESULT CBriefcaseViewCB::OnNOTIFYCOPYHOOK(DWORD pv, COPYHOOKINFO *pchi)
  1355. {
  1356. HRESULT hr = NOERROR;
  1357. // Is this a pertinent operation?
  1358. if (FO_MOVE == pchi->wFunc ||
  1359. FO_RENAME == pchi->wFunc ||
  1360. FO_DELETE == pchi->wFunc)
  1361. {
  1362. // Yes; don't allow the briefcase root or a parent folder to get moved
  1363. // while the briefcase is still open. (The database is locked while
  1364. // the briefcase is open, and will fail the move/rename operation
  1365. // in an ugly way.)
  1366. LPITEMIDLIST pidl = ILCreateFromPath(pchi->pszSrcFile);
  1367. if (pidl)
  1368. {
  1369. // Is the folder that is being moved or renamed a parent or equal
  1370. // of the Briefcase root?
  1371. if (ILIsParent(pidl, _pidlRoot, FALSE) ||
  1372. ILIsEqual(pidl, _pidlRoot))
  1373. {
  1374. // Yes; don't allow it until the briefcase is closed.
  1375. int ids;
  1376. if (FO_MOVE == pchi->wFunc ||
  1377. FO_RENAME == pchi->wFunc)
  1378. {
  1379. ids = IDS_MOVEBRIEFCASE;
  1380. }
  1381. else
  1382. {
  1383. ASSERT(FO_DELETE == pchi->wFunc);
  1384. ids = IDS_DELETEBRIEFCASE;
  1385. }
  1386. ShellMessageBox(HINST_THISDLL, _hwndMain,
  1387. MAKEINTRESOURCE(ids), NULL, MB_OK | MB_ICONINFORMATION);
  1388. hr = IDCANCEL;
  1389. }
  1390. ILFree(pidl);
  1391. }
  1392. }
  1393. return hr;
  1394. }
  1395. HRESULT CBriefcaseViewCB::OnINSERTITEM(DWORD pv, LPCITEMIDLIST pidl)
  1396. {
  1397. HRESULT hr;
  1398. TCHAR szPath[MAX_PATH];
  1399. if (SHGetPathFromIDList(pidl, szPath))
  1400. {
  1401. // Always hide the desktop.ini and the database file.
  1402. LPTSTR pszName = PathFindFileName(szPath);
  1403. if (0 == lstrcmpi(pszName, c_szDesktopIni) ||
  1404. 0 == lstrcmpi(pszName, _szDBName))
  1405. hr = S_FALSE; // don't add
  1406. else
  1407. hr = S_OK;
  1408. }
  1409. else
  1410. hr = S_OK; // Let it be added...
  1411. return hr;
  1412. }
  1413. HRESULT CBriefcaseViewCB::OnDEFVIEWMODE(DWORD pv, FOLDERVIEWMODE*lP)
  1414. {
  1415. *lP = FVM_DETAILS;
  1416. return S_OK;
  1417. }
  1418. HRESULT CBriefcaseViewCB::OnGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA * phtd)
  1419. {
  1420. HRESULT hr;
  1421. if (IsOS(OS_ANYSERVER))
  1422. {
  1423. hr = StringCchCopy(phtd->wszHelpFile, ARRAYSIZE(phtd->wszHelpFile), L"brief.chm");
  1424. }
  1425. else
  1426. {
  1427. hr = StringCchCopy(phtd->wszHelpTopic, ARRAYSIZE(phtd->wszHelpTopic), L"hcp://services/subsite?node=Unmapped/Briefcase");
  1428. }
  1429. return hr;
  1430. }
  1431. CBriefcaseViewCB::CBriefcaseViewCB(CBriefcase *pfolder) : CBaseShellFolderViewCB(pfolder->_pidl, BRFVIEW_EVENTS), _pfolder(pfolder)
  1432. {
  1433. _pfolder->AddRef();
  1434. }
  1435. CBriefcaseViewCB::~CBriefcaseViewCB()
  1436. {
  1437. if (_pbrfstg)
  1438. _pbrfstg->Release();
  1439. if (_pidlRoot)
  1440. ILFree(_pidlRoot);
  1441. _pfolder->Release();
  1442. }
  1443. HRESULT CBriefcaseViewCB::_InitStgForDetails()
  1444. {
  1445. ASSERT(NULL == _pbrfstg);
  1446. HRESULT hr = CreateBrfStgFromIDList(_FolderPidl(), _hwndMain, &_pbrfstg);
  1447. if (SUCCEEDED(hr))
  1448. {
  1449. ASSERT(NULL == _hMutexDelay);
  1450. _pbrfstg->GetExtraInfo(NULL, GEI_DELAYHANDLE, 0, (LPARAM)&_hMutexDelay);
  1451. ASSERT(0 == _szDBName[0]);
  1452. _pbrfstg->GetExtraInfo(NULL, GEI_DATABASENAME, ARRAYSIZE(_szDBName), (LPARAM)_szDBName);
  1453. TCHAR szPath[MAX_PATH];
  1454. hr = _pbrfstg->GetExtraInfo(NULL, GEI_ROOT, (WPARAM)ARRAYSIZE(szPath), (LPARAM)szPath);
  1455. if (SUCCEEDED(hr))
  1456. hr = SHILCreateFromPath(szPath, &_pidlRoot, NULL);
  1457. }
  1458. return hr;
  1459. }
  1460. STDMETHODIMP CBriefcaseViewCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  1461. {
  1462. switch (uMsg)
  1463. {
  1464. HANDLE_MSG(0, SFVM_WINDOWCREATED, OnWINDOWCREATED);
  1465. HANDLE_MSG(0, SFVM_WINDOWDESTROY, OnWINDOWDESTROY);
  1466. HANDLE_MSG(0, SFVM_MERGEMENU, OnMergeMenu);
  1467. HANDLE_MSG(0, SFVM_INVOKECOMMAND, OnINVOKECOMMAND);
  1468. HANDLE_MSG(TRUE , SFVM_GETHELPTEXT , OnGetHelpOrTooltipText);
  1469. HANDLE_MSG(FALSE, SFVM_GETTOOLTIPTEXT, OnGetHelpOrTooltipText);
  1470. HANDLE_MSG(0, SFVM_INITMENUPOPUP, OnINITMENUPOPUP);
  1471. HANDLE_MSG(0, SFVM_GETBUTTONINFO, OnGETBUTTONINFO);
  1472. HANDLE_MSG(0, SFVM_GETBUTTONS, OnGETBUTTONS);
  1473. HANDLE_MSG(0, SFVM_SELCHANGE, OnSELCHANGE);
  1474. HANDLE_MSG(0, SFVM_QUERYFSNOTIFY, OnQUERYFSNOTIFY);
  1475. HANDLE_MSG(0, SFVM_FSNOTIFY, OnFSNOTIFY);
  1476. HANDLE_MSG(0, SFVM_QUERYCOPYHOOK, OnQUERYCOPYHOOK);
  1477. HANDLE_MSG(0, SFVM_NOTIFYCOPYHOOK, OnNOTIFYCOPYHOOK);
  1478. HANDLE_MSG(0, SFVM_INSERTITEM, OnINSERTITEM);
  1479. HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDEFVIEWMODE);
  1480. HANDLE_MSG(0, SFVM_ADDPROPERTYPAGES, SFVCB_OnAddPropertyPages);
  1481. HANDLE_MSG(0, SFVM_GETHELPTOPIC, OnGetHelpTopic);
  1482. HANDLE_MSG(0, SFVM_GETWEBVIEWLAYOUT, OnGetWebViewLayout);
  1483. HANDLE_MSG(0, SFVM_GETWEBVIEWCONTENT, OnGetWebViewContent);
  1484. HANDLE_MSG(0, SFVM_GETWEBVIEWTASKS, OnGetWebViewTasks);
  1485. HANDLE_MSG(0, SFVM_DELAYWINDOWCREATE, OnDELAYWINDOWCREATE);
  1486. default:
  1487. return E_FAIL;
  1488. }
  1489. return NOERROR;
  1490. }
  1491. STDAPI CreateBrfStgFromPath(LPCTSTR pszPath, HWND hwnd, IBriefcaseStg **ppbs)
  1492. {
  1493. IBriefcaseStg *pbrfstg;
  1494. HRESULT hr = CoCreateInstance(CLSID_Briefcase, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IBriefcaseStg, &pbrfstg));
  1495. if (SUCCEEDED(hr))
  1496. {
  1497. hr = pbrfstg->Initialize(pszPath, hwnd);
  1498. if (SUCCEEDED(hr))
  1499. {
  1500. hr = pbrfstg->QueryInterface(IID_PPV_ARG(IBriefcaseStg, ppbs));
  1501. }
  1502. pbrfstg->Release();
  1503. }
  1504. return hr;
  1505. }
  1506. STDAPI CreateBrfStgFromIDList(LPCITEMIDLIST pidl, HWND hwnd, IBriefcaseStg **ppbs)
  1507. {
  1508. HRESULT hr = E_FAIL;
  1509. // Create an instance of IBriefcaseStg
  1510. TCHAR szFolder[MAX_PATH];
  1511. if (SHGetPathFromIDList(pidl, szFolder))
  1512. {
  1513. hr = CreateBrfStgFromPath(szFolder, hwnd, ppbs);
  1514. }
  1515. return hr;
  1516. }
  1517. HRESULT CBriefcaseViewCB::OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData)
  1518. {
  1519. ZeroMemory(pData, sizeof(*pData));
  1520. pData->dwLayout = SFVMWVL_NORMAL | SFVMWVL_FILES;
  1521. return S_OK;
  1522. }
  1523. HRESULT CBriefcaseViewCB::_OnUpdate(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  1524. {
  1525. CBriefcaseViewCB* pThis = (CBriefcaseViewCB*)(void*)pv;
  1526. IDataObject *pdo;
  1527. HRESULT hr = S_OK;
  1528. if (!psiItemArray)
  1529. {
  1530. IFolderView *pfv;
  1531. hr = IUnknown_QueryService(pThis->_punkSite, SID_SFolderView, IID_PPV_ARG(IFolderView, &pfv));
  1532. if (SUCCEEDED(hr))
  1533. {
  1534. hr = pfv->Items(SVGIO_ALLVIEW, IID_PPV_ARG(IDataObject, &pdo));
  1535. pfv->Release();
  1536. }
  1537. }
  1538. else
  1539. {
  1540. hr = psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo));
  1541. }
  1542. if (SUCCEEDED(hr))
  1543. {
  1544. hr = SHInvokeCommandOnDataObject(pThis->_hwndMain, NULL, pdo, 0, "update");
  1545. pdo->Release();
  1546. }
  1547. return hr;
  1548. }
  1549. const WVTASKITEM c_BriefcaseTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_BRIEFCASE, IDS_HEADER_BRIEFCASE_TT);
  1550. const WVTASKITEM c_BriefcaseTaskList[] =
  1551. {
  1552. WVTI_ENTRY_ALL_TITLE(CLSID_NULL, L"shell32.dll", IDS_TASK_UPDATE_ALL, IDS_TASK_UPDATE_ITEM, IDS_TASK_UPDATE_ITEM, IDS_TASK_UPDATE_ITEMS, IDS_TASK_UPDATE_ITEM_TT, IDI_TASK_UPDATEITEMS, NULL, CBriefcaseViewCB::_OnUpdate),
  1553. };
  1554. HRESULT CBriefcaseViewCB::OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData)
  1555. {
  1556. ZeroMemory(pData, sizeof(*pData));
  1557. Create_IUIElement(&c_BriefcaseTaskHeader, &(pData->pSpecialTaskHeader));
  1558. return S_OK;
  1559. }
  1560. HRESULT CBriefcaseViewCB::OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks)
  1561. {
  1562. ZeroMemory(pTasks, sizeof(*pTasks));
  1563. Create_IEnumUICommand((IUnknown*)(void*)this, c_BriefcaseTaskList, ARRAYSIZE(c_BriefcaseTaskList), &pTasks->penumSpecialTasks);
  1564. return S_OK;
  1565. }
  1566. HRESULT CBriefcaseViewCB::OnDELAYWINDOWCREATE(DWORD pv, HWND hwnd)
  1567. {
  1568. HRESULT hr;
  1569. TCHAR szPath[MAX_PATH];
  1570. _pfolder->_GetPath(szPath, ARRAYSIZE(szPath));
  1571. if (!PathAppend(szPath, c_szDesktopIni))
  1572. {
  1573. hr = E_FAIL;
  1574. }
  1575. else
  1576. {
  1577. BOOL bRunWizard = GetPrivateProfileInt(STRINI_CLASSINFO, TEXT("RunWizard"), 0, szPath);
  1578. // Run the wizard?
  1579. if (bRunWizard)
  1580. {
  1581. // work around old bug where FILE_ATTRIBUTE_READONLY was set
  1582. SetFileAttributes(szPath, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
  1583. // Delete the .ini entry
  1584. WritePrivateProfileString(STRINI_CLASSINFO, TEXT("RunWizard"), NULL, szPath);
  1585. SHRunDLLThread(hwnd, TEXT("SYNCUI.DLL,Briefcase_Intro"), SW_SHOW);
  1586. }
  1587. hr = S_OK;
  1588. }
  1589. return hr;
  1590. }