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.

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