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.

1437 lines
37 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation 1993-1994
  4. //
  5. // File: ibrfext.c
  6. //
  7. // This files contains the IShellExtInit, IShellPropSheetExt and
  8. // IContextMenu interfaces.
  9. //
  10. // History:
  11. // 02-02-94 ScottH Moved from iface.c; added new shell interface support
  12. //
  13. //---------------------------------------------------------------------------
  14. #include "brfprv.h" // common headers
  15. #include <brfcasep.h>
  16. #include "res.h"
  17. #include "recact.h"
  18. // Briefcase extension structure. This is used for IContextMenu
  19. // and PropertySheet binding.
  20. //
  21. typedef struct _BriefExt
  22. {
  23. // We use the sxi also as our IUnknown interface
  24. IShellExtInit sxi; // 1st base class
  25. IContextMenu ctm; // 2nd base class
  26. IShellPropSheetExt spx; // 3rd base class
  27. UINT cRef; // reference count
  28. LPDATAOBJECT pdtobj; // data object
  29. HKEY hkeyProgID; // reg. database key to ProgID
  30. } BriefExt, * PBRIEFEXT;
  31. //---------------------------------------------------------------------------
  32. // IDataObject extraction functions
  33. //---------------------------------------------------------------------------
  34. /*----------------------------------------------------------
  35. Purpose: Return TRUE if the IDataObject knows the special
  36. briefcase file-system object format
  37. Returns: see above
  38. Cond: --
  39. */
  40. BOOL PUBLIC DataObj_KnowsBriefObj(
  41. LPDATAOBJECT pdtobj)
  42. {
  43. HRESULT hres;
  44. FORMATETC fmte = {(CLIPFORMAT)g_cfBriefObj, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  45. // Does this dataobject support briefcase object format?
  46. //
  47. hres = pdtobj->lpVtbl->QueryGetData(pdtobj, &fmte);
  48. return (hres == ResultFromScode(S_OK));
  49. }
  50. /*----------------------------------------------------------
  51. Purpose: Gets the briefcase path from an IDataObject.
  52. Returns: standard
  53. Cond: --
  54. */
  55. HRESULT PUBLIC DataObj_QueryBriefPath(
  56. LPDATAOBJECT pdtobj,
  57. LPTSTR pszBriefPath) // Must be size MAX_PATH
  58. {
  59. HRESULT hres = ResultFromScode(E_FAIL);
  60. FORMATETC fmte = {(CLIPFORMAT)g_cfBriefObj, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  61. STGMEDIUM medium;
  62. ASSERT(pdtobj);
  63. ASSERT(pszBriefPath);
  64. // Does this dataobject support briefcase object format?
  65. //
  66. hres = pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium);
  67. if (SUCCEEDED(hres))
  68. {
  69. PBRIEFOBJ pbo = (PBRIEFOBJ)GlobalLock(medium.hGlobal);
  70. LPTSTR psz = BOBriefcasePath(pbo);
  71. lstrcpy(pszBriefPath, psz);
  72. GlobalUnlock(medium.hGlobal);
  73. MyReleaseStgMedium(&medium);
  74. }
  75. return hres;
  76. }
  77. /*----------------------------------------------------------
  78. Purpose: Gets a single path from an IDataObject.
  79. Returns: standard
  80. S_OK if the object is inside a briefcase
  81. S_FALSE if not
  82. Cond: --
  83. */
  84. HRESULT PUBLIC DataObj_QueryPath(
  85. LPDATAOBJECT pdtobj,
  86. LPTSTR pszPath) // Must be size MAX_PATH
  87. {
  88. HRESULT hres = E_FAIL;
  89. FORMATETC fmteBrief = {(CLIPFORMAT)g_cfBriefObj, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  90. FORMATETC fmteHdrop = {(CLIPFORMAT)CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  91. STGMEDIUM medium;
  92. ASSERT(pdtobj);
  93. ASSERT(pszPath);
  94. // Does this dataobject support briefcase object format?
  95. //
  96. hres = pdtobj->lpVtbl->GetData(pdtobj, &fmteBrief, &medium);
  97. if (SUCCEEDED(hres))
  98. {
  99. // Yup
  100. PBRIEFOBJ pbo = (PBRIEFOBJ)GlobalLock(medium.hGlobal);
  101. LPTSTR psz = BOFileList(pbo);
  102. // Only get first path in list
  103. lstrcpy(pszPath, psz);
  104. GlobalUnlock(medium.hGlobal);
  105. MyReleaseStgMedium(&medium);
  106. hres = S_OK;
  107. }
  108. else
  109. {
  110. // Or does it support hdrops?
  111. hres = pdtobj->lpVtbl->GetData(pdtobj, &fmteHdrop, &medium);
  112. if (SUCCEEDED(hres))
  113. {
  114. // Yup
  115. HDROP hdrop = medium.hGlobal;
  116. // Only get first path in the file list
  117. DragQueryFile(hdrop, 0, pszPath, MAX_PATH);
  118. MyReleaseStgMedium(&medium);
  119. hres = S_FALSE;
  120. }
  121. }
  122. return hres;
  123. }
  124. /*----------------------------------------------------------
  125. Purpose: Gets a file list from an IDataObject. Allocates
  126. ppszList to appropriate size and fills it with
  127. a null-terminated list of paths. It is double-null
  128. terminated.
  129. If ppszList is NULL, then simply get the count of files.
  130. Call DataObj_FreeList to free the ppszList.
  131. Returns: standard
  132. S_OK if the objects are inside a briefcase
  133. S_FALSE if not
  134. Cond: --
  135. */
  136. HRESULT PUBLIC DataObj_QueryFileList(
  137. LPDATAOBJECT pdtobj,
  138. LPTSTR * ppszList, // List of files (may be NULL)
  139. LPUINT puCount) // Count of files
  140. {
  141. HRESULT hres = ResultFromScode(E_FAIL);
  142. FORMATETC fmteBrief = {(CLIPFORMAT)g_cfBriefObj, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  143. FORMATETC fmteHdrop = {(CLIPFORMAT)CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  144. STGMEDIUM medium;
  145. ASSERT(pdtobj);
  146. ASSERT(puCount);
  147. // Does this dataobject support briefcase object format?
  148. //
  149. hres = pdtobj->lpVtbl->GetData(pdtobj, &fmteBrief, &medium);
  150. if (SUCCEEDED(hres))
  151. {
  152. // Yup
  153. PBRIEFOBJ pbo = (PBRIEFOBJ)GlobalLock(medium.hGlobal);
  154. *puCount = BOFileCount(pbo);
  155. hres = ResultFromScode(S_OK);
  156. if (ppszList)
  157. {
  158. *ppszList = GAlloc(BOFileListSize(pbo));
  159. if (*ppszList)
  160. {
  161. BltByte(*ppszList, BOFileList(pbo), BOFileListSize(pbo));
  162. }
  163. else
  164. {
  165. hres = ResultFromScode(E_OUTOFMEMORY);
  166. }
  167. }
  168. GlobalUnlock(medium.hGlobal);
  169. MyReleaseStgMedium(&medium);
  170. goto Leave;
  171. }
  172. // Or does it support hdrops?
  173. //
  174. hres = pdtobj->lpVtbl->GetData(pdtobj, &fmteHdrop, &medium);
  175. if (SUCCEEDED(hres))
  176. {
  177. // Yup
  178. HDROP hdrop = medium.hGlobal;
  179. UINT cFiles = DragQueryFile(hdrop, (UINT)-1, NULL, 0);
  180. UINT cchSize = 0;
  181. UINT i;
  182. *puCount = cFiles;
  183. hres = ResultFromScode(S_FALSE);
  184. if (ppszList)
  185. {
  186. // Determine size we need to allocate
  187. for (i = 0; i < cFiles; i++)
  188. {
  189. cchSize += DragQueryFile(hdrop, i, NULL, 0) + 1;
  190. }
  191. cchSize++; // for extra null
  192. *ppszList = GAlloc(CbFromCch(cchSize));
  193. if (*ppszList)
  194. {
  195. LPTSTR psz = *ppszList;
  196. UINT cch;
  197. // Translate the hdrop into our file list format.
  198. // We know that they really are the same format,
  199. // but to maintain the abstraction layer, we
  200. // pretend we don't.
  201. for (i = 0; i < cFiles; i++)
  202. {
  203. cch = DragQueryFile(hdrop, i, psz, cchSize) + 1;
  204. psz += cch;
  205. cchSize -= cch;
  206. }
  207. *psz = TEXT('\0'); // extra null
  208. }
  209. else
  210. {
  211. hres = ResultFromScode(E_OUTOFMEMORY);
  212. }
  213. }
  214. MyReleaseStgMedium(&medium);
  215. goto Leave;
  216. }
  217. // FEATURE: do we need to query for CF_TEXT?
  218. Leave:
  219. return hres;
  220. }
  221. /*----------------------------------------------------------
  222. Purpose: Frees a file list that was allocated by DataObj_QueryFileList.
  223. Returns: --
  224. Cond: --
  225. */
  226. void PUBLIC DataObj_FreeList(
  227. LPTSTR pszList)
  228. {
  229. GFree(pszList);
  230. }
  231. //---------------------------------------------------------------------------
  232. // BriefExt IUnknown base member functions
  233. //---------------------------------------------------------------------------
  234. /*----------------------------------------------------------
  235. Purpose: IUnknown::QueryInterface
  236. Returns: standard
  237. Cond: --
  238. */
  239. STDMETHODIMP BriefExt_QueryInterface(
  240. LPUNKNOWN punk,
  241. REFIID riid,
  242. LPVOID * ppvOut)
  243. {
  244. PBRIEFEXT this = IToClass(BriefExt, sxi, punk);
  245. HRESULT hres;
  246. if (IsEqualIID(riid, &IID_IUnknown) ||
  247. IsEqualIID(riid, &IID_IShellExtInit))
  248. {
  249. // We use the sxi field as our IUnknown as well
  250. *ppvOut = &this->sxi;
  251. this->cRef++;
  252. hres = NOERROR;
  253. }
  254. else if (IsEqualIID(riid, &IID_IContextMenu))
  255. {
  256. (LPCONTEXTMENU)*ppvOut = &this->ctm;
  257. this->cRef++;
  258. hres = NOERROR;
  259. }
  260. else if (IsEqualIID(riid, &IID_IShellPropSheetExt))
  261. {
  262. (LPSHELLPROPSHEETEXT)*ppvOut = &this->spx;
  263. this->cRef++;
  264. hres = NOERROR;
  265. }
  266. else
  267. {
  268. *ppvOut = NULL;
  269. hres = ResultFromScode(E_NOINTERFACE);
  270. }
  271. return hres;
  272. }
  273. /*----------------------------------------------------------
  274. Purpose: IUnknown::AddRef
  275. Returns: new reference count
  276. Cond: --
  277. */
  278. STDMETHODIMP_(UINT) BriefExt_AddRef(
  279. LPUNKNOWN punk)
  280. {
  281. PBRIEFEXT this = IToClass(BriefExt, sxi, punk);
  282. return ++this->cRef;
  283. }
  284. /*----------------------------------------------------------
  285. Purpose: IUnknown::Release
  286. Returns: new reference count
  287. Cond: --
  288. */
  289. STDMETHODIMP_(UINT) BriefExt_Release(
  290. LPUNKNOWN punk)
  291. {
  292. PBRIEFEXT this = IToClass(BriefExt, sxi, punk);
  293. if (--this->cRef)
  294. {
  295. return this->cRef;
  296. }
  297. if (this->pdtobj)
  298. {
  299. this->pdtobj->lpVtbl->Release(this->pdtobj);
  300. }
  301. if (this->hkeyProgID)
  302. {
  303. RegCloseKey(this->hkeyProgID);
  304. }
  305. GFree(this);
  306. ENTEREXCLUSIVE();
  307. {
  308. DecBusySemaphore(); // Decrement the reference count to the DLL
  309. }
  310. LEAVEEXCLUSIVE();
  311. return 0;
  312. }
  313. //---------------------------------------------------------------------------
  314. // BriefExt IShellExtInit member functions
  315. //---------------------------------------------------------------------------
  316. /*----------------------------------------------------------
  317. Purpose: IShellExtInit::QueryInterface
  318. Returns: standard
  319. Cond: --
  320. */
  321. STDMETHODIMP BriefExt_SXI_QueryInterface(
  322. LPSHELLEXTINIT psxi,
  323. REFIID riid,
  324. LPVOID * ppvOut)
  325. {
  326. return BriefExt_QueryInterface((LPUNKNOWN)psxi, riid, ppvOut);
  327. }
  328. /*----------------------------------------------------------
  329. Purpose: IShellExtInit::AddRef
  330. Returns: new reference count
  331. Cond: --
  332. */
  333. STDMETHODIMP_(UINT) BriefExt_SXI_AddRef(
  334. LPSHELLEXTINIT psxi)
  335. {
  336. return BriefExt_AddRef((LPUNKNOWN)psxi);
  337. }
  338. /*----------------------------------------------------------
  339. Purpose: IShellExtInit::Release
  340. Returns: new reference count
  341. Cond: --
  342. */
  343. STDMETHODIMP_(UINT) BriefExt_SXI_Release(
  344. LPSHELLEXTINIT psxi)
  345. {
  346. return BriefExt_Release((LPUNKNOWN)psxi);
  347. }
  348. /*----------------------------------------------------------
  349. Purpose: IShellExtInit::Initialize
  350. Returns: standard
  351. Cond: --
  352. */
  353. STDMETHODIMP BriefExt_SXI_Initialize(
  354. LPSHELLEXTINIT psxi,
  355. LPCITEMIDLIST pidlFolder,
  356. LPDATAOBJECT pdtobj,
  357. HKEY hkeyProgID)
  358. {
  359. PBRIEFEXT this = IToClass(BriefExt, sxi, psxi);
  360. // Initialize can be called more than once.
  361. //
  362. if (this->pdtobj)
  363. {
  364. this->pdtobj->lpVtbl->Release(this->pdtobj);
  365. }
  366. if (this->hkeyProgID)
  367. {
  368. RegCloseKey(this->hkeyProgID);
  369. }
  370. // Duplicate the pdtobj pointer
  371. if (pdtobj)
  372. {
  373. this->pdtobj = pdtobj;
  374. pdtobj->lpVtbl->AddRef(pdtobj);
  375. }
  376. // Duplicate the handle
  377. if (hkeyProgID)
  378. {
  379. RegOpenKeyEx(hkeyProgID, NULL, 0L, MAXIMUM_ALLOWED, &this->hkeyProgID);
  380. }
  381. return NOERROR;
  382. }
  383. //---------------------------------------------------------------------------
  384. // BriefExt IContextMenu member functions
  385. //---------------------------------------------------------------------------
  386. /*----------------------------------------------------------
  387. Purpose: IContextMenu::QueryInterface
  388. Returns: standard
  389. Cond: --
  390. */
  391. STDMETHODIMP BriefExt_CM_QueryInterface(
  392. LPCONTEXTMENU pctm,
  393. REFIID riid,
  394. LPVOID * ppvOut)
  395. {
  396. PBRIEFEXT this = IToClass(BriefExt, ctm, pctm);
  397. return BriefExt_QueryInterface((LPUNKNOWN)&this->sxi, riid, ppvOut);
  398. }
  399. /*----------------------------------------------------------
  400. Purpose: IContextMenu::AddRef
  401. Returns: new reference count
  402. Cond: --
  403. */
  404. STDMETHODIMP_(UINT) BriefExt_CM_AddRef(
  405. LPCONTEXTMENU pctm)
  406. {
  407. PBRIEFEXT this = IToClass(BriefExt, ctm, pctm);
  408. return BriefExt_AddRef((LPUNKNOWN)&this->sxi);
  409. }
  410. /*----------------------------------------------------------
  411. Purpose: IContextMenu::Release
  412. Returns: new reference count
  413. Cond: --
  414. */
  415. STDMETHODIMP_(UINT) BriefExt_CM_Release(
  416. LPCONTEXTMENU pctm)
  417. {
  418. PBRIEFEXT this = IToClass(BriefExt, ctm, pctm);
  419. return BriefExt_Release((LPUNKNOWN)&this->sxi);
  420. }
  421. /*----------------------------------------------------------
  422. Purpose: IContextMenu::QueryContextMenu
  423. Returns: standard
  424. Cond: --
  425. */
  426. #define IDCM_UPDATEALL 0
  427. #define IDCM_UPDATE 1
  428. STDMETHODIMP BriefExt_CM_QueryContextMenu(
  429. LPCONTEXTMENU pctm,
  430. HMENU hmenu,
  431. UINT indexMenu,
  432. UINT idCmdFirst,
  433. UINT idCmdLast,
  434. UINT uFlags)
  435. {
  436. PBRIEFEXT this = IToClass(BriefExt, ctm, pctm);
  437. USHORT cItems = 0;
  438. // We only want to add items to the context menu if:
  439. // 1) That's what the caller is asking for; and
  440. // 2) The object is a briefcase or an object inside
  441. // a briefcase
  442. //
  443. if (IsFlagClear(uFlags, CMF_DEFAULTONLY)) // check for (1)
  444. {
  445. TCHAR szIDS[MAXSHORTLEN];
  446. // Is the object inside a briefcase? We know it is if
  447. // the object understands our special format.
  448. //
  449. if (DataObj_KnowsBriefObj(this->pdtobj))
  450. {
  451. // Yes
  452. InsertMenu(hmenu, indexMenu++, MF_BYPOSITION | MF_STRING,
  453. idCmdFirst+IDCM_UPDATE, SzFromIDS(IDS_MENU_UPDATE, szIDS, ARRAYSIZE(szIDS)));
  454. // NOTE: We should actually be using idCmdFirst+0 above since we are only adding
  455. // one item to the menu. But since this code relies on using idCmdFirst+1 then
  456. // we need to lie and say that we added two items to the menu. Otherwise the next
  457. // context menu handler to get called might use the same menu ID that we are using.
  458. cItems = 2;
  459. }
  460. else
  461. {
  462. // No
  463. TCHAR szPath[MAX_PATH];
  464. // Is the object a briefcase root?
  465. if (SUCCEEDED(DataObj_QueryPath(this->pdtobj, szPath)) &&
  466. PathIsBriefcase(szPath))
  467. {
  468. // Yup
  469. InsertMenu(hmenu, indexMenu++, MF_BYPOSITION | MF_STRING,
  470. idCmdFirst+IDCM_UPDATEALL, SzFromIDS(IDS_MENU_UPDATEALL, szIDS, ARRAYSIZE(szIDS)));
  471. cItems++;
  472. }
  473. }
  474. }
  475. return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, (USHORT)cItems));
  476. }
  477. /*----------------------------------------------------------
  478. Purpose: IContextMenu::InvokeCommand
  479. Returns: standard
  480. Cond: --
  481. */
  482. STDMETHODIMP BriefExt_CM_InvokeCommand(
  483. LPCONTEXTMENU pctm,
  484. LPCMINVOKECOMMANDINFO pici)
  485. {
  486. HWND hwnd = pici->hwnd;
  487. //LPCSTR pszWorkingDir = pici->lpDirectory;
  488. //LPCSTR pszCmd = pici->lpVerb;
  489. //LPCSTR pszParam = pici->lpParameters;
  490. //int iShowCmd = pici->nShow;
  491. PBRIEFEXT this = IToClass(BriefExt, ctm, pctm);
  492. LPBRIEFCASESTG pbrfstg;
  493. HRESULT hres;
  494. // The only command we have is to update the selection(s). Create
  495. // an instance of IBriefcaseStg so we can call its Update
  496. // member function.
  497. //
  498. hres = BriefStg_CreateInstance(NULL, &IID_IBriefcaseStg, &pbrfstg);
  499. if (SUCCEEDED(hres))
  500. {
  501. TCHAR szPath[MAX_PATH];
  502. hres = DataObj_QueryPath(this->pdtobj, szPath);
  503. if (SUCCEEDED(hres))
  504. {
  505. hres = pbrfstg->lpVtbl->Initialize(pbrfstg, szPath, hwnd);
  506. if (SUCCEEDED(hres))
  507. {
  508. hres = pbrfstg->lpVtbl->UpdateObject(pbrfstg, this->pdtobj, hwnd);
  509. }
  510. pbrfstg->lpVtbl->Release(pbrfstg);
  511. }
  512. }
  513. return hres;
  514. }
  515. /*----------------------------------------------------------
  516. Purpose: IContextMenu::GetCommandString
  517. Returns: standard
  518. Cond: --
  519. */
  520. STDMETHODIMP BriefExt_CM_GetCommandString(
  521. LPCONTEXTMENU pctm,
  522. UINT_PTR idCmd,
  523. UINT wReserved,
  524. UINT * pwReserved,
  525. LPSTR pszName,
  526. UINT cchMax)
  527. {
  528. switch (wReserved)
  529. {
  530. case GCS_VERB:
  531. switch (idCmd)
  532. {
  533. case IDCM_UPDATE:
  534. lstrcpyn((LPTSTR)pszName, TEXT("update"), cchMax);
  535. return NOERROR;
  536. case IDCM_UPDATEALL:
  537. lstrcpyn((LPTSTR)pszName, TEXT("update all"), cchMax);
  538. return NOERROR;
  539. }
  540. }
  541. return E_NOTIMPL;
  542. }
  543. //---------------------------------------------------------------------------
  544. // PageData functions
  545. //---------------------------------------------------------------------------
  546. /*----------------------------------------------------------
  547. Purpose: Allocates a pagedata.
  548. Returns: TRUE if the allocation/increment was successful
  549. Cond: --
  550. */
  551. BOOL PRIVATE PageData_Alloc(
  552. PPAGEDATA * pppd,
  553. int atomPath)
  554. {
  555. PPAGEDATA this;
  556. ASSERT(pppd);
  557. this = GAlloc(sizeof(*this));
  558. if (this)
  559. {
  560. HRESULT hres;
  561. LPCTSTR pszPath = Atom_GetName(atomPath);
  562. int atomBrf;
  563. // Create an instance of IBriefcaseStg.
  564. hres = BriefStg_CreateInstance(NULL, &IID_IBriefcaseStg, &this->pbrfstg);
  565. if (SUCCEEDED(hres))
  566. {
  567. hres = this->pbrfstg->lpVtbl->Initialize(this->pbrfstg, pszPath, NULL);
  568. if (SUCCEEDED(hres))
  569. {
  570. TCHAR szBrfPath[MAX_PATH];
  571. // Request the root path of the briefcase storage
  572. this->pbrfstg->lpVtbl->GetExtraInfo(this->pbrfstg, NULL, GEI_ROOT,
  573. (WPARAM)ARRAYSIZE(szBrfPath), (LPARAM)szBrfPath);
  574. atomBrf = Atom_Add(szBrfPath);
  575. hres = (ATOM_ERR != atomBrf) ? NOERROR : E_OUTOFMEMORY;
  576. }
  577. }
  578. if (SUCCEEDED(hres))
  579. {
  580. this->pcbs = CBS_Get(atomBrf);
  581. ASSERT(this->pcbs);
  582. Atom_AddRef(atomPath);
  583. this->atomPath = atomPath;
  584. this->cRef = 1;
  585. this->bFolder = (FALSE != PathIsDirectory(pszPath));
  586. Atom_Delete(atomBrf);
  587. }
  588. else
  589. {
  590. // Failed
  591. if (this->pbrfstg)
  592. this->pbrfstg->lpVtbl->Release(this->pbrfstg);
  593. GFree(this);
  594. }
  595. }
  596. *pppd = this;
  597. return NULL != this;
  598. }
  599. /*----------------------------------------------------------
  600. Purpose: Increments the reference count of a pagedata
  601. Returns: Current count
  602. Cond: --
  603. */
  604. UINT PRIVATE PageData_AddRef(
  605. PPAGEDATA this)
  606. {
  607. ASSERT(this);
  608. return ++(this->cRef);
  609. }
  610. /*----------------------------------------------------------
  611. Purpose: Releases a pagedata struct
  612. Returns: the next reference count
  613. 0 if the struct was freed
  614. Cond: --
  615. */
  616. UINT PRIVATE PageData_Release(
  617. PPAGEDATA this)
  618. {
  619. UINT cRef;
  620. ASSERT(this);
  621. ASSERT(0 < this->cRef);
  622. cRef = this->cRef;
  623. if (0 < this->cRef)
  624. {
  625. this->cRef--;
  626. if (0 == this->cRef)
  627. {
  628. if (this->pftl)
  629. {
  630. Sync_DestroyFolderList(this->pftl);
  631. }
  632. if (this->prl)
  633. {
  634. Sync_DestroyRecList(this->prl);
  635. }
  636. CBS_Delete(this->pcbs->atomBrf, NULL);
  637. Atom_Delete(this->atomPath);
  638. this->pbrfstg->lpVtbl->Release(this->pbrfstg);
  639. GFree(this);
  640. return 0;
  641. }
  642. }
  643. return this->cRef;
  644. }
  645. /*----------------------------------------------------------
  646. Purpose: Sets the data in the pagedata struct to indicate this
  647. is an orphan. This function makes no change to the
  648. database--the caller must do that.
  649. Returns: --
  650. Cond: --
  651. */
  652. void PUBLIC PageData_Orphanize(
  653. PPAGEDATA this)
  654. {
  655. this->bOrphan = TRUE;
  656. if (this->pftl)
  657. {
  658. Sync_DestroyFolderList(this->pftl);
  659. this->pftl = NULL;
  660. }
  661. if (this->prl)
  662. {
  663. Sync_DestroyRecList(this->prl);
  664. this->prl = NULL;
  665. }
  666. }
  667. /*----------------------------------------------------------
  668. Purpose: Initializes the common page data struct shared between
  669. the property pages. Keep in mind that this function may
  670. be called multiple times, so it must behave properly
  671. under these conditions (ie, don't blow anything away).
  672. This function will return S_OK if it is. S_FALSE means
  673. the data in question has been invalidated. This means
  674. the twin has become an orphan.
  675. Returns: standard result
  676. Cond: --
  677. */
  678. HRESULT PUBLIC PageData_Init(
  679. PPAGEDATA this,
  680. HWND hwndOwner)
  681. {
  682. HRESULT hres;
  683. HBRFCASE hbrf = PageData_GetHbrf(this);
  684. LPCTSTR pszPath = Atom_GetName(this->atomPath);
  685. // ** Note: this structure is not serialized because it is
  686. // assumed that of the pages that are sharing it, only one
  687. // can access it at a time.
  688. ASSERT(pszPath);
  689. // Has this been explicitly marked as an orphan?
  690. if (FALSE == this->bOrphan)
  691. {
  692. // No; is it (still) a twin?
  693. if (S_OK == Sync_IsTwin(hbrf, pszPath, 0))
  694. {
  695. // Yes; has the folder twinlist or reclist been created yet?
  696. if (NULL == this->prl ||
  697. (this->bFolder && NULL == this->pftl))
  698. {
  699. // No; create it/them
  700. HTWINLIST htl;
  701. PFOLDERTWINLIST pftl = NULL;
  702. PRECLIST prl = NULL;
  703. HWND hwndProgress;
  704. TWINRESULT tr;
  705. ASSERT(NULL == this->prl);
  706. ASSERT( !this->bFolder || NULL == this->pftl);
  707. hwndProgress = UpdBar_Show(hwndOwner, UB_CHECKING | UB_NOCANCEL, DELAY_UPDBAR);
  708. tr = Sync_CreateTwinList(hbrf, &htl);
  709. hres = HRESULT_FROM_TR(tr);
  710. if (SUCCEEDED(hres))
  711. {
  712. // Add to the twinlist. Create folder twinlist if
  713. // necessary.
  714. if (Sync_AddPathToTwinList(hbrf, htl, pszPath, &pftl))
  715. {
  716. // Does the reclist need creating?
  717. if (NULL == this->prl)
  718. {
  719. // Yes
  720. hres = Sync_CreateRecListEx(htl, UpdBar_GetAbortEvt(hwndProgress), &prl);
  721. if (SUCCEEDED(hres))
  722. {
  723. // The object may have been implicitly
  724. // deleted in CreateRecList. Check again.
  725. hres = Sync_IsTwin(hbrf, pszPath, 0);
  726. }
  727. }
  728. }
  729. else
  730. hres = E_FAIL;
  731. // Fill in proper fields
  732. if (NULL == this->prl && prl)
  733. {
  734. this->prl = prl;
  735. }
  736. if (NULL == this->pftl && pftl)
  737. {
  738. this->pftl = pftl;
  739. }
  740. // Clean up twinlist
  741. Sync_DestroyTwinList(htl);
  742. }
  743. UpdBar_Kill(hwndProgress);
  744. // Did the above succeed?
  745. if (FAILED(hres) || S_FALSE == hres)
  746. {
  747. // No
  748. PageData_Orphanize(this);
  749. }
  750. }
  751. else
  752. {
  753. // Yes; do nothing
  754. hres = S_OK;
  755. }
  756. }
  757. else
  758. {
  759. // No; say the thing is an orphan
  760. PageData_Orphanize(this);
  761. hres = S_FALSE;
  762. }
  763. }
  764. else
  765. {
  766. // Yes
  767. hres = S_FALSE;
  768. }
  769. #ifdef DEBUG
  770. if (S_OK == hres)
  771. {
  772. ASSERT( !this->bFolder || this->pftl );
  773. ASSERT(this->prl);
  774. }
  775. else
  776. {
  777. ASSERT(NULL == this->pftl);
  778. ASSERT(NULL == this->prl);
  779. }
  780. #endif
  781. return hres;
  782. }
  783. /*----------------------------------------------------------
  784. Purpose: Verifies whether the page data shared by the property
  785. pages is still valid. This function will return S_OK if
  786. it is. S_FALSE means the data in question has been
  787. invalidated. This means the twin has become an orphan.
  788. This function assumes PageData_Init has been previously
  789. called.
  790. Returns: standard result
  791. Cond: --
  792. */
  793. HRESULT PUBLIC PageData_Query(
  794. PPAGEDATA this,
  795. HWND hwndOwner,
  796. PRECLIST * pprl, // May be NULL
  797. PFOLDERTWINLIST * ppftl) // May be NULL
  798. {
  799. HRESULT hres;
  800. LPCTSTR pszPath = Atom_GetName(this->atomPath);
  801. // ** Note: this structure is not serialized because it is
  802. // assumed that of the pages that are sharing it, only one
  803. // can access it at a time.
  804. ASSERT(pszPath);
  805. // Is a recalc called for?
  806. if (this->bRecalc)
  807. {
  808. // Yes; clear the fields and do again
  809. PageData_Orphanize(this); // only temporary
  810. this->bOrphan = FALSE; // undo the orphan state
  811. this->bRecalc = FALSE;
  812. // Reinit
  813. hres = PageData_Init(this, hwndOwner);
  814. if (pprl)
  815. *pprl = this->prl;
  816. if (ppftl)
  817. *ppftl = this->pftl;
  818. }
  819. // Are the fields valid?
  820. else if ( this->prl && (!this->bFolder || this->pftl) )
  821. {
  822. // Yes; is it (still) a twin?
  823. ASSERT(FALSE == this->bOrphan);
  824. hres = Sync_IsTwin(this->pcbs->hbrf, pszPath, 0);
  825. if (S_OK == hres)
  826. {
  827. // Yes
  828. if (pprl)
  829. *pprl = this->prl;
  830. if (ppftl)
  831. *ppftl = this->pftl;
  832. }
  833. else if (S_FALSE == hres)
  834. {
  835. // No; update struct fields
  836. PageData_Orphanize(this);
  837. goto OrphanTime;
  838. }
  839. }
  840. else
  841. {
  842. // No; say it is an orphan
  843. OrphanTime:
  844. ASSERT(this->bOrphan);
  845. if (pprl)
  846. *pprl = NULL;
  847. if (ppftl)
  848. *ppftl = NULL;
  849. hres = S_FALSE;
  850. }
  851. return hres;
  852. }
  853. //---------------------------------------------------------------------------
  854. // BriefExt IShellPropSheetExt member functions
  855. //---------------------------------------------------------------------------
  856. /*----------------------------------------------------------
  857. Purpose: IShellPropSheetExt::QueryInterface
  858. Returns: standard
  859. Cond: --
  860. */
  861. STDMETHODIMP BriefExt_SPX_QueryInterface(
  862. LPSHELLPROPSHEETEXT pspx,
  863. REFIID riid,
  864. LPVOID * ppvOut)
  865. {
  866. PBRIEFEXT this = IToClass(BriefExt, spx, pspx);
  867. return BriefExt_QueryInterface((LPUNKNOWN)&this->sxi, riid, ppvOut);
  868. }
  869. /*----------------------------------------------------------
  870. Purpose: IShellPropSheetExt::AddRef
  871. Returns: new reference count
  872. Cond: --
  873. */
  874. STDMETHODIMP_(UINT) BriefExt_SPX_AddRef(
  875. LPSHELLPROPSHEETEXT pspx)
  876. {
  877. PBRIEFEXT this = IToClass(BriefExt, spx, pspx);
  878. return BriefExt_AddRef((LPUNKNOWN)&this->sxi);
  879. }
  880. /*----------------------------------------------------------
  881. Purpose: IShellPropSheetExt::Release
  882. Returns: new reference count
  883. Cond: --
  884. */
  885. STDMETHODIMP_(UINT) BriefExt_SPX_Release(
  886. LPSHELLPROPSHEETEXT pspx)
  887. {
  888. PBRIEFEXT this = IToClass(BriefExt, spx, pspx);
  889. return BriefExt_Release((LPUNKNOWN)&this->sxi);
  890. }
  891. /*----------------------------------------------------------
  892. Purpose: Callback when Status property page is done
  893. Returns: --
  894. Cond: --
  895. */
  896. UINT CALLBACK StatusPageCallback(
  897. HWND hwnd,
  898. UINT uMsg,
  899. LPPROPSHEETPAGE ppsp)
  900. {
  901. if (PSPCB_RELEASE == uMsg)
  902. {
  903. PPAGEDATA ppagedata = (PPAGEDATA)ppsp->lParam;
  904. DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Releasing status page")); )
  905. PageData_Release(ppagedata);
  906. }
  907. return TRUE;
  908. }
  909. /*----------------------------------------------------------
  910. Purpose: Callback when Info property sheet is done
  911. Returns: --
  912. Cond: --
  913. */
  914. UINT CALLBACK InfoPageCallback(
  915. HWND hwnd,
  916. UINT uMsg,
  917. LPPROPSHEETPAGE ppsp)
  918. {
  919. if (PSPCB_RELEASE == uMsg)
  920. {
  921. PPAGEDATA ppagedata = (PPAGEDATA)ppsp->lParam;
  922. PINFODATA pinfodata = (PINFODATA)ppagedata->lParam;
  923. DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Releasing info page")); )
  924. if (pinfodata->hdpaTwins)
  925. {
  926. int iItem;
  927. int cItems = DPA_GetPtrCount(pinfodata->hdpaTwins);
  928. HTWIN htwin;
  929. for (iItem = 0; iItem < cItems; iItem++)
  930. {
  931. htwin = DPA_FastGetPtr(pinfodata->hdpaTwins, iItem);
  932. Sync_ReleaseTwin(htwin);
  933. }
  934. DPA_Destroy(pinfodata->hdpaTwins);
  935. }
  936. GFree(pinfodata);
  937. PageData_Release(ppagedata);
  938. }
  939. return TRUE;
  940. }
  941. /*----------------------------------------------------------
  942. Purpose: Add the status property page
  943. Returns: TRUE on success
  944. FALSE if out of memory
  945. Cond: --
  946. */
  947. BOOL PRIVATE AddStatusPage(
  948. PPAGEDATA ppd,
  949. LPFNADDPROPSHEETPAGE pfnAddPage,
  950. LPARAM lParam)
  951. {
  952. BOOL bRet = FALSE;
  953. HPROPSHEETPAGE hpsp;
  954. PROPSHEETPAGE psp = {
  955. sizeof(PROPSHEETPAGE), // size
  956. PSP_USECALLBACK, // PSP_ flags
  957. g_hinst, // hinstance
  958. MAKEINTRESOURCE(IDD_STATUS), // pszTemplate
  959. NULL, // icon
  960. NULL, // pszTitle
  961. Stat_WrapperProc, // pfnDlgProc
  962. (LPARAM)ppd, // lParam
  963. StatusPageCallback, // pfnCallback
  964. 0 }; // ref count
  965. ASSERT(ppd);
  966. ASSERT(pfnAddPage);
  967. DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Adding status page")); )
  968. // Add the status property sheet
  969. hpsp = CreatePropertySheetPage(&psp);
  970. if (hpsp)
  971. {
  972. bRet = (*pfnAddPage)(hpsp, lParam);
  973. if (FALSE == bRet)
  974. {
  975. // Cleanup on failure
  976. DestroyPropertySheetPage(hpsp);
  977. }
  978. }
  979. return bRet;
  980. }
  981. /*----------------------------------------------------------
  982. Purpose: Add the info property page.
  983. Returns: TRUE on success
  984. FALSE if out of memory
  985. Cond: --
  986. */
  987. BOOL PRIVATE AddInfoPage(
  988. PPAGEDATA ppd,
  989. LPFNADDPROPSHEETPAGE lpfnAddPage,
  990. LPARAM lParam)
  991. {
  992. BOOL bRet = FALSE;
  993. HPROPSHEETPAGE hpsp;
  994. PINFODATA pinfodata;
  995. ASSERT(lpfnAddPage);
  996. DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Adding info page")); )
  997. pinfodata = GAlloc(sizeof(*pinfodata));
  998. if (pinfodata)
  999. {
  1000. PROPSHEETPAGE psp = {
  1001. sizeof(PROPSHEETPAGE), // size
  1002. PSP_USECALLBACK, // PSP_ flags
  1003. g_hinst, // hinstance
  1004. MAKEINTRESOURCE(IDD_INFO), // pszTemplate
  1005. NULL, // icon
  1006. NULL, // pszTitle
  1007. Info_WrapperProc, // pfnDlgProc
  1008. (LPARAM)ppd, // lParam
  1009. InfoPageCallback, // pfnCallback
  1010. 0 }; // ref count
  1011. ppd->lParam = (LPARAM)pinfodata;
  1012. pinfodata->atomTo = ATOM_ERR; // Not needed for page
  1013. pinfodata->bStandAlone = FALSE;
  1014. if (NULL != (pinfodata->hdpaTwins = DPA_Create(8)))
  1015. {
  1016. hpsp = CreatePropertySheetPage(&psp);
  1017. if (hpsp)
  1018. {
  1019. bRet = (*lpfnAddPage)(hpsp, lParam);
  1020. if (FALSE == bRet)
  1021. {
  1022. // Cleanup on failure
  1023. DestroyPropertySheetPage(hpsp);
  1024. }
  1025. }
  1026. if (FALSE == bRet)
  1027. {
  1028. // Cleanup on failure
  1029. DPA_Destroy(pinfodata->hdpaTwins);
  1030. }
  1031. }
  1032. if (FALSE == bRet)
  1033. {
  1034. // Cleanup on failure
  1035. GFree(pinfodata);
  1036. }
  1037. }
  1038. return bRet;
  1039. }
  1040. /*----------------------------------------------------------
  1041. Purpose: Does the real work to add the briefcase pages to
  1042. the property sheet.
  1043. Returns: --
  1044. Cond: --
  1045. */
  1046. void PRIVATE BriefExt_AddPagesPrivate(
  1047. LPSHELLPROPSHEETEXT pspx,
  1048. LPCTSTR pszPath,
  1049. LPFNADDPROPSHEETPAGE lpfnAddPage,
  1050. LPARAM lParam)
  1051. {
  1052. PBRIEFEXT this = IToClass(BriefExt, spx, pspx);
  1053. HRESULT hres = NOERROR;
  1054. TCHAR szCanonPath[MAX_PATH];
  1055. int atomPath;
  1056. BrfPathCanonicalize(pszPath, szCanonPath);
  1057. atomPath = Atom_Add(szCanonPath);
  1058. if (atomPath != ATOM_ERR)
  1059. {
  1060. PPAGEDATA ppagedata;
  1061. BOOL bVal;
  1062. // Allocate the pagedata
  1063. if (PageData_Alloc(&ppagedata, atomPath))
  1064. {
  1065. // Always add the status page (even for orphans).
  1066. // Add the info page if the object is a folder.
  1067. bVal = AddStatusPage(ppagedata, lpfnAddPage, lParam);
  1068. if (bVal && ppagedata->bFolder)
  1069. {
  1070. PageData_AddRef(ppagedata);
  1071. AddInfoPage(ppagedata, lpfnAddPage, lParam);
  1072. }
  1073. else if (FALSE == bVal)
  1074. {
  1075. // (Cleanup on failure)
  1076. PageData_Release(ppagedata);
  1077. }
  1078. }
  1079. Atom_Delete(atomPath);
  1080. }
  1081. }
  1082. /*----------------------------------------------------------
  1083. Purpose: IShellPropSheetExt::AddPages
  1084. The shell calls this member function when it is
  1085. time to add pages to a property sheet.
  1086. As the briefcase storage, we only add pages for
  1087. entities inside a briefcase. Anything outside
  1088. a briefcase is not touched.
  1089. We can quickly determine if the object is inside
  1090. the briefcase by querying the data object that
  1091. we have. If it knows our special "briefcase object"
  1092. format, then it must be inside a briefcase. We
  1093. purposely do not add pages for the root folder itself.
  1094. Returns: standard hresult
  1095. Cond: --
  1096. */
  1097. STDMETHODIMP BriefExt_SPX_AddPages(
  1098. LPSHELLPROPSHEETEXT pspx,
  1099. LPFNADDPROPSHEETPAGE lpfnAddPage,
  1100. LPARAM lParam)
  1101. {
  1102. PBRIEFEXT this = IToClass(BriefExt, spx, pspx);
  1103. LPTSTR pszList;
  1104. UINT cFiles;
  1105. // Questions:
  1106. // 1) Does this know the briefcase object format?
  1107. // 2) Is there only a single object selected?
  1108. //
  1109. if (DataObj_KnowsBriefObj(this->pdtobj) && /* (1) */
  1110. SUCCEEDED(DataObj_QueryFileList(this->pdtobj, &pszList, &cFiles)) &&
  1111. cFiles == 1) /* (2) */
  1112. {
  1113. // Yes; add the pages
  1114. BriefExt_AddPagesPrivate(pspx, pszList, lpfnAddPage, lParam);
  1115. DataObj_FreeList(pszList);
  1116. }
  1117. return NOERROR; // Always allow property sheet to appear
  1118. }
  1119. //---------------------------------------------------------------------------
  1120. // BriefExtMenu class : Vtables
  1121. //---------------------------------------------------------------------------
  1122. IShellExtInitVtbl c_BriefExt_SXIVtbl =
  1123. {
  1124. BriefExt_SXI_QueryInterface,
  1125. BriefExt_SXI_AddRef,
  1126. BriefExt_SXI_Release,
  1127. BriefExt_SXI_Initialize
  1128. };
  1129. IContextMenuVtbl c_BriefExt_CTMVtbl =
  1130. {
  1131. BriefExt_CM_QueryInterface,
  1132. BriefExt_CM_AddRef,
  1133. BriefExt_CM_Release,
  1134. BriefExt_CM_QueryContextMenu,
  1135. BriefExt_CM_InvokeCommand,
  1136. BriefExt_CM_GetCommandString,
  1137. };
  1138. IShellPropSheetExtVtbl c_BriefExt_SPXVtbl = {
  1139. BriefExt_SPX_QueryInterface,
  1140. BriefExt_SPX_AddRef,
  1141. BriefExt_SPX_Release,
  1142. BriefExt_SPX_AddPages
  1143. };
  1144. /*----------------------------------------------------------
  1145. Purpose: This function is called back from within
  1146. IClassFactory::CreateInstance() of the default class
  1147. factory object, which is created by SHCreateClassObject.
  1148. Returns: standard
  1149. Cond: --
  1150. */
  1151. HRESULT CALLBACK BriefExt_CreateInstance(
  1152. LPUNKNOWN punkOuter,
  1153. REFIID riid,
  1154. LPVOID * ppvOut)
  1155. {
  1156. HRESULT hres;
  1157. PBRIEFEXT this;
  1158. DBG_ENTER_RIID(TEXT("BriefExt_CreateInstance"), riid);
  1159. // Shell extentions typically do not support aggregation.
  1160. //
  1161. if (punkOuter)
  1162. {
  1163. hres = ResultFromScode(CLASS_E_NOAGGREGATION);
  1164. *ppvOut = NULL;
  1165. goto Leave;
  1166. }
  1167. this = GAlloc(sizeof(*this));
  1168. if (!this)
  1169. {
  1170. hres = ResultFromScode(E_OUTOFMEMORY);
  1171. *ppvOut = NULL;
  1172. goto Leave;
  1173. }
  1174. this->sxi.lpVtbl = &c_BriefExt_SXIVtbl;
  1175. this->ctm.lpVtbl = &c_BriefExt_CTMVtbl;
  1176. this->spx.lpVtbl = &c_BriefExt_SPXVtbl;
  1177. this->cRef = 1;
  1178. this->pdtobj = NULL;
  1179. this->hkeyProgID = NULL;
  1180. ENTEREXCLUSIVE();
  1181. {
  1182. // The decrement is in BriefExt_Release()
  1183. IncBusySemaphore();
  1184. }
  1185. LEAVEEXCLUSIVE();
  1186. // Note that the Release member will free the object, if QueryInterface
  1187. // failed.
  1188. //
  1189. hres = c_BriefExt_SXIVtbl.QueryInterface(&this->sxi, riid, ppvOut);
  1190. c_BriefExt_SXIVtbl.Release(&this->sxi);
  1191. Leave:
  1192. DBG_EXIT_HRES(TEXT("BriefExt_CreateInstance"), hres);
  1193. return hres; // S_OK or E_NOINTERFACE
  1194. }