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.

5433 lines
174 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include "clsobj.h"
  4. #include "ids.h"
  5. #include <cowsite.h>
  6. #include "datautil.h"
  7. #include "idhidden.h"
  8. #include "prop.h"
  9. #include "stgutil.h"
  10. #include "sfstorage.h"
  11. #include "util.h"
  12. #include "fstreex.h"
  13. #include "basefvcb.h"
  14. #include "category.h"
  15. #include "mergfldr.h"
  16. #include "filefldr.h"
  17. #include "idldata.h"
  18. #include "defcm.h"
  19. #define TF_AUGM 0x10000000
  20. // pidl wrapper contains a TAG word for validation and then
  21. // the count for the number of packaged pidl object.
  22. //
  23. // each pidl has a hidden payload which is the name space
  24. // index that it originated from.
  25. #pragma pack(1)
  26. typedef struct
  27. {
  28. USHORT cb; // pidl wrap length
  29. USHORT dwFlags; // flags
  30. ULONG ulTag; // signature
  31. ULONG ulVersion ; // AugMergeISF pidl version
  32. ULONG cSrcs; // Number of source _Namespace objects backing this composite pidl
  33. } AUGM_IDWRAP;
  34. typedef UNALIGNED AUGM_IDWRAP *PAUGM_IDWRAP;
  35. typedef struct
  36. {
  37. HIDDENITEMID hid;
  38. UINT uSrcID; // src _Namespace
  39. } AUGM_NAMESPACE;
  40. typedef UNALIGNED AUGM_NAMESPACE *PAUGM_NAMESPACE;
  41. #pragma pack()
  42. #define AUGM_NS_CURRENTVERSION 0
  43. #define AUGM_WRAPVERSION_1_0 MAKELONG(1, 0)
  44. #define AUGM_WRAPVERSION_2_0 MAKELONG(2, 0)
  45. #define AUGM_WRAPCURRENTVERSION AUGM_WRAPVERSION_2_0
  46. #define AUGM_WRAPTAG MAKELONG(MAKEWORD('A','u'), MAKEWORD('g','M'))
  47. #define CB_IDLIST_TERMINATOR sizeof(USHORT)
  48. // dwFlags field flags
  49. #define AUGMF_ISSIMPLE 0x0001
  50. // helpers.
  51. HRESULT CMergedFldrContextMenu_CreateInstance(HWND hwnd, CMergedFolder *pmf, UINT cidl, LPCITEMIDLIST *apidl, IContextMenu *pcmCommon, IContextMenu *pcmUser, IContextMenu **ppcm);
  52. HRESULT CMergedFldrEnum_CreateInstance(CMergedFolder*pmf, DWORD grfFlags, IEnumIDList **ppenum);
  53. HRESULT CMergedFldrDropTarget_CreateInstance(CMergedFolder*pmf, HWND hwnd, IDropTarget **ppdt);
  54. HRESULT CMergedFolderViewCB_CreateInstance(CMergedFolder *pmf, IShellFolderViewCB **ppsfvcb);
  55. // Helper function that spans all objects
  56. BOOL AffectAllUsers(HWND hwnd)
  57. {
  58. BOOL bRet = FALSE; // default to NO
  59. if (hwnd)
  60. {
  61. TCHAR szMessage[255];
  62. TCHAR szTitle[20];
  63. if (LoadString(HINST_THISDLL, IDS_ALLUSER_WARNING, szMessage, ARRAYSIZE(szMessage)) > 0 &&
  64. LoadString(HINST_THISDLL, IDS_WARNING, szTitle, ARRAYSIZE(szTitle)) > 0)
  65. {
  66. bRet = (IDYES == MessageBox(hwnd, szMessage, szTitle, MB_YESNO | MB_ICONINFORMATION));
  67. }
  68. }
  69. else
  70. bRet = TRUE; // NULL hwnd implies NO UI, say "yes"
  71. return bRet;
  72. }
  73. // CMergedFoldersource _Namespace descriptor.
  74. //
  75. // Objects of class CMergedFldrNamespace are created by CMergedFolderin
  76. // the AddNameSpace() method impl, and are maintained in the collection
  77. // CMergedFolder::_hdpaNamespaces.
  78. //
  79. class CMergedFldrNamespace
  80. {
  81. public:
  82. CMergedFldrNamespace();
  83. ~CMergedFldrNamespace();
  84. IShellFolder* Folder() const
  85. { return _psf; }
  86. REFGUID GetGUID() const
  87. { return _guid; }
  88. ULONG FolderAttrib() const
  89. { return _dwAttrib; }
  90. LPCITEMIDLIST GetIDList() const
  91. { return _pidl; }
  92. HRESULT GetLocation(LPWSTR pszBuffer, INT cchBuffer)
  93. { StrCpyN(pszBuffer, _szLocation, cchBuffer); return S_OK; };
  94. LPCWSTR GetDropFolder()
  95. { return _szDrop; };
  96. ULONG FixItemAttributes(ULONG attrib)
  97. { return (attrib & _dwItemAttribMask) | _dwItemAttrib; }
  98. DWORD GetDropEffect(void) const
  99. { return _dwDropEffect; }
  100. int GetDefaultOverlayIndex() const
  101. { return _iDefaultOverlayIndex; }
  102. int GetConflictOverlayIndex() const
  103. { return _iConflictOverlayIndex; }
  104. int GetNamespaceOverlayIndex(LPCITEMIDLIST pidl);
  105. HRESULT SetNamespace(const GUID * pguidUIObject, IShellFolder* psf, LPCITEMIDLIST pidl, ULONG dwAttrib);
  106. HRESULT SetDropFolder(LPCWSTR pszDrop);
  107. HRESULT RegisterNotify(HWND, UINT, ULONG);
  108. HRESULT UnregisterNotify();
  109. BOOL SetOwner(IUnknown *punk);
  110. protected:
  111. ULONG _RegisterNotify(HWND hwnd, UINT nMsg, LPCITEMIDLIST pidl, DWORD dwEvents, UINT uFlags, BOOL fRecursive);
  112. void _ReleaseNamespace();
  113. IShellFolder* _psf; // IShellFolder interface pointer
  114. GUID _guid; // optional GUID for specialized UI handling
  115. LPITEMIDLIST _pidl; // optional pidl
  116. ULONG _dwAttrib; // optional flags
  117. UINT _uChangeReg; // Shell change notify registration ID.
  118. WCHAR _szLocation[MAX_PATH]; // Location to use for object
  119. WCHAR _szDrop[MAX_PATH]; // folder that gets the forced drop effect
  120. DWORD _dwItemAttrib; // OR mask for the attributes
  121. DWORD _dwItemAttribMask; // AND mask for the attributes
  122. DWORD _dwDropEffect; // default drop effect for this folder
  123. int _iDefaultOverlayIndex; // overlay icon index for default
  124. int _iConflictOverlayIndex; // overlay icon index if the name exists in another namespace
  125. };
  126. CMergedFldrNamespace::CMergedFldrNamespace() :
  127. _dwItemAttribMask(-1)
  128. {
  129. }
  130. inline CMergedFldrNamespace::~CMergedFldrNamespace()
  131. {
  132. UnregisterNotify();
  133. _ReleaseNamespace();
  134. }
  135. HRESULT CMergedFldrNamespace::SetNamespace(const GUID * pguidUIObject, IShellFolder* psf, LPCITEMIDLIST pidl, ULONG dwAttrib)
  136. {
  137. _ReleaseNamespace();
  138. // store the IShellFolder object if we have one
  139. if (psf)
  140. {
  141. _psf = psf;
  142. _psf->AddRef();
  143. }
  144. else if (pidl)
  145. {
  146. SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidl, &_psf));
  147. }
  148. // get the IDLIST that this namespace represnets
  149. if (pidl)
  150. {
  151. _pidl = ILClone(pidl); // we have a PIDL passed to us.
  152. }
  153. else
  154. {
  155. _pidl = NULL;
  156. IPersistFolder3 *ppf3;
  157. if (SUCCEEDED(_psf->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf3))))
  158. {
  159. PERSIST_FOLDER_TARGET_INFO pfti;
  160. if (SUCCEEDED(ppf3->GetFolderTargetInfo(&pfti)))
  161. {
  162. _pidl = pfti.pidlTargetFolder;
  163. }
  164. ppf3->Release();
  165. }
  166. // if it doesnt have IPersistFolder3 or if there's no target folder then
  167. // fall back to IPersistFolder2
  168. if (!_pidl)
  169. {
  170. SHGetIDListFromUnk(psf, &_pidl);
  171. }
  172. }
  173. if (!_psf || !_pidl)
  174. return E_FAIL;
  175. // now fill out the information about the namespace, including getting the display
  176. // information from the registry
  177. _guid = pguidUIObject ? *pguidUIObject : GUID_NULL;
  178. _dwAttrib = dwAttrib;
  179. _szLocation[0] = TEXT('\0');
  180. _dwItemAttrib = 0; // item attribute become a NOP
  181. _dwItemAttribMask = (DWORD)-1;
  182. _dwDropEffect = 0; // default behaviour
  183. _iDefaultOverlayIndex = -1;
  184. _iConflictOverlayIndex = -1;
  185. // format a key to the property bag stored in the registry, then create the
  186. // property bag which we then query against.
  187. TCHAR szKey[MAX_PATH], szGUID[GUIDSTR_MAX+1];
  188. SHStringFromGUID(_guid, szGUID, ARRAYSIZE(szGUID));
  189. wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("CLSID\\%s\\MergedFolder"), szGUID);
  190. IPropertyBag *ppb;
  191. if (SUCCEEDED(SHCreatePropertyBagOnRegKey(HKEY_CLASSES_ROOT, szKey, STGM_READ, IID_PPV_ARG(IPropertyBag, &ppb))))
  192. {
  193. TCHAR szLocalized[100];
  194. if (SUCCEEDED(SHPropertyBag_ReadStr(ppb, L"Location", szLocalized, ARRAYSIZE(szLocalized))))
  195. {
  196. SHLoadIndirectString(szLocalized, _szLocation, ARRAYSIZE(_szLocation), NULL);
  197. }
  198. SHPropertyBag_ReadDWORD(ppb, L"Attributes", &_dwItemAttrib);
  199. SHPropertyBag_ReadDWORD(ppb, L"AttributeMask", &_dwItemAttribMask);
  200. SHPropertyBag_ReadDWORD(ppb, L"DropEffect", &_dwDropEffect);
  201. TCHAR szIconLocation[MAX_PATH];
  202. szIconLocation[0] = 0;
  203. SHPropertyBag_ReadStr(ppb, L"DefaultOverlayIcon", szIconLocation, ARRAYSIZE(szIconLocation));
  204. _iDefaultOverlayIndex = SHGetIconOverlayIndex(szIconLocation, PathParseIconLocation(szIconLocation));
  205. szIconLocation[0] = 0;
  206. SHPropertyBag_ReadStr(ppb, L"ConflictOverlayIcon", szIconLocation, ARRAYSIZE(szIconLocation));
  207. _iConflictOverlayIndex = SHGetIconOverlayIndex(szIconLocation, PathParseIconLocation(szIconLocation));
  208. ppb->Release();
  209. }
  210. if (!SHGetPathFromIDList(_pidl, _szDrop))
  211. {
  212. _szDrop[0] = 0;
  213. }
  214. return S_OK;
  215. }
  216. HRESULT CMergedFldrNamespace::SetDropFolder(LPCWSTR pszDrop)
  217. {
  218. StrCpyN(_szDrop, pszDrop, ARRAYSIZE(_szDrop));
  219. return S_OK;
  220. }
  221. void CMergedFldrNamespace::_ReleaseNamespace()
  222. {
  223. ATOMICRELEASE(_psf);
  224. ILFree(_pidl);
  225. _pidl = NULL;
  226. _guid = GUID_NULL;
  227. _dwAttrib = 0L;
  228. }
  229. ULONG CMergedFldrNamespace::_RegisterNotify(HWND hwnd, UINT nMsg, LPCITEMIDLIST pidl, DWORD dwEvents, UINT uFlags, BOOL fRecursive)
  230. {
  231. SHChangeNotifyEntry fsne = { 0 };
  232. fsne.fRecursive = fRecursive;
  233. fsne.pidl = pidl;
  234. return SHChangeNotifyRegister(hwnd, uFlags | SHCNRF_NewDelivery, dwEvents, nMsg, 1, &fsne);
  235. }
  236. // Register change notification for the _Namespace
  237. HRESULT CMergedFldrNamespace::RegisterNotify(HWND hwnd, UINT uMsg, ULONG lEvents)
  238. {
  239. if (0 == _uChangeReg)
  240. {
  241. _uChangeReg = _RegisterNotify(hwnd, uMsg, _pidl, lEvents,
  242. SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_RecursiveInterrupt,
  243. TRUE);
  244. }
  245. return 0 != _uChangeReg ? S_OK : E_FAIL;
  246. }
  247. // Unregister change notification for the _Namespace
  248. HRESULT CMergedFldrNamespace::UnregisterNotify()
  249. {
  250. if (_uChangeReg)
  251. {
  252. ::SHChangeNotifyDeregister(_uChangeReg);
  253. _uChangeReg = 0;
  254. }
  255. return S_OK;
  256. }
  257. inline BOOL CMergedFldrNamespace::SetOwner(IUnknown *punkOwner)
  258. {
  259. if (!_psf)
  260. return FALSE;
  261. IUnknown_SetOwner(_psf, punkOwner);
  262. return TRUE;
  263. }
  264. int CMergedFldrNamespace::GetNamespaceOverlayIndex(LPCITEMIDLIST pidl)
  265. {
  266. int iIndex = -1;
  267. if (_psf)
  268. {
  269. IShellIconOverlay *psio;
  270. if (SUCCEEDED(_psf->QueryInterface(IID_PPV_ARG(IShellIconOverlay, &psio))))
  271. {
  272. psio->GetOverlayIndex(pidl, &iIndex);
  273. psio->Release();
  274. }
  275. }
  276. return iIndex;
  277. }
  278. // object which takes ownership of the IDLIST and handles wrapping and returning information from it.
  279. class CMergedFldrItem
  280. {
  281. public:
  282. ~CMergedFldrItem();
  283. BOOL Init(IShellFolder* psf, LPITEMIDLIST pidl, int iNamespace);
  284. BOOL Init(CMergedFldrItem *pmfi);
  285. BOOL SetDisplayName(LPTSTR pszDispName)
  286. { return Str_SetPtr(&_pszDisplayName, pszDispName); }
  287. ULONG GetFolderAttrib()
  288. { return _rgfAttrib; }
  289. LPTSTR GetDisplayName()
  290. { return _pszDisplayName; }
  291. LPITEMIDLIST GetIDList()
  292. { return _pidlWrap; }
  293. int GetNamespaceID()
  294. { return _iNamespace; }
  295. private:
  296. ULONG _rgfAttrib;
  297. LPTSTR _pszDisplayName;
  298. LPITEMIDLIST _pidlWrap;
  299. int _iNamespace;
  300. friend CMergedFolder;
  301. };
  302. CMergedFldrItem::~CMergedFldrItem()
  303. {
  304. Str_SetPtr(&_pszDisplayName, NULL);
  305. ILFree(_pidlWrap);
  306. }
  307. BOOL CMergedFldrItem::Init(CMergedFldrItem *pmfi)
  308. {
  309. _iNamespace = pmfi->_iNamespace;
  310. _pidlWrap = ILClone(pmfi->GetIDList());
  311. BOOL fRet = (_pidlWrap != NULL);
  312. if (fRet)
  313. {
  314. fRet = SetDisplayName(pmfi->GetDisplayName());
  315. _rgfAttrib = pmfi->GetFolderAttrib();
  316. }
  317. return fRet;
  318. }
  319. BOOL CMergedFldrItem::Init(IShellFolder* psf, LPITEMIDLIST pidl, int iNamespace)
  320. {
  321. BOOL fRet = FALSE;
  322. _pidlWrap = pidl; // evil, hold an alias
  323. _rgfAttrib = SFGAO_FOLDER | SFGAO_HIDDEN;
  324. _iNamespace = iNamespace;
  325. if (SUCCEEDED(psf->GetAttributesOf(1, (LPCITEMIDLIST*)&pidl, &_rgfAttrib)))
  326. {
  327. TCHAR szDisplayName[MAX_PATH];
  328. if (SUCCEEDED(DisplayNameOf(psf, pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szDisplayName, ARRAYSIZE(szDisplayName))))
  329. {
  330. fRet = SetDisplayName(szDisplayName);
  331. }
  332. }
  333. return fRet;
  334. }
  335. // shell folder object.
  336. CMergedFolder::CMergedFolder(CMergedFolder *pmfParent, REFCLSID clsid) :
  337. _clsid(clsid),
  338. _cRef(1),
  339. _pmfParent(pmfParent),
  340. _iColumnOffset(-1)
  341. {
  342. ASSERT(_hdpaNamespaces == NULL);
  343. if (_pmfParent)
  344. {
  345. _pmfParent->AddRef();
  346. _fDontMerge = _pmfParent->_fDontMerge;
  347. _fCDBurn = _pmfParent->_fCDBurn;
  348. _fInShellView = _pmfParent->_fInShellView;
  349. _dwDropEffect = _pmfParent->_dwDropEffect;
  350. }
  351. else
  352. {
  353. _fDontMerge = IsEqualCLSID(_clsid, CLSID_CompositeFolder);
  354. _fCDBurn = IsEqualCLSID(_clsid, CLSID_CDBurnFolder);
  355. }
  356. DllAddRef();
  357. }
  358. CMergedFolder::~CMergedFolder()
  359. {
  360. SetOwner(NULL);
  361. ILFree(_pidl);
  362. _FreeNamespaces();
  363. _FreeObjects();
  364. ATOMICRELEASE(_pmfParent);
  365. ATOMICRELEASE(_pstg);
  366. DllRelease();
  367. }
  368. // CMergedFolderglobal CreateInstance method for da class factory
  369. HRESULT CMergedFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  370. {
  371. // aggregation checking is handled in class factory
  372. HRESULT hr = E_OUTOFMEMORY;
  373. CMergedFolder* pmf = new CMergedFolder(NULL, CLSID_MergedFolder);
  374. if (pmf)
  375. {
  376. hr = pmf->QueryInterface(riid, ppv);
  377. pmf->Release();
  378. }
  379. return hr;
  380. }
  381. HRESULT CCompositeFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  382. {
  383. // aggregation checking is handled in class factory
  384. HRESULT hr = E_OUTOFMEMORY;
  385. CMergedFolder* pmf = new CMergedFolder(NULL, CLSID_CompositeFolder);
  386. if (pmf)
  387. {
  388. hr = pmf->QueryInterface(riid, ppv);
  389. pmf->Release();
  390. }
  391. return hr;
  392. }
  393. #ifdef TESTING_COMPOSITEFOLDER
  394. COMPFOLDERINIT s_rgcfiTripleD[] = {
  395. {CFITYPE_CSIDL, CSIDL_DRIVES, L"Drives"},
  396. {CFITYPE_PIDL, (int)&c_idlDesktop, L"Desktop"},
  397. {CFITYPE_PATH, (int)L"::{450d8fba-ad25-11d0-98a8-0800361b1103}", L"MyDocs"}
  398. };
  399. STDAPI CTripleD_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  400. {
  401. ICompositeFolder *pcf;
  402. HRESULT hr = CCompositeFolder_CreateInstance(punkOuter, IID_PPV_ARG(ICompositeFolder, &pcf));
  403. if (SUCCEEDED(hr))
  404. {
  405. hr = pcf->InitComposite(0x8877, CLSID_TripleD, CFINITF_FLAT, ARRAYSIZE(s_rgcfiTripleD), s_rgcfiTripleD);
  406. if (SUCCEEDED(hr))
  407. {
  408. hr = pcf->QueryInterface(riid, ppv);
  409. }
  410. pcf->Release();
  411. }
  412. return hr;
  413. }
  414. #endif //TESTING_COMPOSITEFOLDER
  415. STDMETHODIMP CMergedFolder::QueryInterface(REFIID riid, void **ppv)
  416. {
  417. static const QITAB qit[] = {
  418. QITABENTMULTI(CMergedFolder, IShellFolder, IAugmentedShellFolder),
  419. QITABENT (CMergedFolder, IAugmentedShellFolder),
  420. QITABENT (CMergedFolder, IAugmentedShellFolder2),
  421. QITABENT (CMergedFolder, IAugmentedShellFolder3),
  422. QITABENT (CMergedFolder, IShellFolder2),
  423. QITABENT (CMergedFolder, IShellService),
  424. QITABENT (CMergedFolder, ITranslateShellChangeNotify),
  425. QITABENT (CMergedFolder, IStorage),
  426. QITABENT (CMergedFolder, IShellIconOverlay),
  427. QITABENTMULTI(CMergedFolder, IPersist, IPersistFolder2),
  428. QITABENTMULTI(CMergedFolder, IPersistFolder, IPersistFolder2),
  429. QITABENT (CMergedFolder, IPersistFolder2),
  430. QITABENT (CMergedFolder, IPersistPropertyBag),
  431. QITABENT (CMergedFolder, ICompositeFolder),
  432. QITABENT (CMergedFolder, IItemNameLimits),
  433. { 0 },
  434. };
  435. if (IsEqualIID(CLSID_MergedFolder, riid))
  436. {
  437. *ppv = this;
  438. AddRef();
  439. return S_OK;
  440. }
  441. return QISearch(this, qit, riid, ppv);
  442. }
  443. STDMETHODIMP_(ULONG) CMergedFolder::AddRef()
  444. {
  445. return InterlockedIncrement(&_cRef);
  446. }
  447. STDMETHODIMP_(ULONG) CMergedFolder::Release()
  448. {
  449. ASSERT( 0 != _cRef );
  450. ULONG cRef = InterlockedDecrement(&_cRef);
  451. if ( 0 == cRef )
  452. {
  453. delete this;
  454. }
  455. return cRef;
  456. }
  457. // get the count from the enumerated items.
  458. int CMergedFolder::_ObjectCount() const
  459. {
  460. return _hdpaObjects ? DPA_GetPtrCount(_hdpaObjects) : 0;
  461. }
  462. CMergedFldrItem *CMergedFolder::_GetObject(int i)
  463. {
  464. return _hdpaObjects ? (CMergedFldrItem *)DPA_GetPtr(_hdpaObjects, i) : NULL;
  465. }
  466. int CMergedFolder::_NamespaceCount() const
  467. {
  468. return _hdpaNamespaces ? DPA_GetPtrCount(_hdpaNamespaces) : 0;
  469. }
  470. // Retrieves a pointer to a source _Namespace descriptor associated with
  471. // the specified lookup index.
  472. HRESULT CMergedFolder::_Namespace(int iIndex, CMergedFldrNamespace **ppns)
  473. {
  474. *ppns = NULL;
  475. if ((iIndex >= 0) && (iIndex < _NamespaceCount()))
  476. *ppns = _Namespace(iIndex);
  477. return *ppns ? S_OK : E_INVALIDARG;
  478. }
  479. // given an index for the name space return it.
  480. CMergedFldrNamespace* CMergedFolder::_Namespace(int iNamespace)
  481. {
  482. if (!_hdpaNamespaces)
  483. return NULL;
  484. return (CMergedFldrNamespace*)DPA_GetPtr(_hdpaNamespaces, iNamespace);
  485. }
  486. // Determine whether pidls from the two namespaces should be merged
  487. // The NULL namespace is a wildcard that always merges (if merging is permitted at all)
  488. BOOL CMergedFolder::_ShouldMergeNamespaces(CMergedFldrNamespace *pns1, CMergedFldrNamespace *pns2)
  489. {
  490. // Early-out: Identical namespaces can be merged (even if merging
  491. // is globally disabled)
  492. if (pns1 == pns2)
  493. {
  494. return TRUE;
  495. }
  496. // Early-out: Merging globally disabled
  497. if (_fDontMerge)
  498. {
  499. return FALSE;
  500. }
  501. // Early-out: Merging globally enabled
  502. if (!_fPartialMerge)
  503. {
  504. return TRUE;
  505. }
  506. if (!pns1 || !pns2)
  507. {
  508. return TRUE; // wildcard
  509. }
  510. if (!(pns1->FolderAttrib() & ASFF_MERGESAMEGUID))
  511. {
  512. // this namespace will merge with anybody!
  513. return TRUE;
  514. }
  515. // Source namespace will merge only with namespaces of the same GUID
  516. // See if destination namespace has the same GUID
  517. return IsEqualGUID(pns1->GetGUID(), pns2->GetGUID());
  518. }
  519. // Determine whether pidls from the two namespaces should be merged
  520. // NAmespace -1 is a wildcard that always merges (if merging is permitted at all)
  521. BOOL CMergedFolder::_ShouldMergeNamespaces(int iNS1, int iNS2)
  522. {
  523. // Early-out: Merging globally disabled
  524. if (_fDontMerge)
  525. {
  526. return FALSE;
  527. }
  528. // Early-out: Merging globally enabled
  529. if (!_fPartialMerge)
  530. {
  531. return TRUE;
  532. }
  533. if (iNS1 < 0 || iNS2 < 0)
  534. {
  535. return TRUE; // wildcard
  536. }
  537. return _ShouldMergeNamespaces(_Namespace(iNS1), _Namespace(iNS2));
  538. }
  539. // check to see if the IDLIST we are given is a wrapped one.
  540. HRESULT CMergedFolder::_IsWrap(LPCITEMIDLIST pidl)
  541. {
  542. HRESULT hr = E_INVALIDARG;
  543. if (pidl)
  544. {
  545. ASSERT(IS_VALID_PIDL(pidl));
  546. PAUGM_IDWRAP pWrap = (PAUGM_IDWRAP)pidl;
  547. if ((pWrap->cb >= sizeof(AUGM_IDWRAP)) &&
  548. (pWrap->ulTag == AUGM_WRAPTAG) &&
  549. (pWrap->ulVersion == AUGM_WRAPVERSION_2_0))
  550. {
  551. hr = S_OK;
  552. }
  553. else if (ILFindHiddenID(pidl, IDLHID_PARENTFOLDER))
  554. {
  555. hr = S_OK;
  556. }
  557. }
  558. return hr;
  559. }
  560. // STRRET_OFFSET has no meaning in context of the pidl wrapper.
  561. // We can either calculate the offset into the wrapper, or allocate
  562. // a wide char for the name. For expedience, we'll allocate the name.
  563. HRESULT CMergedFolder::_FixStrRetOffset(LPCITEMIDLIST pidl, STRRET *psr)
  564. {
  565. HRESULT hr = S_OK;
  566. if (psr->uType == STRRET_OFFSET)
  567. {
  568. UINT cch = lstrlenA(STRRET_OFFPTR(pidl, psr));
  569. LPWSTR pwszName = (LPWSTR)SHAlloc((cch + 1) * sizeof(WCHAR));
  570. if (pwszName)
  571. {
  572. SHAnsiToUnicode(STRRET_OFFPTR(pidl, psr), pwszName, cch + 1);
  573. pwszName[cch] = 0;
  574. psr->pOleStr = pwszName;
  575. psr->uType = STRRET_WSTR;
  576. }
  577. else
  578. hr = E_OUTOFMEMORY;
  579. }
  580. return hr;
  581. }
  582. // is the object a folder?
  583. BOOL CMergedFolder::_IsFolder(LPCITEMIDLIST pidl)
  584. {
  585. ULONG rgf = SFGAO_FOLDER | SFGAO_STREAM;
  586. return SUCCEEDED(GetAttributesOf(1, &pidl, &rgf)) && (SFGAO_FOLDER == (rgf & (SFGAO_FOLDER | SFGAO_STREAM)));
  587. }
  588. // the number of source _Namespace pidls in the wrap.
  589. ULONG CMergedFolder::_GetSourceCount(LPCITEMIDLIST pidl)
  590. {
  591. if (SUCCEEDED(_IsWrap(pidl)))
  592. {
  593. if (ILFindHiddenID(pidl, IDLHID_PARENTFOLDER))
  594. {
  595. return 1;
  596. }
  597. else
  598. {
  599. PAUGM_IDWRAP pWrap = (PAUGM_IDWRAP)pidl;
  600. return pWrap->cSrcs;
  601. }
  602. }
  603. return 0;
  604. }
  605. // Creates an IDLIST for CMergedFolder that wraps a single source pidl.
  606. HRESULT CMergedFolder::_CreateWrap(LPCITEMIDLIST pidlSrc, UINT nSrcID, LPITEMIDLIST *ppidlWrap)
  607. {
  608. *ppidlWrap = NULL; // incase of failure
  609. LPITEMIDLIST pidlSrcWithID;
  610. HRESULT hr = SHILClone(pidlSrc, &pidlSrcWithID);
  611. if (SUCCEEDED(hr))
  612. {
  613. hr = E_OUTOFMEMORY;
  614. if (!ILFindHiddenID(pidlSrcWithID, IDLHID_PARENTFOLDER))
  615. {
  616. AUGM_NAMESPACE ans = { {sizeof(ans), AUGM_NS_CURRENTVERSION, IDLHID_PARENTFOLDER} , nSrcID };
  617. pidlSrcWithID = ILAppendHiddenID((LPITEMIDLIST)pidlSrcWithID, &ans.hid);
  618. }
  619. if (pidlSrcWithID)
  620. {
  621. UINT cbAlloc = sizeof(AUGM_IDWRAP) + CB_IDLIST_TERMINATOR + // header for our IDLIST
  622. pidlSrcWithID->mkid.cb + CB_IDLIST_TERMINATOR; // wrapped IDLIST
  623. AUGM_IDWRAP *pWrap = (AUGM_IDWRAP *)_ILCreate(cbAlloc);
  624. if (pWrap)
  625. {
  626. // fill out wrapped header
  627. pWrap->cb = (USHORT)(cbAlloc - CB_IDLIST_TERMINATOR);
  628. pWrap->dwFlags = 0;
  629. pWrap->ulTag = AUGM_WRAPTAG;
  630. pWrap->ulVersion = AUGM_WRAPVERSION_2_0;
  631. pWrap->cSrcs = 1;
  632. // copy the IDLIST with the hidden data into the wrapping object
  633. LPITEMIDLIST pidl = (LPITEMIDLIST)((BYTE *)pWrap + sizeof(AUGM_IDWRAP));
  634. memcpy(pidl, pidlSrcWithID, pidlSrcWithID->mkid.cb);
  635. *ppidlWrap = (LPITEMIDLIST)pWrap;
  636. hr = S_OK;
  637. }
  638. ILFree(pidlSrcWithID);
  639. }
  640. }
  641. return hr;
  642. }
  643. // does the wrapped IDLIST we are passed contain the given source ID?
  644. BOOL CMergedFolder::_ContainsSrcID(LPCITEMIDLIST pidl, UINT uSrcID)
  645. {
  646. UINT uID;
  647. for (UINT nSrc = 0; SUCCEEDED(_GetSubPidl(pidl, nSrc, &uID, NULL, NULL)); nSrc++)
  648. {
  649. if (uID == uSrcID)
  650. return TRUE;
  651. }
  652. return FALSE;
  653. }
  654. // returns new pidl in *ppidl free of nSrcID
  655. HRESULT CMergedFolder::_WrapRemoveIDList(LPITEMIDLIST pidlWrap, UINT nSrcID, LPITEMIDLIST *ppidl)
  656. {
  657. ASSERT(IS_VALID_WRITE_PTR(ppidl, LPITEMIDLIST));
  658. *ppidl = NULL;
  659. HRESULT hr = _IsWrap(pidlWrap);
  660. if (SUCCEEDED(hr))
  661. {
  662. UINT uID;
  663. LPITEMIDLIST pidl;
  664. for (UINT i = 0; SUCCEEDED(hr) && SUCCEEDED(_GetSubPidl(pidlWrap, i, &uID, &pidl, NULL)); i++)
  665. {
  666. if (uID != nSrcID)
  667. hr = _WrapAddIDList(pidl, uID, ppidl);
  668. ILFree(pidl);
  669. }
  670. }
  671. return hr;
  672. }
  673. HRESULT CMergedFolder::_WrapRemoveIDListAbs(LPITEMIDLIST pidlWrapAbs, UINT nSrcID, LPITEMIDLIST *ppidlAbs)
  674. {
  675. ASSERT(ppidlAbs);
  676. HRESULT hr = E_OUTOFMEMORY;
  677. *ppidlAbs = ILCloneParent(pidlWrapAbs);
  678. if (*ppidlAbs)
  679. {
  680. LPITEMIDLIST pidlLast;
  681. hr = _WrapRemoveIDList(ILFindLastID(pidlWrapAbs), nSrcID, &pidlLast);
  682. if (SUCCEEDED(hr))
  683. {
  684. // shilappend frees pidlLast
  685. hr = SHILAppend(pidlLast, ppidlAbs);
  686. }
  687. }
  688. return hr;
  689. }
  690. // Adds a source pidl to *ppidlWrap (IN/OUT param!)
  691. HRESULT CMergedFolder::_WrapAddIDList(LPCITEMIDLIST pidlSrc, UINT nSrcID, LPITEMIDLIST* ppidlWrap)
  692. {
  693. HRESULT hr;
  694. if (!*ppidlWrap)
  695. {
  696. // called as a create, rather than append
  697. hr = _CreateWrap(pidlSrc, nSrcID, ppidlWrap);
  698. }
  699. else
  700. {
  701. // check to see if we already have the ID in this IDLIST we are wrapping onto.
  702. LPITEMIDLIST pidlSrcWithID;
  703. hr = SHILClone(pidlSrc, &pidlSrcWithID);
  704. if (SUCCEEDED(hr))
  705. {
  706. hr = E_OUTOFMEMORY;
  707. if (!ILFindHiddenID(pidlSrcWithID, IDLHID_PARENTFOLDER))
  708. {
  709. AUGM_NAMESPACE ans = { {sizeof(ans), AUGM_NS_CURRENTVERSION, IDLHID_PARENTFOLDER} , nSrcID };
  710. pidlSrcWithID = ILAppendHiddenID((LPITEMIDLIST)pidlSrcWithID, &ans.hid);
  711. }
  712. // ok, we have an IDLIST that we can use to append to this object.
  713. if (pidlSrcWithID)
  714. {
  715. BOOL fOtherSrcIDsExist = TRUE;
  716. // check to see if this ID already exists within the wrap idlist.
  717. if (*ppidlWrap && _ContainsSrcID(*ppidlWrap, nSrcID))
  718. {
  719. LPITEMIDLIST pidlFree = *ppidlWrap;
  720. if (SUCCEEDED(_WrapRemoveIDList(pidlFree, nSrcID, ppidlWrap)))
  721. {
  722. ILFree(pidlFree);
  723. }
  724. fOtherSrcIDsExist = (*ppidlWrap != NULL);
  725. }
  726. if (fOtherSrcIDsExist)
  727. {
  728. // now compute the new size of the IDLIST. (*ppidlWrap has been updated);
  729. PAUGM_IDWRAP pWrap = (PAUGM_IDWRAP)*ppidlWrap;
  730. SHORT cbOld = pWrap->cb;
  731. SHORT cbNew = cbOld + (pidlSrcWithID->mkid.cb + CB_IDLIST_TERMINATOR); // extra terminator is appended.
  732. pWrap = (PAUGM_IDWRAP)SHRealloc(pWrap, cbNew + CB_IDLIST_TERMINATOR);
  733. if (pWrap)
  734. {
  735. // copy the new idlist and its hidden payload (ensure we are terminated)
  736. memcpy(((BYTE*)pWrap)+ cbOld, pidlSrcWithID, cbNew-cbOld);
  737. *((UNALIGNED SHORT*)(((BYTE*)pWrap)+ cbNew)) = 0;
  738. pWrap->cb += cbNew-cbOld;
  739. pWrap->cSrcs++;
  740. hr = S_OK;
  741. }
  742. *ppidlWrap = (LPITEMIDLIST)pWrap;
  743. }
  744. else
  745. {
  746. hr = _CreateWrap(pidlSrc, nSrcID, ppidlWrap);
  747. }
  748. ILFree(pidlSrcWithID);
  749. }
  750. }
  751. }
  752. return hr;
  753. }
  754. // used to itterate through the sub pidls in the wrapped pidl
  755. // all out params optional
  756. //
  757. // out:
  758. // *ppidl alias into pidlWrap (nested pidl)
  759. HRESULT CMergedFolder::_GetSubPidl(LPCITEMIDLIST pidlWrap, int i, UINT *pnSrcID, LPITEMIDLIST *ppidl, CMergedFldrNamespace **ppns)
  760. {
  761. if (pnSrcID)
  762. *pnSrcID = -1;
  763. if (ppidl)
  764. *ppidl = NULL;
  765. if (ppns)
  766. *ppns = NULL;
  767. HRESULT hr = _IsWrap(pidlWrap);
  768. if (SUCCEEDED(hr))
  769. {
  770. if ((UINT)i < _GetSourceCount(pidlWrap))
  771. {
  772. PAUGM_NAMESPACE pans = (PAUGM_NAMESPACE)ILFindHiddenID(pidlWrap, IDLHID_PARENTFOLDER);
  773. if (!pans)
  774. {
  775. PAUGM_IDWRAP pWrap = (PAUGM_IDWRAP)pidlWrap;
  776. LPITEMIDLIST pidlSrc = (LPITEMIDLIST)(((BYTE *)pWrap) + sizeof(AUGM_IDWRAP));
  777. while (i--)
  778. {
  779. // advance to next item
  780. SHORT cb = pidlSrc->mkid.cb;
  781. pidlSrc = (LPITEMIDLIST)(((BYTE *)pidlSrc) + cb + CB_IDLIST_TERMINATOR);
  782. }
  783. if (pnSrcID || ppns)
  784. {
  785. PAUGM_NAMESPACE pans = (PAUGM_NAMESPACE)ILFindHiddenID(pidlSrc, IDLHID_PARENTFOLDER);
  786. ASSERTMSG((pans != NULL), "Failed to find hidden _Namespace in pidlWrap");
  787. if (pans && pnSrcID)
  788. *pnSrcID = pans->uSrcID;
  789. if (pans && ppns)
  790. hr = _Namespace(pans->uSrcID, ppns);
  791. }
  792. if (SUCCEEDED(hr) && ppidl)
  793. {
  794. hr = SHILClone(pidlSrc, ppidl);
  795. }
  796. }
  797. else
  798. {
  799. if (pnSrcID)
  800. *pnSrcID = pans->uSrcID;
  801. if (ppns)
  802. hr = _Namespace(pans->uSrcID, ppns);
  803. if (SUCCEEDED(hr) && ppidl)
  804. {
  805. hr = SHILClone(pidlWrap, ppidl);
  806. }
  807. }
  808. }
  809. else
  810. {
  811. hr = E_INVALIDARG;
  812. }
  813. }
  814. if (SUCCEEDED(hr) && ppidl)
  815. {
  816. // we need to strip away the hidden id that marks this guy as merged.
  817. // this is because the pidl we're returning is supposed to be a child of one of our
  818. // namespaces we're merging, so it should know absolutely nothing about being merged.
  819. // these guys used to slip through and cause problems.
  820. ILRemoveHiddenID(*ppidl, IDLHID_PARENTFOLDER);
  821. }
  822. ASSERT(!ppidl || (ILFindLastID(*ppidl) == *ppidl));
  823. return hr;
  824. }
  825. // function to compare two opaque pidls.
  826. // this is helpful since in the non-merged case, there's some difficulty
  827. // getting defview to contain items with the same name. we need a way to
  828. // compare two pidls to say "yes defview, these pidls are actually different!"
  829. // note that the actual order doesn't matter, as long as the comparison
  830. // is consistent (since this is used in sorting functions).
  831. int CMergedFolder::_CompareArbitraryPidls(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  832. {
  833. UINT iRet;
  834. UINT cbItem1 = ILGetSize(pidl1);
  835. UINT cbItem2 = ILGetSize(pidl2);
  836. if (cbItem1 != cbItem2)
  837. {
  838. iRet = (cbItem1 < cbItem2) ? 1 : -1;
  839. }
  840. else
  841. {
  842. iRet = memcmp(pidl1, pidl2, cbItem1);
  843. ASSERTMSG(iRet != 0, "no two pidls from the enumerators should be EXACTLY alike!");
  844. }
  845. return iRet;
  846. }
  847. int CMergedFolder::_Compare(void *pv1, void *pv2, LPARAM lParam)
  848. {
  849. int iRet = -1;
  850. CMergedFldrItem* pmfiEnum1 = (CMergedFldrItem*)pv1;
  851. CMergedFldrItem* pmfiEnum2 = (CMergedFldrItem*)pv2;
  852. if (pmfiEnum1 && pmfiEnum2)
  853. {
  854. // Are these two items of different types?
  855. if (BOOLIFY(pmfiEnum1->GetFolderAttrib() & SFGAO_FOLDER) ^ BOOLIFY(pmfiEnum2->GetFolderAttrib() & SFGAO_FOLDER))
  856. {
  857. // Yes. Then Folders sort before items.
  858. iRet = BOOLIFY(pmfiEnum1->GetFolderAttrib() & SFGAO_FOLDER) ? 1 : -1;
  859. }
  860. else // They are of the same type. Then compare by name
  861. {
  862. iRet = lstrcmpi(pmfiEnum1->GetDisplayName(), pmfiEnum2->GetDisplayName());
  863. if (iRet == 0)
  864. {
  865. CMergedFolder *pmf = (CMergedFolder *) lParam;
  866. if (!pmf->_ShouldMergeNamespaces(pmfiEnum1->GetNamespaceID(), pmfiEnum2->GetNamespaceID()))
  867. {
  868. // these items cannot be merged,
  869. // force iRet to be nonzero. the only reason why this comparison
  870. // has to be well-defined is so we can pass our ASSERTs that the
  871. // list is sorted using this comparison function.
  872. iRet = _CompareArbitraryPidls(pmfiEnum1->GetIDList(), pmfiEnum2->GetIDList());
  873. }
  874. }
  875. }
  876. }
  877. return iRet;
  878. }
  879. void *CMergedFolder::_Merge(UINT uMsg, void *pv1, void *pv2, LPARAM lParam)
  880. {
  881. CMergedFolder*pmf = (CMergedFolder*)lParam;
  882. void * pvRet = pv1;
  883. switch (uMsg)
  884. {
  885. case DPAMM_MERGE:
  886. {
  887. UINT nSrcID;
  888. LPITEMIDLIST pidl;
  889. CMergedFldrItem* pitemSrc = (CMergedFldrItem*)pv2;
  890. if (SUCCEEDED(pmf->_GetSubPidl(pitemSrc->GetIDList(), 0, &nSrcID, &pidl, NULL)))
  891. {
  892. // add pidl from src to dest
  893. CMergedFldrItem* pitemDest = (CMergedFldrItem*)pv1;
  894. pmf->_WrapAddIDList(pidl, nSrcID, &pitemDest->_pidlWrap);
  895. ILFree(pidl);
  896. }
  897. }
  898. break;
  899. case DPAMM_INSERT:
  900. {
  901. CMergedFldrItem* pmfiNew = new CMergedFldrItem;
  902. if (pmfiNew)
  903. {
  904. CMergedFldrItem* pmfiSrc = (CMergedFldrItem*)pv1;
  905. if (!pmfiNew->Init(pmfiSrc))
  906. {
  907. delete pmfiNew;
  908. pmfiNew = NULL;
  909. }
  910. }
  911. pvRet = pmfiNew;
  912. }
  913. break;
  914. default:
  915. ASSERT(0);
  916. }
  917. return pvRet;
  918. }
  919. typedef struct
  920. {
  921. LPTSTR pszDisplayName;
  922. BOOL fFolder;
  923. CMergedFolder *self;
  924. int iNamespace;
  925. } SEARCH_FOR_PIDL;
  926. int CALLBACK CMergedFolder::_SearchByName(void *p1, void *p2, LPARAM lParam)
  927. {
  928. SEARCH_FOR_PIDL* psfp = (SEARCH_FOR_PIDL*)p1;
  929. CMergedFldrItem* pmfiEnum = (CMergedFldrItem*)p2;
  930. // Are they of different types?
  931. if (BOOLIFY(pmfiEnum->GetFolderAttrib() & SFGAO_FOLDER) ^ psfp->fFolder)
  932. {
  933. // Yes.
  934. return psfp->fFolder ? 1 : -1;
  935. }
  936. // They are of the same type. Then compare by name
  937. int iRc = StrCmpI(psfp->pszDisplayName, pmfiEnum->GetDisplayName());
  938. if (iRc)
  939. return iRc;
  940. // They are the same name. But if they're not allowed to merge, then
  941. // they're really different.
  942. if (!psfp->self->_ShouldMergeNamespaces(pmfiEnum->GetNamespaceID(), psfp->iNamespace))
  943. {
  944. // Sort by namespace ID
  945. return psfp->iNamespace - pmfiEnum->GetNamespaceID();
  946. }
  947. // I guess they're really equal
  948. return 0;
  949. }
  950. // IPersistFolder::Initialize()
  951. STDMETHODIMP CMergedFolder::Initialize(LPCITEMIDLIST pidl)
  952. {
  953. return Pidl_Set(&_pidl, pidl) ? S_OK : E_OUTOFMEMORY;
  954. }
  955. // IPersistFolder2::GetCurFolder()
  956. STDMETHODIMP CMergedFolder::GetCurFolder(LPITEMIDLIST *ppidl)
  957. {
  958. if (_pidl)
  959. return SHILClone(_pidl, ppidl);
  960. else
  961. {
  962. *ppidl = NULL;
  963. return S_FALSE;
  964. }
  965. }
  966. // IPersistPropertyBag
  967. void CMergedFolder::_GetKeyForProperty(LPWSTR pszName, LPWSTR pszValue, LPWSTR pszBuffer, INT cchBuffer)
  968. {
  969. StrCpyNW(pszBuffer, L"MergedFolder\\", cchBuffer);
  970. StrCatBuffW(pszBuffer, pszName, cchBuffer);
  971. StrCatBuffW(pszBuffer, pszValue, cchBuffer);
  972. }
  973. HRESULT CMergedFolder::_AddNameSpaceFromPropertyBag(IPropertyBag *ppb, LPWSTR pszName)
  974. {
  975. WCHAR szKey[MAX_PATH];
  976. // get the path of the folder
  977. WCHAR szPath[MAX_PATH];
  978. LPITEMIDLIST pidl = NULL;
  979. _GetKeyForProperty(pszName, L"Path", szKey, ARRAYSIZE(szKey));
  980. HRESULT hr = SHPropertyBag_ReadStr(ppb, szKey, szPath, ARRAYSIZE(szPath));
  981. if (SUCCEEDED(hr))
  982. {
  983. // we picked a path from the property bag, so lets convert
  984. // that to an IDLIST so we can do something with it.
  985. hr = SHILCreateFromPath(szPath, &pidl, NULL);
  986. }
  987. else
  988. {
  989. // attempt to determine the CSIDL for the folder we are going
  990. // to show, if that works then convert it to an IDLIST
  991. // so that we can pass it to AddNamespace.
  992. _GetKeyForProperty(pszName, L"CSIDL", szKey, ARRAYSIZE(szKey));
  993. int csidl;
  994. hr = SHPropertyBag_ReadDWORD(ppb, szKey, (DWORD*)&csidl);
  995. if (SUCCEEDED(hr))
  996. {
  997. hr = SHGetSpecialFolderLocation(NULL, csidl, &pidl);
  998. }
  999. }
  1000. if (SUCCEEDED(hr) && pidl)
  1001. {
  1002. // we succeeded in getting a location for the folder we
  1003. // are going to add, so lets pick up the rest of the
  1004. // information on that object.
  1005. GUID guid;
  1006. GUID *pguid = NULL;
  1007. _GetKeyForProperty(pszName, L"GUID", szKey, ARRAYSIZE(szKey));
  1008. pguid = SUCCEEDED(SHPropertyBag_ReadGUID(ppb, szKey, &guid)) ? &guid:NULL;
  1009. DWORD dwFlags = 0;
  1010. _GetKeyForProperty(pszName, L"Flags", szKey, ARRAYSIZE(szKey));
  1011. SHPropertyBag_ReadDWORD(ppb, szKey, &dwFlags);
  1012. hr = AddNameSpace(pguid, NULL, pidl, dwFlags);
  1013. }
  1014. ILFree(pidl);
  1015. return hr;
  1016. }
  1017. HRESULT CMergedFolder::Load(IPropertyBag* ppb, IErrorLog *pErrLog)
  1018. {
  1019. SHPropertyBag_ReadGUID(ppb, L"MergedFolder\\CLSID", &_clsid); // get the folders CLSID
  1020. SHPropertyBag_ReadDWORD(ppb, L"MergedFolder\\DropEffect", &_dwDropEffect);
  1021. _fInShellView = SHPropertyBag_ReadBOOLDefRet(ppb, L"MergedFolder\\ShellView", FALSE);
  1022. WCHAR sz[MAX_PATH];
  1023. if (SUCCEEDED(SHPropertyBag_ReadStr(ppb, L"MergedFolder\\Folders", sz, ARRAYSIZE(sz))))
  1024. {
  1025. LPWSTR pszName = sz;
  1026. while (pszName && *pszName)
  1027. {
  1028. LPWSTR pszNext = StrChrW(pszName, L',');
  1029. if (pszNext)
  1030. {
  1031. *pszNext = 0;
  1032. pszNext++;
  1033. }
  1034. _AddNameSpaceFromPropertyBag(ppb, pszName);
  1035. pszName = pszNext;
  1036. }
  1037. }
  1038. return S_OK;
  1039. }
  1040. // IShellFolder
  1041. STDMETHODIMP CMergedFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
  1042. {
  1043. *ppenumIDList = NULL;
  1044. HRESULT hr = E_FAIL;
  1045. if (_hdpaNamespaces)
  1046. {
  1047. _FreeObjects();
  1048. hr = CMergedFldrEnum_CreateInstance(this, grfFlags, ppenumIDList);
  1049. }
  1050. if (SUCCEEDED(hr) && _fInShellView)
  1051. {
  1052. Register(NULL, 0, 0);
  1053. }
  1054. return hr;
  1055. }
  1056. HRESULT CMergedFolder::_CreateWithCLSID(CLSID clsid, CMergedFolder **ppmf)
  1057. {
  1058. *ppmf = new CMergedFolder(this, clsid);
  1059. return *ppmf ? S_OK : E_OUTOFMEMORY;
  1060. }
  1061. BOOL CMergedFolder::_ShouldSuspend(REFGUID rguid)
  1062. {
  1063. return FALSE;
  1064. }
  1065. // create a new CMergedFolder from the first element in pidlWrap
  1066. // this is our private init method, IPersistFolder::Initialize() is how we
  1067. // get inited at our junction point.
  1068. HRESULT CMergedFolder::_New(LPCITEMIDLIST pidlWrap, CMergedFolder **ppmf)
  1069. {
  1070. ASSERT(ppmf);
  1071. *ppmf = NULL;
  1072. HRESULT hr = E_OUTOFMEMORY; // assume the worst
  1073. // just want the first element in pidlWrap
  1074. LPITEMIDLIST pidlFirst = ILCloneFirst(pidlWrap);
  1075. if (pidlFirst)
  1076. {
  1077. if (_IsFolder(pidlFirst))
  1078. {
  1079. hr = _CreateWithCLSID(_clsid, ppmf);
  1080. if (SUCCEEDED(hr) && _pidl)
  1081. {
  1082. hr = SHILCombine(_pidl, pidlFirst, &(*ppmf)->_pidl);
  1083. if (FAILED(hr))
  1084. {
  1085. (*ppmf)->Release();
  1086. *ppmf = NULL;
  1087. }
  1088. }
  1089. }
  1090. else
  1091. {
  1092. hr = E_NOINTERFACE;
  1093. }
  1094. ILFree(pidlFirst);
  1095. }
  1096. return hr;
  1097. }
  1098. void CMergedFolder::_AddAllOtherNamespaces(LPITEMIDLIST *ppidl)
  1099. {
  1100. TCHAR szName[MAX_PATH];
  1101. if (SUCCEEDED(DisplayNameOf(static_cast<CSFStorage *>(this), *ppidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName))))
  1102. {
  1103. CMergedFldrNamespace *pns;
  1104. for (int n = 0; pns = _Namespace(n); n++)
  1105. {
  1106. if (FAILED(_GetSubPidl(*ppidl, n, NULL, NULL, NULL)))
  1107. {
  1108. IBindCtx *pbc;
  1109. WIN32_FIND_DATA wfd = {0};
  1110. wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
  1111. if (SUCCEEDED(SHCreateFileSysBindCtx(&wfd, &pbc)))
  1112. {
  1113. LPITEMIDLIST pidlNamespace;
  1114. if (SUCCEEDED(pns->Folder()->ParseDisplayName(NULL, pbc, szName, NULL, &pidlNamespace, NULL)))
  1115. {
  1116. _WrapAddIDList(pidlNamespace, n, ppidl);
  1117. ILFree(pidlNamespace);
  1118. }
  1119. pbc->Release();
  1120. }
  1121. }
  1122. }
  1123. }
  1124. }
  1125. STDMETHODIMP CMergedFolder::BindToObject(LPCITEMIDLIST pidlWrap, LPBC pbc, REFIID riid, void **ppv)
  1126. {
  1127. ASSERT(IS_VALID_PIDL(pidlWrap));
  1128. *ppv = NULL;
  1129. HRESULT hr = E_OUTOFMEMORY;
  1130. LPITEMIDLIST pidlRewrappedFirst;
  1131. if (_fDontMerge)
  1132. {
  1133. // this doesn't contain a wrap consisting of all namespaces but only one instead
  1134. LPITEMIDLIST pidlWrapFirst = ILCloneFirst(pidlWrap);
  1135. if (pidlWrapFirst)
  1136. {
  1137. TCHAR szName[MAX_PATH];
  1138. hr = DisplayNameOf(reinterpret_cast<IShellFolder *>(this), pidlWrapFirst, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  1139. if (SUCCEEDED(hr))
  1140. {
  1141. // we want to round-trip the name so the non-merged pidl gets remerged
  1142. // during a bind (so you dont get just one namespace from here on down)
  1143. hr = ParseDisplayName(NULL, NULL, szName, NULL, &pidlRewrappedFirst, NULL);
  1144. }
  1145. ILFree(pidlWrapFirst);
  1146. }
  1147. }
  1148. else
  1149. {
  1150. pidlRewrappedFirst = ILCloneFirst(pidlWrap);
  1151. if (pidlRewrappedFirst)
  1152. {
  1153. hr = S_OK;
  1154. if (_fCDBurn && _IsFolder(pidlRewrappedFirst))
  1155. {
  1156. // in the cdburn case we need to fake up the other namespaces in the pidl we're about to bind to.
  1157. // this is so when we navigate into a subfolder that only exists on the CD and not the staging area,
  1158. // if a file is later added to the staging area it'll still be merged in.
  1159. _AddAllOtherNamespaces(&pidlRewrappedFirst);
  1160. }
  1161. }
  1162. }
  1163. if (SUCCEEDED(hr))
  1164. {
  1165. // just in case
  1166. ASSERT(SUCCEEDED(_IsWrap(pidlRewrappedFirst)));
  1167. LPCITEMIDLIST pidlNext = _ILNext(pidlWrap);
  1168. CMergedFolder *pmf;
  1169. hr = _New(pidlRewrappedFirst, &pmf);
  1170. if (SUCCEEDED(hr))
  1171. {
  1172. LPITEMIDLIST pidlSrc;
  1173. CMergedFldrNamespace *pns;
  1174. for (UINT i = 0; SUCCEEDED(_GetSubPidl(pidlRewrappedFirst, i, NULL, &pidlSrc, &pns)); i++)
  1175. {
  1176. hr = E_OUTOFMEMORY;
  1177. ASSERT(ILFindLastID(pidlSrc) == pidlSrc);
  1178. LPITEMIDLIST pidlSrcFirst = ILCloneFirst(pidlSrc);
  1179. if (pidlSrcFirst)
  1180. {
  1181. IShellFolder *psf;
  1182. if (SUCCEEDED(pns->Folder()->BindToObject(pidlSrcFirst, pbc, IID_PPV_ARG(IShellFolder, &psf))))
  1183. {
  1184. LPITEMIDLIST pidlAbs = ILCombine(pns->GetIDList(), pidlSrcFirst);
  1185. if (pidlAbs)
  1186. {
  1187. CMergedFldrNamespace *pnsNew = new CMergedFldrNamespace();
  1188. if (pnsNew)
  1189. {
  1190. hr = pnsNew->SetNamespace(&(pns->GetGUID()), psf, pidlAbs, pns->FolderAttrib());
  1191. if (SUCCEEDED(hr))
  1192. {
  1193. // propagate the drop folder down to the child.
  1194. hr = pnsNew->SetDropFolder(pns->GetDropFolder());
  1195. if (SUCCEEDED(hr))
  1196. {
  1197. hr = pmf->_SimpleAddNamespace(pnsNew);
  1198. if (SUCCEEDED(hr))
  1199. {
  1200. // success, _SimpleAddNamespace took ownership
  1201. pnsNew = NULL;
  1202. }
  1203. }
  1204. }
  1205. if (pnsNew)
  1206. delete pnsNew;
  1207. }
  1208. else
  1209. {
  1210. hr = E_OUTOFMEMORY;
  1211. }
  1212. ILFree(pidlAbs);
  1213. }
  1214. psf->Release();
  1215. }
  1216. ILFree(pidlSrcFirst);
  1217. }
  1218. ILFree(pidlSrc);
  1219. }
  1220. // it's possible to go through the loop without adding any namespaces.
  1221. // usually it's when BindToObject above fails -- this can happen if somebody
  1222. // puts a junction point in the merged folder (like a zip file). in that case
  1223. // we're in trouble.
  1224. if (ILIsEmpty(pidlNext))
  1225. hr = pmf->QueryInterface(riid, ppv);
  1226. else
  1227. hr = pmf->BindToObject(pidlNext, pbc, riid, ppv);
  1228. pmf->Release();
  1229. }
  1230. if (FAILED(hr) && ILIsEmpty(pidlNext))
  1231. {
  1232. // maybe it's an interface that we don't support ourselves (IStream?).
  1233. // we cant merge interfaces that we don't know about so lets just
  1234. // assume we'll pick the interface up from the default namespace in
  1235. // the wrapped pidl.
  1236. LPITEMIDLIST pidlSrc;
  1237. CMergedFldrNamespace *pns;
  1238. hr = _NamespaceForItem(pidlRewrappedFirst, ASFF_DEFNAMESPACE_BINDSTG, ASFF_DEFNAMESPACE_BINDSTG, NULL, &pidlSrc, &pns);
  1239. if (SUCCEEDED(hr))
  1240. {
  1241. hr = pns->Folder()->BindToObject(pidlSrc, pbc, riid, ppv);
  1242. ILFree(pidlSrc);
  1243. }
  1244. }
  1245. ILFree(pidlRewrappedFirst);
  1246. }
  1247. if (SUCCEEDED(hr) && _fInShellView)
  1248. {
  1249. Register(NULL, 0, 0);
  1250. }
  1251. return hr;
  1252. }
  1253. STDMETHODIMP CMergedFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  1254. {
  1255. return BindToObject(pidl, pbc, riid, ppv);
  1256. }
  1257. HRESULT CMergedFolder::_CompareSingleLevelIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1258. {
  1259. IShellFolder *psf1;
  1260. LPITEMIDLIST pidlItem1;
  1261. CMergedFldrNamespace *pns1;
  1262. HRESULT hr = _NamespaceForItem(pidl1, ASFF_DEFNAMESPACE_DISPLAYNAME, ASFF_DEFNAMESPACE_DISPLAYNAME, &psf1, &pidlItem1, &pns1);
  1263. if (SUCCEEDED(hr))
  1264. {
  1265. IShellFolder *psf2;
  1266. LPITEMIDLIST pidlItem2;
  1267. CMergedFldrNamespace *pns2;
  1268. hr = _NamespaceForItem(pidl2, ASFF_DEFNAMESPACE_DISPLAYNAME, ASFF_DEFNAMESPACE_DISPLAYNAME, &psf2, &pidlItem2, &pns2);
  1269. if (SUCCEEDED(hr))
  1270. {
  1271. // Same _Namespace? Just forward the request.
  1272. if (psf1 == psf2)
  1273. {
  1274. hr = psf1->CompareIDs(lParam, pidlItem1, pidlItem2);
  1275. }
  1276. else if ((pns1->FolderAttrib() & ASFF_SORTDOWN) ^ (pns2->FolderAttrib() & ASFF_SORTDOWN))
  1277. {
  1278. // One namespace marked ASFF_SORTDOWN and one not? The SORTDOWN one
  1279. // comes second.
  1280. hr = ResultFromShort((pns1->FolderAttrib() & ASFF_SORTDOWN) ? 1 : -1);
  1281. }
  1282. else
  1283. {
  1284. if (!_IsSimple(pidl1) && !_IsSimple(pidl2))
  1285. {
  1286. // Comparison heuristics:
  1287. // (1) folders take precedence over nonfolders, (2) alphanum comparison
  1288. int iFolder1 = SHGetAttributes(psf1, pidlItem1, SFGAO_FOLDER) ? 1 : 0;
  1289. int iFolder2 = SHGetAttributes(psf2, pidlItem2, SFGAO_FOLDER) ? 1 : 0;
  1290. hr = ResultFromShort(iFolder2 - iFolder1);
  1291. }
  1292. else
  1293. {
  1294. // if a pidl is simple, compare based on name only.
  1295. hr = ResultFromShort(0);
  1296. }
  1297. if (ResultFromShort(0) == hr)
  1298. {
  1299. TCHAR szName1[MAX_PATH], szName2[MAX_PATH];
  1300. if (SUCCEEDED(DisplayNameOf(psf1, pidlItem1, SHGDN_INFOLDER, szName1, ARRAYSIZE(szName1))) &&
  1301. SUCCEEDED(DisplayNameOf(psf2, pidlItem2, SHGDN_INFOLDER, szName2, ARRAYSIZE(szName2))))
  1302. {
  1303. int iRet = StrCmp(szName1, szName2); // Comparisons are by name with items of the same type.
  1304. if ((iRet == 0) &&
  1305. SUCCEEDED(DisplayNameOf(psf1, pidlItem1, SHGDN_FORPARSING | SHGDN_INFOLDER, szName1, ARRAYSIZE(szName1))) &&
  1306. SUCCEEDED(DisplayNameOf(psf2, pidlItem2, SHGDN_FORPARSING | SHGDN_INFOLDER, szName2, ARRAYSIZE(szName2))))
  1307. {
  1308. iRet = lstrcmp(szName1, szName2); // minimal behavior change for xpsp1: fall back to parsing name if theres still a tie.
  1309. if ((iRet == 0) && !_ShouldMergeNamespaces(pns1, pns2))
  1310. {
  1311. ASSERTMSG(!_fInShellView, "we shouldn't be in this code path for the start menu");
  1312. // different namespaces must compare differently in the non-merged case.
  1313. iRet = _CompareArbitraryPidls(pidlItem1, pidlItem2);
  1314. }
  1315. }
  1316. hr = ResultFromShort(iRet);
  1317. }
  1318. }
  1319. }
  1320. ILFree(pidlItem2);
  1321. }
  1322. ILFree(pidlItem1);
  1323. }
  1324. return hr;
  1325. }
  1326. STDMETHODIMP CMergedFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1327. {
  1328. // just in case
  1329. //ASSERT(!pidl1 || SUCCEEDED(_IsWrap(pidl1)));
  1330. //ASSERT(!pidl2 || SUCCEEDED(_IsWrap(pidl2)));
  1331. HRESULT hr = E_FAIL;
  1332. LPITEMIDLIST pidlFirst1 = pidl1 ? ILCloneFirst(pidl1) : NULL;
  1333. LPITEMIDLIST pidlFirst2 = pidl2 ? ILCloneFirst(pidl2) : NULL;
  1334. if (pidlFirst1 && pidlFirst2)
  1335. {
  1336. hr = _CompareSingleLevelIDs(lParam, pidlFirst1, pidlFirst2);
  1337. }
  1338. ILFree(pidlFirst1);
  1339. ILFree(pidlFirst2);
  1340. // if there was an exact match then lets compare the trailing elements of the IDLIST
  1341. // if there are some (by binding down) etc.
  1342. if (ResultFromShort(0) == hr)
  1343. {
  1344. IShellFolder *psf;
  1345. hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psf));
  1346. if (SUCCEEDED(hr))
  1347. {
  1348. hr = ILCompareRelIDs(psf, pidl1, pidl2, lParam);
  1349. psf->Release();
  1350. }
  1351. }
  1352. if (!_IsSimple(pidl1) && !_IsSimple(pidl2))
  1353. {
  1354. // if we're still the same, compare the number
  1355. // of namespaces in the pidl.
  1356. int nCount1, nCount2;
  1357. if (ResultFromShort(0) == hr)
  1358. {
  1359. nCount1 = pidl1 ? _GetSourceCount(pidl1) : 0;
  1360. nCount2 = pidl2 ? _GetSourceCount(pidl2) : 0;
  1361. hr = ResultFromShort(nCount1 - nCount2);
  1362. }
  1363. // next compare the namespaces themselves.
  1364. // basically we're only concerned with the two-namespace case, so if both pidls have
  1365. // elements from 0 or 2 namespaces theyre equal; we're worried about when one pidl has
  1366. // 1 sub-pidl in namespace 0 and the other one has 1 sub-pidl in namespace 1.
  1367. // we dont worry about 3+ namespaces and those permutations.
  1368. if ((ResultFromShort(0) == hr) && (nCount1 == 1) && (nCount2 == 1))
  1369. {
  1370. GUID guid1 = GUID_NULL, guid2 = GUID_NULL;
  1371. GetNameSpaceID(pidl1, &guid1);
  1372. GetNameSpaceID(pidl2, &guid2);
  1373. hr = ResultFromShort(memcmp(&guid1, &guid2, sizeof(GUID)));
  1374. }
  1375. }
  1376. return hr;
  1377. }
  1378. STDMETHODIMP CMergedFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
  1379. {
  1380. *ppv = NULL;
  1381. HRESULT hr;
  1382. if (IsEqualIID(riid, IID_IDropTarget))
  1383. {
  1384. hr = CMergedFldrDropTarget_CreateInstance(this, hwnd, (IDropTarget**)ppv);
  1385. }
  1386. else if (IsEqualIID(riid, IID_IShellView))
  1387. {
  1388. IShellFolderViewCB *psfvcb;
  1389. hr = CMergedFolderViewCB_CreateInstance(this, &psfvcb);
  1390. if (SUCCEEDED(hr))
  1391. {
  1392. SFV_CREATE csfv = {0};
  1393. csfv.cbSize = sizeof(csfv);
  1394. csfv.pshf = SAFECAST(this, IAugmentedShellFolder2*);
  1395. csfv.psfvcb = psfvcb;
  1396. hr = SHCreateShellFolderView(&csfv, (IShellView **)ppv);
  1397. psfvcb->Release();
  1398. }
  1399. }
  1400. else if (_fInShellView && IsEqualIID(riid, IID_ICategoryProvider))
  1401. {
  1402. IShellFolder *psf;
  1403. hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psf));
  1404. if (SUCCEEDED(hr))
  1405. {
  1406. BEGIN_CATEGORY_LIST(s_Categories)
  1407. CATEGORY_ENTRY_SCIDMAP(SCID_WHICHFOLDER, CLSID_MergedCategorizer)
  1408. END_CATEGORY_LIST()
  1409. hr = CCategoryProvider_Create(&CLSID_MergedCategorizer, &SCID_WHICHFOLDER, NULL, s_Categories, psf, riid, ppv);
  1410. psf->Release();
  1411. }
  1412. }
  1413. else if (_fInShellView && IsEqualIID(riid, IID_IContextMenu))
  1414. {
  1415. // this is pretty much what filefldr does to create its background
  1416. // context menu. we don't want to let one of our namespaces take over for the background
  1417. // context menu because then the context menu will think it's in an unmerged namespace.
  1418. // for example the new menu would then work with the storage of the child namespace
  1419. // and couldn't select the new item after its done since it has an unmerged pidl
  1420. // and the view has a merged one.
  1421. IShellFolder *psfToPass;
  1422. hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psfToPass));
  1423. if (SUCCEEDED(hr))
  1424. {
  1425. HKEY hkNoFiles = NULL;
  1426. RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("Directory\\Background"), 0, KEY_READ, &hkNoFiles);
  1427. // initialize with our merged pidl.
  1428. IContextMenuCB *pcmcb = new CDefBackgroundMenuCB(_pidl);
  1429. if (pcmcb)
  1430. {
  1431. hr = CDefFolderMenu_Create2Ex(_pidl, hwnd, 0, NULL, psfToPass, pcmcb,
  1432. 1, &hkNoFiles, (IContextMenu **)ppv);
  1433. pcmcb->Release();
  1434. }
  1435. if (hkNoFiles) // CDefFolderMenu_Create can handle NULL ok
  1436. RegCloseKey(hkNoFiles);
  1437. psfToPass->Release();
  1438. }
  1439. }
  1440. else
  1441. {
  1442. CMergedFldrNamespace *pns;
  1443. hr = _FindNamespace(ASFF_DEFNAMESPACE_VIEWOBJ, ASFF_DEFNAMESPACE_VIEWOBJ, NULL, &pns);
  1444. if (SUCCEEDED(hr))
  1445. {
  1446. hr = pns->Folder()->CreateViewObject(hwnd, riid, ppv);
  1447. }
  1448. }
  1449. return hr;
  1450. }
  1451. STDMETHODIMP CMergedFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut)
  1452. {
  1453. // attribs of the namespace root.
  1454. // scope this to the sub-namespaces?
  1455. if (!cidl || !apidl)
  1456. {
  1457. *rgfInOut &= SFGAO_FOLDER | SFGAO_FILESYSTEM |
  1458. SFGAO_LINK | SFGAO_DROPTARGET |
  1459. SFGAO_CANRENAME | SFGAO_CANDELETE |
  1460. SFGAO_CANLINK | SFGAO_CANCOPY |
  1461. SFGAO_CANMOVE | SFGAO_HASSUBFOLDER;
  1462. return S_OK;
  1463. }
  1464. HRESULT hr = S_OK;
  1465. for (UINT i = 0; SUCCEEDED(hr) && (i < cidl); i++)
  1466. {
  1467. ULONG ulAttribs = *rgfInOut;
  1468. IShellFolder* psf;
  1469. LPITEMIDLIST pidlItem;
  1470. CMergedFldrNamespace *pns;
  1471. hr = _NamespaceForItem(apidl[0], ASFF_DEFNAMESPACE_ATTRIB, ASFF_DEFNAMESPACE_ATTRIB, &psf, &pidlItem, &pns);
  1472. if (SUCCEEDED(hr))
  1473. {
  1474. ulAttribs |= SFGAO_FOLDER;
  1475. hr = psf->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlItem, &ulAttribs);
  1476. if (SUCCEEDED(hr))
  1477. {
  1478. ulAttribs = pns->FixItemAttributes(ulAttribs);
  1479. if (_fInShellView || !(*rgfInOut & SFGAO_FOLDER))
  1480. {
  1481. ulAttribs &= ~SFGAO_CANLINK; // avoid people creating links to our pidls
  1482. }
  1483. if (*rgfInOut & (SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK))
  1484. {
  1485. // allow per-type guys to do what they want.
  1486. IQueryAssociations *pqa;
  1487. DWORD dwDefEffect = DROPEFFECT_NONE;
  1488. if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST*)&pidlItem, IID_X_PPV_ARG(IQueryAssociations, NULL, &pqa))))
  1489. {
  1490. DWORD cb = sizeof(dwDefEffect);
  1491. pqa->GetData(0, ASSOCDATA_VALUE, L"DefaultDropEffect", &dwDefEffect, &cb);
  1492. pqa->Release();
  1493. }
  1494. ulAttribs |= dwDefEffect & (SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK);
  1495. }
  1496. }
  1497. ILFree(pidlItem);
  1498. }
  1499. // keep only the attributes common to all pidls.
  1500. *rgfInOut &= ulAttribs;
  1501. }
  1502. return hr;
  1503. }
  1504. STDMETHODIMP CMergedFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl, REFIID riid, UINT *prgf, void **ppv)
  1505. {
  1506. *ppv = NULL;
  1507. HRESULT hr = E_NOTIMPL;
  1508. if (IsEqualGUID(riid, IID_IContextMenu))
  1509. {
  1510. hr = _GetContextMenu(hwnd, cidl, apidl, riid, ppv);
  1511. }
  1512. else if (IsEqualGUID(riid, IID_IDropTarget) && _IsFolder(apidl[0]))
  1513. {
  1514. IShellFolder *psf;
  1515. hr = BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psf));
  1516. if (SUCCEEDED(hr))
  1517. {
  1518. hr = psf->CreateViewObject(hwnd, riid, ppv);
  1519. psf->Release();
  1520. }
  1521. }
  1522. else if ((IsEqualIID(riid, IID_IExtractImage) ||
  1523. IsEqualIID(riid, IID_IExtractLogo)) && _IsFolder(apidl[0]))
  1524. {
  1525. IShellFolder *psfThis;
  1526. hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psfThis));
  1527. if (SUCCEEDED(hr))
  1528. {
  1529. hr = CFolderExtractImage_Create(psfThis, apidl[0], riid, ppv);
  1530. psfThis->Release();
  1531. }
  1532. }
  1533. else if (IsEqualIID(riid, IID_IDataObject) && _pidl)
  1534. {
  1535. hr = SHCreateFileDataObject(_pidl, cidl, apidl, NULL, (IDataObject **)ppv);
  1536. }
  1537. else
  1538. {
  1539. hr = E_OUTOFMEMORY;
  1540. // Forward to default _Namespace for UI object
  1541. LPITEMIDLIST *apidlItems = new LPITEMIDLIST[cidl];
  1542. if (apidlItems)
  1543. {
  1544. hr = E_FAIL; // assume failure
  1545. UINT cidlItems = 0;
  1546. IShellFolder *psf, *psfKeep; // not ref counted
  1547. LPITEMIDLIST pidlItem;
  1548. for (UINT i = 0; i < cidl; i++)
  1549. {
  1550. if (SUCCEEDED(_NamespaceForItem(apidl[i], ASFF_DEFNAMESPACE_UIOBJ, ASFF_DEFNAMESPACE_UIOBJ, &psf, &pidlItem, NULL)))
  1551. {
  1552. // only keep the ones that match the default namespace for UI object
  1553. // if they dont match, too bad.
  1554. apidlItems[cidlItems++] = pidlItem;
  1555. psfKeep = psf;
  1556. }
  1557. }
  1558. if (cidlItems)
  1559. {
  1560. hr = psfKeep->GetUIObjectOf(hwnd, cidlItems, (LPCITEMIDLIST *)apidlItems, riid, NULL, ppv);
  1561. }
  1562. for (UINT j = 0; j < cidlItems; j++)
  1563. {
  1564. ILFree(apidlItems[j]);
  1565. }
  1566. delete [] apidlItems;
  1567. }
  1568. }
  1569. return hr;
  1570. }
  1571. // in:
  1572. // pidl optional, NULL means get default
  1573. // out:
  1574. // *ppidl if pidl is != NULL
  1575. HRESULT CMergedFolder::_GetFolder2(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlInner, IShellFolder2 **ppsf)
  1576. {
  1577. if (ppidlInner)
  1578. *ppidlInner = NULL;
  1579. HRESULT hr;
  1580. if (NULL == pidl)
  1581. {
  1582. CMergedFldrNamespace *pns;
  1583. hr = _FindNamespace(ASFF_DEFNAMESPACE_DISPLAYNAME, ASFF_DEFNAMESPACE_DISPLAYNAME, NULL, &pns);
  1584. if (FAILED(hr))
  1585. {
  1586. pns = _Namespace(0);
  1587. hr = pns ? S_OK : E_FAIL;
  1588. }
  1589. if (SUCCEEDED(hr))
  1590. hr = pns->Folder()->QueryInterface(IID_PPV_ARG(IShellFolder2, ppsf));
  1591. }
  1592. else
  1593. {
  1594. IShellFolder* psf;
  1595. hr = _NamespaceForItem(pidl, ASFF_DEFNAMESPACE_DISPLAYNAME, ASFF_DEFNAMESPACE_DISPLAYNAME, &psf, ppidlInner, NULL);
  1596. if (SUCCEEDED(hr))
  1597. {
  1598. hr = psf->QueryInterface(IID_PPV_ARG(IShellFolder2, ppsf));
  1599. if (FAILED(hr) && ppidlInner)
  1600. {
  1601. ILFree(*ppidlInner);
  1602. }
  1603. }
  1604. }
  1605. return hr;
  1606. }
  1607. // extended column information, these are appended after the set from the merged folder.
  1608. #define COLID_WHICHFOLDER 0x00 // column index for the merged folder location
  1609. static struct
  1610. {
  1611. const SHCOLUMNID *pscid;
  1612. UINT iTitle;
  1613. UINT cchCol;
  1614. UINT iFmt;
  1615. }
  1616. _columns[] =
  1617. {
  1618. {&SCID_WHICHFOLDER, IDS_WHICHFOLDER_COL, 20, LVCFMT_LEFT},
  1619. };
  1620. // column handler helpers
  1621. BOOL CMergedFolder::_IsOurColumn(UINT iCol)
  1622. {
  1623. return ((_iColumnOffset != -1) && ((iCol >= _iColumnOffset) && ((iCol - _iColumnOffset) < ARRAYSIZE(_columns))));
  1624. }
  1625. HRESULT CMergedFolder::_GetWhichFolderColumn(LPCITEMIDLIST pidl, LPWSTR pszBuffer, INT cchBuffer)
  1626. {
  1627. CMergedFldrNamespace *pns;
  1628. HRESULT hr = _NamespaceForItem(pidl, ASFF_DEFNAMESPACE_ATTRIB, ASFF_DEFNAMESPACE_ATTRIB, NULL, NULL, &pns);
  1629. if (SUCCEEDED(hr))
  1630. {
  1631. hr = pns->GetLocation(pszBuffer, cchBuffer);
  1632. }
  1633. return hr;
  1634. }
  1635. STDMETHODIMP CMergedFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails)
  1636. {
  1637. // do we have a column offset, or is this within the range of our the ISFs columns
  1638. HRESULT hr = E_FAIL;
  1639. if (!_IsOurColumn(iColumn))
  1640. {
  1641. IShellFolder2 *psf2;
  1642. LPITEMIDLIST pidlItem;
  1643. // get the column value from the folder.
  1644. hr = _GetFolder2(pidl, &pidlItem, &psf2);
  1645. if (SUCCEEDED(hr))
  1646. {
  1647. hr = psf2->GetDetailsOf(pidlItem, iColumn, pDetails);
  1648. psf2->Release();
  1649. ILFree(pidlItem);
  1650. }
  1651. // we failed and we don't know the column offset to handle
  1652. if (FAILED(hr) && (_iColumnOffset == -1))
  1653. _iColumnOffset = iColumn;
  1654. }
  1655. if (FAILED(hr) && _IsOurColumn(iColumn))
  1656. {
  1657. iColumn -= _iColumnOffset;
  1658. pDetails->str.uType = STRRET_CSTR; // we are returning strings
  1659. pDetails->str.cStr[0] = 0;
  1660. WCHAR szTemp[MAX_PATH];
  1661. if (!pidl)
  1662. {
  1663. pDetails->fmt = _columns[iColumn].iFmt;
  1664. pDetails->cxChar = _columns[iColumn].cchCol;
  1665. LoadString(HINST_THISDLL, _columns[iColumn].iTitle, szTemp, ARRAYSIZE(szTemp));
  1666. hr = StringToStrRet(szTemp, &(pDetails->str));
  1667. }
  1668. else if (SUCCEEDED(_IsWrap(pidl)))
  1669. {
  1670. if (iColumn == COLID_WHICHFOLDER)
  1671. {
  1672. hr = _GetWhichFolderColumn(pidl, szTemp, ARRAYSIZE(szTemp));
  1673. if (SUCCEEDED(hr))
  1674. hr = StringToStrRet(szTemp, &(pDetails->str));
  1675. }
  1676. }
  1677. }
  1678. return hr;
  1679. }
  1680. STDMETHODIMP CMergedFolder::GetDefaultColumnState(UINT iColumn, DWORD *pbState)
  1681. {
  1682. IShellFolder2 *psf2;
  1683. HRESULT hr = _GetFolder2(NULL, NULL, &psf2);
  1684. if (SUCCEEDED(hr))
  1685. {
  1686. hr = psf2->GetDefaultColumnState(iColumn, pbState);
  1687. psf2->Release();
  1688. }
  1689. return hr;
  1690. }
  1691. STDMETHODIMP CMergedFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
  1692. {
  1693. HRESULT hr;
  1694. if (IsEqualSCID(*pscid, SCID_WHICHFOLDER))
  1695. {
  1696. WCHAR szTemp[MAX_PATH];
  1697. hr = _GetWhichFolderColumn(pidl, szTemp, ARRAYSIZE(szTemp));
  1698. if (SUCCEEDED(hr))
  1699. hr = InitVariantFromStr(pv, szTemp);
  1700. }
  1701. else
  1702. {
  1703. IShellFolder2 *psf2;
  1704. LPITEMIDLIST pidlItem;
  1705. hr = _GetFolder2(pidl, &pidlItem, &psf2);
  1706. if (SUCCEEDED(hr))
  1707. {
  1708. hr = psf2->GetDetailsEx(pidlItem, pscid, pv);
  1709. psf2->Release();
  1710. ILFree(pidlItem);
  1711. }
  1712. }
  1713. return hr;
  1714. }
  1715. STDMETHODIMP CMergedFolder::MapColumnToSCID(UINT iCol, SHCOLUMNID *pscid)
  1716. {
  1717. HRESULT hr = S_OK;
  1718. // one of our columns?
  1719. if (_IsOurColumn(iCol))
  1720. {
  1721. iCol -= _iColumnOffset;
  1722. *pscid = *_columns[iCol].pscid;
  1723. }
  1724. else
  1725. {
  1726. IShellFolder2 *psf2;
  1727. hr = _GetFolder2(NULL, NULL, &psf2);
  1728. if (SUCCEEDED(hr))
  1729. {
  1730. hr = psf2->MapColumnToSCID(iCol, pscid);
  1731. psf2->Release();
  1732. }
  1733. }
  1734. return hr;
  1735. }
  1736. // Forward to default _Namespace for display name
  1737. STDMETHODIMP CMergedFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD grfFlags, STRRET *psr)
  1738. {
  1739. IShellFolder* psf;
  1740. LPITEMIDLIST pidlItem;
  1741. HRESULT hr = _NamespaceForItem(pidl, ASFF_DEFNAMESPACE_DISPLAYNAME, ASFF_DEFNAMESPACE_DISPLAYNAME, &psf, &pidlItem, NULL);
  1742. if (SUCCEEDED(hr))
  1743. {
  1744. ASSERT(ILFindLastID(pidlItem) == pidlItem);
  1745. hr = psf->GetDisplayNameOf(pidlItem, grfFlags, psr);
  1746. if (SUCCEEDED(hr))
  1747. {
  1748. hr = _FixStrRetOffset(pidlItem, psr);
  1749. #ifdef DEBUG
  1750. // If the trace flags are set, and this is not comming from an internal query,
  1751. // Then append the location where this name came from
  1752. if (!((SHGDN_FORPARSING | SHGDN_FOREDITING) & grfFlags) &&
  1753. (g_qwTraceFlags & TF_AUGM))
  1754. {
  1755. LPWSTR pwszOldName;
  1756. hr = StrRetToStrW(psr, pidlItem, &pwszOldName);
  1757. if (SUCCEEDED(hr))
  1758. {
  1759. UINT cch = lstrlenW(pwszOldName) + 50;
  1760. psr->uType = STRRET_WSTR;
  1761. psr->pOleStr = (LPWSTR)SHAlloc(cch * sizeof(WCHAR));
  1762. if (psr->pOleStr)
  1763. {
  1764. UINT cchAdded = wnsprintfW(psr->pOleStr, cch, L"%s ", pwszOldName);
  1765. cch -= cchAdded;
  1766. LPWSTR pwsz = psr->pOleStr + cchAdded;
  1767. ULONG nSrc = _GetSourceCount(pidl);
  1768. UINT uSrc;
  1769. // Cut off after 10 to avoid buffer overflow
  1770. for (uSrc = 0; uSrc < nSrc && uSrc < 10; uSrc++)
  1771. {
  1772. UINT uID;
  1773. if (SUCCEEDED(_GetSubPidl(pidl, uSrc, &uID, NULL, NULL)))
  1774. {
  1775. cchAdded = wnsprintfW(pwsz, cch, L"%c%d", uSrc ? '+' : '(', uID);
  1776. cch -= cchAdded;
  1777. pwsz += cchAdded;
  1778. }
  1779. }
  1780. pwsz += wnsprintfW(pwsz, cch, L")");
  1781. }
  1782. else
  1783. {
  1784. hr = E_OUTOFMEMORY;
  1785. }
  1786. SHFree(pwszOldName);
  1787. }
  1788. }
  1789. #endif
  1790. }
  1791. ILFree(pidlItem);
  1792. }
  1793. else
  1794. {
  1795. if (IsSelf(1, &pidl) &&
  1796. ((grfFlags & (SHGDN_FORADDRESSBAR | SHGDN_INFOLDER | SHGDN_FORPARSING)) == SHGDN_FORPARSING))
  1797. {
  1798. IShellFolder2 *psf2;
  1799. hr = _GetFolder2(NULL, NULL, &psf2);
  1800. if (SUCCEEDED(hr))
  1801. {
  1802. hr = psf2->GetDisplayNameOf(NULL, grfFlags, psr);
  1803. psf2->Release();
  1804. }
  1805. }
  1806. else
  1807. {
  1808. hr = E_INVALIDARG;
  1809. }
  1810. }
  1811. return hr;
  1812. }
  1813. // CFSFolder helper
  1814. HRESULT _NextSegment(LPCWSTR *ppszIn, LPTSTR pszSegment, UINT cchSegment, BOOL bValidate);
  1815. void CMergedFolder::_SetSimple(LPITEMIDLIST *ppidl)
  1816. {
  1817. PAUGM_IDWRAP pWrap = (PAUGM_IDWRAP)*ppidl;
  1818. if ((pWrap->cb >= sizeof(AUGM_IDWRAP)) &&
  1819. (pWrap->ulTag == AUGM_WRAPTAG) &&
  1820. (pWrap->ulVersion == AUGM_WRAPVERSION_2_0))
  1821. {
  1822. pWrap->dwFlags |= AUGMF_ISSIMPLE;
  1823. }
  1824. }
  1825. BOOL CMergedFolder::_IsSimple(LPCITEMIDLIST pidl)
  1826. {
  1827. BOOL fSimple = FALSE;
  1828. PAUGM_IDWRAP pWrap = (PAUGM_IDWRAP)pidl;
  1829. if ((pWrap->cb >= sizeof(AUGM_IDWRAP)) &&
  1830. (pWrap->ulTag == AUGM_WRAPTAG) &&
  1831. (pWrap->ulVersion == AUGM_WRAPVERSION_2_0))
  1832. {
  1833. fSimple = (pWrap->dwFlags & AUGMF_ISSIMPLE);
  1834. }
  1835. return fSimple;
  1836. }
  1837. STDMETHODIMP CMergedFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pwszName,
  1838. ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttrib)
  1839. {
  1840. *ppidl = NULL;
  1841. TCHAR szName[MAX_PATH];
  1842. // ISSUE: this relies on the fact that the path that's getting parsed is FS (with backslashes).
  1843. // alternatively this could be rearchitected so that the namespaces would be delegate folders
  1844. // and we could do the merge when we handle their pidl allocation.
  1845. HRESULT hr = _NextSegment((LPCWSTR *) &pwszName, szName, ARRAYSIZE(szName), TRUE);
  1846. if (SUCCEEDED(hr))
  1847. {
  1848. // let all name spaces try to parse, append them into the pidl as these are found
  1849. CMergedFldrNamespace *pns;
  1850. HRESULT hrParse = S_OK;
  1851. for (int i = 0; SUCCEEDED(hr) && (pns = _Namespace(i)); i++)
  1852. {
  1853. LPITEMIDLIST pidl;
  1854. hrParse = pns->Folder()->ParseDisplayName(hwnd, pbc, szName, NULL, &pidl, NULL);
  1855. if (SUCCEEDED(hrParse))
  1856. {
  1857. // let each name space parse, accumulate results
  1858. // into *ppidl across multiple folders
  1859. hr = _WrapAddIDList(pidl, i, ppidl);
  1860. ILFree(pidl);
  1861. }
  1862. }
  1863. if (!*ppidl)
  1864. {
  1865. if (SUCCEEDED(hr))
  1866. {
  1867. hr = hrParse;
  1868. }
  1869. ASSERT(FAILED(hr));
  1870. }
  1871. else
  1872. {
  1873. if (S_OK == SHIsFileSysBindCtx(pbc, NULL))
  1874. {
  1875. _SetSimple(ppidl);
  1876. }
  1877. ASSERT(ILFindLastID(*ppidl) == *ppidl);
  1878. }
  1879. if (SUCCEEDED(hr) && pwszName)
  1880. {
  1881. IShellFolder *psf;
  1882. hr = BindToObject(*ppidl, pbc, IID_PPV_ARG(IShellFolder, &psf));
  1883. if (SUCCEEDED(hr))
  1884. {
  1885. LPITEMIDLIST pidlNext;
  1886. hr = psf->ParseDisplayName(hwnd, pbc, pwszName, NULL, &pidlNext, pdwAttrib);
  1887. if (SUCCEEDED(hr))
  1888. {
  1889. // shilappend frees pidlNext
  1890. hr = SHILAppend(pidlNext, ppidl);
  1891. }
  1892. psf->Release();
  1893. }
  1894. if (FAILED(hr))
  1895. {
  1896. Pidl_Set(ppidl, NULL);
  1897. }
  1898. }
  1899. if (SUCCEEDED(hr) && pdwAttrib && *pdwAttrib)
  1900. {
  1901. GetAttributesOf(1, (LPCITEMIDLIST *)ppidl, pdwAttrib);
  1902. }
  1903. }
  1904. ASSERT(SUCCEEDED(hr) ? (*ppidl != NULL) : (*ppidl == NULL));
  1905. return hr;
  1906. }
  1907. STDMETHODIMP CMergedFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidlWrap,
  1908. LPCOLESTR pwszName, DWORD uFlags, LPITEMIDLIST *ppidlOut)
  1909. {
  1910. if (ppidlOut)
  1911. *ppidlOut = NULL;
  1912. HRESULT hr = E_FAIL;
  1913. IShellFolder* psf; // not ref counted
  1914. LPITEMIDLIST pidlItem;
  1915. if (!_fInShellView)
  1916. {
  1917. hr = _NamespaceForItem(pidlWrap, ASFF_COMMON, 0, &psf, &pidlItem, NULL, TRUE);
  1918. if (FAILED(hr))
  1919. {
  1920. hr = _NamespaceForItem(pidlWrap, ASFF_COMMON, ASFF_COMMON, &psf, &pidlItem, NULL, TRUE);
  1921. if (SUCCEEDED(hr))
  1922. {
  1923. hr = AffectAllUsers(hwnd) ? S_OK : E_FAIL;
  1924. if (FAILED(hr))
  1925. {
  1926. ILFree(pidlItem);
  1927. }
  1928. }
  1929. }
  1930. }
  1931. else
  1932. {
  1933. hr = _NamespaceForItem(pidlWrap, ASFF_DEFNAMESPACE_DISPLAYNAME, ASFF_DEFNAMESPACE_DISPLAYNAME, &psf, &pidlItem, NULL);
  1934. }
  1935. if (SUCCEEDED(hr))
  1936. {
  1937. ASSERT(ILFindLastID(pidlItem) == pidlItem);
  1938. hr = psf->SetNameOf(hwnd, pidlItem, pwszName, uFlags, NULL);
  1939. ILFree(pidlItem);
  1940. }
  1941. if (SUCCEEDED(hr) && ppidlOut)
  1942. {
  1943. WCHAR szName[MAX_PATH];
  1944. hr = DisplayNameOf(SAFECAST(this, IAugmentedShellFolder2*), pidlWrap, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  1945. if (SUCCEEDED(hr))
  1946. hr = ParseDisplayName(NULL, NULL, szName, NULL, ppidlOut, NULL);
  1947. }
  1948. return hr;
  1949. }
  1950. // IAugmentedShellFolder::AddNameSpace
  1951. // Adds a source _Namespace to the Merge shell folder object
  1952. STDMETHODIMP CMergedFolder::AddNameSpace(const GUID *pguidObject, IShellFolder *psf, LPCITEMIDLIST pidl, DWORD dwFlags)
  1953. {
  1954. // Check for duplicate via full display name
  1955. CMergedFldrNamespace *pns;
  1956. for (int i = 0; pns = _Namespace(i); i++)
  1957. {
  1958. if (pidl && ILIsEqual(pns->GetIDList(), pidl))
  1959. {
  1960. // If Found, then reassign attributes and return
  1961. return pns->SetNamespace(pguidObject, psf, pidl, dwFlags);
  1962. }
  1963. }
  1964. HRESULT hr;
  1965. pns = new CMergedFldrNamespace();
  1966. if (pns)
  1967. {
  1968. hr = pns->SetNamespace(pguidObject, psf, pidl, dwFlags);
  1969. if (SUCCEEDED(hr))
  1970. {
  1971. hr = _SimpleAddNamespace(pns);
  1972. if (SUCCEEDED(hr))
  1973. {
  1974. pns = NULL; // success, don't free below
  1975. }
  1976. }
  1977. if (pns)
  1978. delete pns;
  1979. }
  1980. else
  1981. hr = E_OUTOFMEMORY;
  1982. return hr;
  1983. }
  1984. HRESULT CMergedFolder::_SimpleAddNamespace(CMergedFldrNamespace *pns)
  1985. {
  1986. if (NULL == _hdpaNamespaces)
  1987. _hdpaNamespaces = DPA_Create(2);
  1988. HRESULT hr = E_OUTOFMEMORY;
  1989. if (_hdpaNamespaces && (DPA_AppendPtr(_hdpaNamespaces, pns) != -1))
  1990. {
  1991. // If there is any conditional merging going on, then remember it
  1992. if (pns->FolderAttrib() & ASFF_MERGESAMEGUID)
  1993. {
  1994. _fPartialMerge = TRUE;
  1995. }
  1996. hr = S_OK;
  1997. }
  1998. return hr;
  1999. }
  2000. STDMETHODIMP CMergedFolder::GetNameSpaceID(LPCITEMIDLIST pidl, GUID * pguidOut)
  2001. {
  2002. HRESULT hr = E_INVALIDARG;
  2003. ASSERT(IS_VALID_PIDL(pidl));
  2004. ASSERT(IS_VALID_WRITE_PTR(pguidOut, GUID));
  2005. if (pidl && pguidOut)
  2006. {
  2007. CMergedFldrNamespace *pns;
  2008. hr = _GetSubPidl(pidl, 0, NULL, NULL, &pns);
  2009. if (SUCCEEDED(hr))
  2010. {
  2011. *pguidOut = pns->GetGUID();
  2012. }
  2013. }
  2014. return hr;
  2015. }
  2016. // Retrieves data for the _Namespace identified by dwID.
  2017. STDMETHODIMP CMergedFolder::QueryNameSpace(ULONG iIndex, GUID *pguidOut, IShellFolder **ppsf)
  2018. {
  2019. CMergedFldrNamespace *pns;
  2020. HRESULT hr = _Namespace(iIndex, &pns);
  2021. if (SUCCEEDED(hr))
  2022. {
  2023. if (pguidOut)
  2024. *pguidOut = pns->GetGUID();
  2025. if (ppsf)
  2026. {
  2027. *ppsf = pns->Folder();
  2028. if (*ppsf)
  2029. (*ppsf)->AddRef();
  2030. }
  2031. }
  2032. return hr;
  2033. }
  2034. #define ASFQNSI_SUPPORTED (ASFQNSI_FLAGS | ASFQNSI_FOLDER | ASFQNSI_GUID | ASFQNSI_PIDL)
  2035. STDMETHODIMP CMergedFolder::QueryNameSpace2(ULONG iIndex, QUERYNAMESPACEINFO *pqnsi)
  2036. {
  2037. if (pqnsi->cbSize != sizeof(QUERYNAMESPACEINFO) ||
  2038. (pqnsi->dwMask & ~ASFQNSI_SUPPORTED))
  2039. {
  2040. return E_INVALIDARG;
  2041. }
  2042. CMergedFldrNamespace *pns;
  2043. HRESULT hr = _Namespace(iIndex, &pns);
  2044. if (SUCCEEDED(hr))
  2045. {
  2046. // Do PIDL first since it's the only one that can fail
  2047. // so we don't have to do cleanup
  2048. if (pqnsi->dwMask & ASFQNSI_PIDL)
  2049. {
  2050. hr = SHILClone(pns->GetIDList(), &pqnsi->pidl);
  2051. if (FAILED(hr))
  2052. return hr;
  2053. }
  2054. if (pqnsi->dwMask & ASFQNSI_FLAGS)
  2055. pqnsi->dwFlags = pns->FolderAttrib();
  2056. if (pqnsi->dwMask & ASFQNSI_FOLDER)
  2057. {
  2058. pqnsi->psf = pns->Folder();
  2059. if (pqnsi->psf)
  2060. pqnsi->psf->AddRef();
  2061. }
  2062. if (pqnsi->dwMask & ASFQNSI_GUID)
  2063. pqnsi->guidObject = pns->GetGUID();
  2064. }
  2065. return hr;
  2066. }
  2067. STDMETHODIMP CMergedFolder::EnumNameSpace(DWORD uNameSpace, DWORD *pdwID)
  2068. {
  2069. if (uNameSpace == (DWORD)-1)
  2070. {
  2071. return ResultFromShort(_NamespaceCount());
  2072. }
  2073. if (uNameSpace < (UINT)_NamespaceCount())
  2074. {
  2075. // Our namespace IDs are just ordinals
  2076. *pdwID = uNameSpace;
  2077. return S_OK;
  2078. }
  2079. return HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
  2080. }
  2081. // IAugmentedShellFolder2 methods
  2082. STDMETHODIMP CMergedFolder::UnWrapIDList(LPCITEMIDLIST pidlWrap, LONG cPidls,
  2083. IShellFolder** apsf, LPITEMIDLIST* apidlFolder,
  2084. LPITEMIDLIST* apidlItems, LONG* pcFetched)
  2085. {
  2086. if (cPidls <= 0)
  2087. return E_INVALIDARG;
  2088. HRESULT hr = S_OK;
  2089. // Enumerate pidls in wrap
  2090. LPITEMIDLIST pidlItem;
  2091. CMergedFldrNamespace *pns;
  2092. LONG cFetched;
  2093. for (cFetched = 0; SUCCEEDED(hr) && (cFetched < cPidls) && SUCCEEDED(_GetSubPidl(pidlWrap, cFetched, NULL, &pidlItem, &pns)); cFetched++)
  2094. {
  2095. if (apsf)
  2096. {
  2097. apsf[cFetched] = pns->Folder();
  2098. if (apsf[cFetched])
  2099. apsf[cFetched]->AddRef();
  2100. }
  2101. if (apidlFolder)
  2102. {
  2103. hr = SHILClone(pns->GetIDList(), &apidlFolder[cFetched]);
  2104. }
  2105. if (apidlItems)
  2106. {
  2107. apidlItems[cFetched] = NULL;
  2108. if (SUCCEEDED(hr))
  2109. {
  2110. hr = SHILClone(pidlItem, &apidlItems[cFetched]);
  2111. }
  2112. }
  2113. ILFree(pidlItem);
  2114. }
  2115. if (SUCCEEDED(hr))
  2116. {
  2117. if (pcFetched)
  2118. {
  2119. *pcFetched = cFetched;
  2120. }
  2121. hr = (cFetched == cPidls) ? S_OK : S_FALSE;
  2122. }
  2123. else
  2124. {
  2125. // clean up items we've already allocated; since the caller won't free if we
  2126. // return failure
  2127. for (LONG i = 0; i < cFetched; i++)
  2128. {
  2129. if (apsf)
  2130. ATOMICRELEASE(apsf[i]);
  2131. if (apidlFolder)
  2132. ILFree(apidlFolder[i]);
  2133. if (apidlItems)
  2134. ILFree(apidlItems[i]);
  2135. }
  2136. }
  2137. return hr;
  2138. }
  2139. STDMETHODIMP CMergedFolder::SetOwner(IUnknown* punkOwner)
  2140. {
  2141. DPA_EnumCallback(_hdpaNamespaces, _SetOwnerProc, punkOwner);
  2142. return S_OK;
  2143. }
  2144. int CMergedFolder::_SetOwnerProc(void *pv, void *pvParam)
  2145. {
  2146. CMergedFldrNamespace *pns = (CMergedFldrNamespace*) pv;
  2147. return pns->SetOwner((IUnknown*)pvParam);
  2148. }
  2149. // ITranslateShellChangeNotify methods
  2150. // old translate style only
  2151. LPITEMIDLIST CMergedFolder::_ILCombineBase(LPCITEMIDLIST pidlContainingBase, LPCITEMIDLIST pidlRel)
  2152. {
  2153. // This routine differs from ILCombine in that it takes the First pidl's base, and
  2154. // cats on the last id of the second pidl. We need this so Wrapped pidls
  2155. // end up with the same base, and we get a valid full pidl.
  2156. LPITEMIDLIST pidlRet = NULL;
  2157. LPITEMIDLIST pidlBase = ILClone(pidlContainingBase);
  2158. if (pidlBase)
  2159. {
  2160. ILRemoveLastID(pidlBase);
  2161. pidlRet = ILCombine(pidlBase, pidlRel);
  2162. ILFree(pidlBase);
  2163. }
  2164. return pidlRet;
  2165. }
  2166. BOOL CMergedFolder::_IsFolderEvent(LONG lEvent)
  2167. {
  2168. return lEvent == SHCNE_MKDIR || lEvent == SHCNE_RMDIR || lEvent == SHCNE_RENAMEFOLDER;
  2169. }
  2170. BOOL GetRealPidlFromSimple(LPCITEMIDLIST pidlSimple, LPITEMIDLIST* ppidlReal)
  2171. {
  2172. // Similar to SHGetRealIDL in Function, but SHGetRealIDL does SHGDN_FORPARSING | INFOLDER.
  2173. // I need the parsing name. I can't rev SHGetRealIDL very easily, so here's this one!
  2174. TCHAR szFullName[MAX_PATH];
  2175. if (SUCCEEDED(SHGetNameAndFlags(pidlSimple, SHGDN_FORPARSING, szFullName, SIZECHARS(szFullName), NULL)))
  2176. {
  2177. *ppidlReal = ILCreateFromPath(szFullName);
  2178. }
  2179. if (*ppidlReal == NULL) // Unable to create? Then use the simple pidl. This is because it does not exist any more
  2180. { // For say, a Delete Notify
  2181. *ppidlReal = ILClone(pidlSimple);
  2182. }
  2183. return *ppidlReal != NULL;
  2184. }
  2185. // the new way we have of translating ids is better than the old way since it handles
  2186. // multi-level translation.
  2187. // the problem is that the old way is used extensively by the start menu, so the
  2188. // start menu will need to be rewritten to use the new way. when that happens,
  2189. // then the old method can be ripped out.
  2190. STDMETHODIMP CMergedFolder::TranslateIDs(
  2191. LONG *plEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2,
  2192. LPITEMIDLIST * ppidlOut1, LPITEMIDLIST * ppidlOut2,
  2193. LONG *plEvent2, LPITEMIDLIST *ppidlOut1Event2,
  2194. LPITEMIDLIST *ppidlOut2Event2)
  2195. {
  2196. if (_fInShellView)
  2197. {
  2198. return _NewTranslateIDs(plEvent, pidl1, pidl2, ppidlOut1, ppidlOut2, plEvent2, ppidlOut1Event2, ppidlOut2Event2);
  2199. }
  2200. else
  2201. {
  2202. return _OldTranslateIDs(plEvent, pidl1, pidl2, ppidlOut1, ppidlOut2, plEvent2, ppidlOut1Event2, ppidlOut2Event2);
  2203. }
  2204. }
  2205. // old version.
  2206. HRESULT CMergedFolder::_OldTranslateIDs(
  2207. LONG *plEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2,
  2208. LPITEMIDLIST * ppidlOut1, LPITEMIDLIST * ppidlOut2,
  2209. LONG *plEvent2, LPITEMIDLIST *ppidlOut1Event2,
  2210. LPITEMIDLIST *ppidlOut2Event2)
  2211. {
  2212. HRESULT hr = E_FAIL;
  2213. if (!plEvent)
  2214. return E_FAIL;
  2215. switch (*plEvent)
  2216. {
  2217. case SHCNE_EXTENDED_EVENT:
  2218. case SHCNE_ASSOCCHANGED:
  2219. case SHCNE_UPDATEIMAGE:
  2220. return S_OK;
  2221. case SHCNE_UPDATEDIR:
  2222. _FreeObjects();
  2223. return S_OK;
  2224. }
  2225. ASSERT(ppidlOut1);
  2226. ASSERT(ppidlOut2);
  2227. LONG lEvent = *plEvent;
  2228. *plEvent2 = (LONG)-1;
  2229. *ppidlOut1Event2 = NULL;
  2230. *ppidlOut2Event2 = NULL;
  2231. *ppidlOut1 = (LPITEMIDLIST)pidl1;
  2232. *ppidlOut2 = (LPITEMIDLIST)pidl2;
  2233. // If they are already wrapped, don't wrap twice.
  2234. if (SUCCEEDED(_IsWrap(ILFindLastID(pidl1))) ||
  2235. SUCCEEDED(_IsWrap(ILFindLastID(pidl2))))
  2236. {
  2237. // We don't want to wrap twice.
  2238. return E_FAIL;
  2239. }
  2240. if (!_hdpaNamespaces)
  2241. return E_FAIL;
  2242. if (!_hdpaObjects)
  2243. return E_FAIL;
  2244. CMergedFldrItem* pmfi;
  2245. int iIndex;
  2246. int iShellFolder1 = -1;
  2247. int iShellFolder2 = -1;
  2248. IShellFolder* psf1 = NULL;
  2249. IShellFolder* psf2 = NULL;
  2250. LPITEMIDLIST pidlReal1 = NULL;
  2251. LPITEMIDLIST pidlReal2 = NULL;
  2252. LPITEMIDLIST pidlRealRel1 = NULL;
  2253. LPITEMIDLIST pidlRealRel2 = NULL;
  2254. CMergedFldrNamespace * pns1 = NULL;
  2255. CMergedFldrNamespace * pns2 = NULL;
  2256. BOOL fFolder = _IsFolderEvent(*plEvent);
  2257. // Get the information about these Simple pidls: Are they our Children? If so, what _Namespace?
  2258. BOOL fChild1 = _IsChildIDInternal(pidl1, TRUE, &iShellFolder1);
  2259. BOOL fChild2 = _IsChildIDInternal(pidl2, TRUE, &iShellFolder2);
  2260. // Is either a child?
  2261. if (!(fChild1 || fChild2))
  2262. return hr;
  2263. // Ok, pidl1 is a child, can we get the Real pidl from the simple one?
  2264. if (pidl1 && !GetRealPidlFromSimple(pidl1, &pidlReal1))
  2265. goto Cleanup;
  2266. // Ok, pidl2 is a child, can we get the Real pidl from the simple one?
  2267. if (pidl2 && !GetRealPidlFromSimple(pidl2, &pidlReal2))
  2268. goto Cleanup;
  2269. // These are for code clarity later on. We deal with Relative pidls from here until the very end,
  2270. // when we combine the base of the in pidls with the outgoing wrapped pidls.
  2271. if (pidlReal1)
  2272. pidlRealRel1 = ILFindLastID(pidlReal1);
  2273. if (pidlReal2)
  2274. pidlRealRel2 = ILFindLastID(pidlReal2);
  2275. // Is Pidl1 in our _Namespaces?
  2276. if (iShellFolder1 != -1)
  2277. {
  2278. // Yes, lets get the non-refcounted shell folder that know's about this pidl.
  2279. pns1 = _Namespace(iShellFolder1);
  2280. psf1 = pns1->Folder(); // Non ref counted.
  2281. }
  2282. // Is Pidl2 in our _Namespaces?
  2283. if (iShellFolder2 != -1)
  2284. {
  2285. // Yes, lets get the non-refcounted shell folder that know's about this pidl.
  2286. pns2 = _Namespace(iShellFolder2);
  2287. psf2 = pns2->Folder(); // Non ref counted.
  2288. }
  2289. hr = S_OK;
  2290. switch(*plEvent)
  2291. {
  2292. case SHCNE_UPDATEITEM:
  2293. case 0: // Just look up the pidls and return.
  2294. {
  2295. DWORD rgfAttrib = SFGAO_FOLDER;
  2296. if (iShellFolder1 != -1)
  2297. {
  2298. psf1->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlRealRel1, &rgfAttrib);
  2299. if (S_OK == _SearchForPidl(iShellFolder1, pns1, pidlRealRel1, BOOLIFY(rgfAttrib & SFGAO_FOLDER), &iIndex, &pmfi))
  2300. {
  2301. *ppidlOut1 = _ILCombineBase(pidlReal1, pmfi->GetIDList());
  2302. if (!*ppidlOut1)
  2303. hr = E_OUTOFMEMORY;
  2304. }
  2305. }
  2306. rgfAttrib = SFGAO_FOLDER;
  2307. if (iShellFolder2 != -1 && SUCCEEDED(hr))
  2308. {
  2309. psf2->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlRealRel2, &rgfAttrib);
  2310. if (S_OK == _SearchForPidl(iShellFolder2, pns2, pidlRealRel2, BOOLIFY(rgfAttrib & SFGAO_FOLDER), &iIndex, &pmfi))
  2311. {
  2312. *ppidlOut2 = _ILCombineBase(pidlReal2, pmfi->GetIDList());
  2313. if (!*ppidlOut2)
  2314. hr = E_OUTOFMEMORY;
  2315. }
  2316. }
  2317. }
  2318. break;
  2319. case SHCNE_CREATE:
  2320. case SHCNE_MKDIR:
  2321. {
  2322. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s", fFolder? TEXT("SHCNE_MKDIR") : TEXT("SHCNE_CREATE"));
  2323. // Is there a thing of this name already?
  2324. if (S_OK == _SearchForPidl(iShellFolder1, pns1, pidlRealRel1, fFolder, &iIndex, &pmfi))
  2325. {
  2326. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s needs to be merged. Converting to Rename", pmfi->GetDisplayName());
  2327. // Yes; Then we need to merge this new pidl into the wrapped pidl, and change this
  2328. // to a rename, passing the Old wrapped pidl as the first arg, and the new wrapped pidl
  2329. // as the second arg. I have to be careful about the freeing:
  2330. // Free *ppidlOut1
  2331. // Clone pmfi->pidlWrap -> *ppidlOut1.
  2332. // Add pidl1 to pmfi->_pidlWrap.
  2333. // Clone new pmfi->_pidlWrap -> *ppidlOut2. ASSERT(*ppidlOut2 == NULL)
  2334. *ppidlOut1 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2335. if (*ppidlOut1)
  2336. {
  2337. _WrapAddIDList(pidlRealRel1, iShellFolder1, &pmfi->_pidlWrap);
  2338. *ppidlOut2 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2339. if (!*ppidlOut2)
  2340. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to create new pidl2");
  2341. *plEvent = fFolder? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
  2342. }
  2343. else
  2344. {
  2345. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to create new pidl1");
  2346. }
  2347. }
  2348. else
  2349. {
  2350. CMergedFldrItem* pmfiEnum = new CMergedFldrItem;
  2351. if (pmfiEnum)
  2352. {
  2353. LPITEMIDLIST pidlWrap;
  2354. if (SUCCEEDED(_CreateWrap(pidlRealRel1, (UINT)iShellFolder1, &pidlWrap)) &&
  2355. pmfiEnum->Init(SAFECAST(this, IAugmentedShellFolder2*), pidlWrap, iShellFolder1))
  2356. {
  2357. SEARCH_FOR_PIDL sfp;
  2358. sfp.pszDisplayName = pmfiEnum->GetDisplayName();
  2359. sfp.fFolder = fFolder;
  2360. sfp.self = this;
  2361. sfp.iNamespace = -1;
  2362. int iInsertIndex = DPA_Search(_hdpaObjects, &sfp, 0,
  2363. _SearchByName, NULL, DPAS_SORTED | DPAS_INSERTAFTER);
  2364. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: Creating new unmerged %s at %d", pmfiEnum->GetDisplayName(), iInsertIndex);
  2365. if (iInsertIndex < 0)
  2366. iInsertIndex = DA_LAST;
  2367. if (DPA_InsertPtr(_hdpaObjects, iInsertIndex, pmfiEnum) == -1)
  2368. {
  2369. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Was unable to add %s for some reason. Bailing",
  2370. pmfiEnum->GetDisplayName());
  2371. delete pmfiEnum;
  2372. }
  2373. else
  2374. {
  2375. *ppidlOut1 = _ILCombineBase(pidl1, pmfiEnum->GetIDList());
  2376. }
  2377. }
  2378. else
  2379. delete pmfiEnum;
  2380. }
  2381. }
  2382. }
  2383. break;
  2384. case SHCNE_DELETE:
  2385. case SHCNE_RMDIR:
  2386. {
  2387. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s", fFolder?
  2388. TEXT("SHCNE_RMDIR") : TEXT("SHCNE_DELETE"));
  2389. int iDeleteIndex;
  2390. // Is there a folder of this name already?
  2391. if (S_OK == _SearchForPidl(iShellFolder1, pns1, pidlRealRel1,
  2392. fFolder, &iDeleteIndex, &pmfi))
  2393. {
  2394. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: Found %s checking merge state.", pmfi->GetDisplayName());
  2395. // Yes; Then we need to unmerge this pidl from the wrapped pidl, and change this
  2396. // to a rename, passing the Old wrapped pidl as the first arg, and the new wrapped pidl
  2397. // as the second arg. I have to be careful about the freeing:
  2398. // Free *ppidlOut1
  2399. // Clone pmfi->GetIDList() -> *ppidlOut1.
  2400. // Remove pidl1 from pmfi->_GetIDList()
  2401. // Convert to rename, pass new wrapped as second arg.
  2402. if (_GetSourceCount(pmfi->GetIDList()) > 1)
  2403. {
  2404. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s is Merged. Removing pidl, convert to rename", pmfi->GetDisplayName());
  2405. *ppidlOut1 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2406. if (*ppidlOut1)
  2407. {
  2408. LPITEMIDLIST pidlFree = pmfi->GetIDList();
  2409. if (SUCCEEDED(_WrapRemoveIDList(pidlFree, iShellFolder1, &pmfi->_pidlWrap)))
  2410. {
  2411. ILFree(pidlFree);
  2412. }
  2413. *ppidlOut2 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2414. if (!*ppidlOut2)
  2415. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to create new pidl2");
  2416. *plEvent = fFolder? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
  2417. }
  2418. else
  2419. {
  2420. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to create new pidl1");
  2421. }
  2422. }
  2423. else
  2424. {
  2425. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s is not Merged. deleteing", pmfi->GetDisplayName());
  2426. pmfi = (CMergedFldrItem*)DPA_DeletePtr(_hdpaObjects, iDeleteIndex);
  2427. if (EVAL(pmfi))
  2428. {
  2429. *ppidlOut1 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2430. delete pmfi;
  2431. }
  2432. else
  2433. {
  2434. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to get %d from DPA", iDeleteIndex);
  2435. }
  2436. }
  2437. }
  2438. }
  2439. break;
  2440. case SHCNE_RENAMEITEM:
  2441. case SHCNE_RENAMEFOLDER:
  2442. {
  2443. // REARCHITECT: (lamadio): When renaming an item in the menu, this code will split it into
  2444. // a Delete and a Create. We need to detect this situation and convert it to 1 rename. This
  2445. // will solve the problem of the lost order during a rename....
  2446. BOOL fEvent1Set = FALSE;
  2447. BOOL fFirstPidlInNamespace = FALSE;
  2448. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s", fFolder?
  2449. TEXT("SHCNE_RENAMEFOLDER") : TEXT("SHCNE_RENAMEITEM"));
  2450. // Is this item being renamed FROM the Folder?
  2451. if (iShellFolder1 != -1)
  2452. {
  2453. // Is this pidl a child of the Folder?
  2454. if (S_OK == _SearchForPidl(iShellFolder1, pns1, pidlRealRel1,
  2455. fFolder, &iIndex, &pmfi)) // Is it found?
  2456. {
  2457. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: Old pidl %s is in the Folder", pmfi->GetDisplayName());
  2458. // Yes.
  2459. // Then we need to see if the item that it's being renamed from was Merged
  2460. // Need this for reentrancy
  2461. if (_ContainsSrcID(pmfi->GetIDList(), iShellFolder1))
  2462. {
  2463. // Was it merged?
  2464. if (_GetSourceCount(pmfi->GetIDList()) > 1) // Case 3)
  2465. {
  2466. // Yes; Then we need to unmerge that item.
  2467. *ppidlOut1 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2468. if (*ppidlOut1)
  2469. {
  2470. LPITEMIDLIST pidlFree = pmfi->GetIDList();
  2471. if (SUCCEEDED(_WrapRemoveIDList(pidlFree, iShellFolder1, &pmfi->_pidlWrap)))
  2472. {
  2473. ILFree(pidlFree);
  2474. }
  2475. *ppidlOut2 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2476. if (!*ppidlOut2)
  2477. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to create new pidl2");
  2478. // This We need to "Rename" the old wrapped pidl, to this new one
  2479. // that does not contain the old item.
  2480. fEvent1Set = TRUE;
  2481. }
  2482. else
  2483. {
  2484. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to create new pidl1");
  2485. }
  2486. }
  2487. else
  2488. {
  2489. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s is not merged. Nuking item Convert to Delete for event 1.",
  2490. pmfi->GetDisplayName());
  2491. // No, This was not a wrapped pidl. Then, convert to a delete:
  2492. pmfi = (CMergedFldrItem*)DPA_DeletePtr(_hdpaObjects, iIndex);
  2493. if (EVAL(pmfi))
  2494. {
  2495. // If we're renaming from this folder, into this folder, Then the first event stays a rename.
  2496. if (iShellFolder2 == -1)
  2497. {
  2498. fEvent1Set = TRUE;
  2499. *plEvent = fFolder? SHCNE_RMDIR : SHCNE_DELETE;
  2500. }
  2501. else
  2502. {
  2503. fFirstPidlInNamespace = TRUE;
  2504. }
  2505. *ppidlOut1 = _ILCombineBase(pidl1, pmfi->GetIDList());
  2506. delete pmfi;
  2507. }
  2508. else
  2509. {
  2510. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Failure. Was unable to find Item at %d", iIndex);
  2511. }
  2512. }
  2513. }
  2514. else
  2515. {
  2516. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: Skipping this because we already processed it."
  2517. "Dragging To Desktop?");
  2518. hr = E_FAIL;
  2519. }
  2520. }
  2521. else
  2522. {
  2523. // we were told to rename something that is supposed to exist in the first
  2524. // namespace, but we couldn't find it.
  2525. // we dont want to have the caller fire off some more events because of this,
  2526. // so we fail.
  2527. hr = E_FAIL;
  2528. }
  2529. }
  2530. // Is this item is being rename INTO the Start Menu?
  2531. if (iShellFolder2 != -1)
  2532. {
  2533. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: New pidl is in the Folder");
  2534. LPITEMIDLIST* ppidlNewWrapped1 = ppidlOut1;
  2535. LPITEMIDLIST* ppidlNewWrapped2 = ppidlOut2;
  2536. LONG* plNewEvent = plEvent;
  2537. if (fEvent1Set)
  2538. {
  2539. plNewEvent = plEvent2;
  2540. ppidlNewWrapped1 = ppidlOut1Event2;
  2541. ppidlNewWrapped2 = ppidlOut2Event2;
  2542. }
  2543. if (S_OK == _SearchForPidl(iShellFolder2, pns2, pidlRealRel2,
  2544. fFolder, &iIndex, &pmfi))
  2545. {
  2546. // If we're renaming from this folder, into this folder, Check to see if the destination has a
  2547. // conflict. If there is a confict (This case), then convert first event to a remove,
  2548. // and the second event to the rename.
  2549. if (fFirstPidlInNamespace)
  2550. {
  2551. fEvent1Set = TRUE;
  2552. *plEvent = fFolder? SHCNE_RMDIR : SHCNE_DELETE;
  2553. plNewEvent = plEvent2;
  2554. ppidlNewWrapped1 = ppidlOut1Event2;
  2555. ppidlNewWrapped2 = ppidlOut2Event2;
  2556. }
  2557. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s is in Folder", pmfi->GetDisplayName());
  2558. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: Adding pidl to %s. Convert to Rename for event %s",
  2559. pmfi->GetDisplayName(), fEvent1Set? TEXT("2") : TEXT("1"));
  2560. // Then the destination needs to be merged.
  2561. *ppidlNewWrapped1 = _ILCombineBase(pidl2, pmfi->GetIDList());
  2562. if (*ppidlNewWrapped1)
  2563. {
  2564. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: Successfully created out pidl1");
  2565. _WrapAddIDList(pidlRealRel2, iShellFolder2, &pmfi->_pidlWrap);
  2566. *ppidlNewWrapped2 = _ILCombineBase(pidl2, pmfi->GetIDList());
  2567. *plNewEvent = fFolder? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
  2568. }
  2569. }
  2570. else
  2571. {
  2572. CMergedFldrItem* pmfiEnum = new CMergedFldrItem;
  2573. if (pmfiEnum)
  2574. {
  2575. LPITEMIDLIST pidlWrap;
  2576. if (SUCCEEDED(_CreateWrap(pidlRealRel2, (UINT)iShellFolder2, &pidlWrap)) &&
  2577. pmfiEnum->Init(SAFECAST(this, IAugmentedShellFolder2*), pidlWrap, iShellFolder2))
  2578. {
  2579. SEARCH_FOR_PIDL sfp;
  2580. sfp.pszDisplayName = pmfiEnum->GetDisplayName();
  2581. sfp.fFolder = BOOLIFY(pmfiEnum->GetFolderAttrib() & SFGAO_FOLDER);
  2582. sfp.self = this;
  2583. sfp.iNamespace = -1;
  2584. int iInsertIndex = DPA_Search(_hdpaObjects, &sfp, 0,
  2585. _SearchByName, NULL,
  2586. DPAS_SORTED | DPAS_INSERTAFTER);
  2587. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: %s is a new item. Converting to Create", pmfiEnum->GetDisplayName());
  2588. if (iInsertIndex < 0)
  2589. iInsertIndex = DA_LAST;
  2590. if (DPA_InsertPtr(_hdpaObjects, iInsertIndex, pmfiEnum) == -1)
  2591. {
  2592. TraceMsg(TF_ERROR, "CMergedFolder::TranslateIDs: Was unable to add %s for some reason. Bailing",
  2593. pmfiEnum->GetDisplayName());
  2594. delete pmfiEnum;
  2595. }
  2596. else
  2597. {
  2598. TraceMsg(TF_AUGM, "CMergedFolder::TranslateIDs: Creating new item %s at %d for event %s",
  2599. pmfiEnum->GetDisplayName(), iInsertIndex, fEvent1Set? TEXT("2") : TEXT("1"));
  2600. // If we're renaming from this folder, into this folder, Then the first event stays
  2601. // a rename.
  2602. if (!fFirstPidlInNamespace)
  2603. {
  2604. *plNewEvent = fFolder ? SHCNE_MKDIR : SHCNE_CREATE;
  2605. *ppidlNewWrapped1 = _ILCombineBase(pidl2, pidlWrap);
  2606. *ppidlNewWrapped2 = NULL;
  2607. }
  2608. else
  2609. *ppidlOut2 = _ILCombineBase(pidl2, pidlWrap);
  2610. }
  2611. }
  2612. else
  2613. delete pmfiEnum;
  2614. }
  2615. }
  2616. }
  2617. }
  2618. break;
  2619. default:
  2620. break;
  2621. }
  2622. Cleanup:
  2623. ILFree(pidlReal1);
  2624. ILFree(pidlReal2);
  2625. return hr;
  2626. }
  2627. // parsedisplayname with some extras.
  2628. // this is used to process change notifies. the change notify can be fired AFTER the
  2629. // item has been moved/deleted/whatever, but we still have to be able to correctly process that
  2630. // pidl. so here pidlAbsNamespace identifies the absolute pidl of the item being changenotified.
  2631. // if we're parsing a name within that namespace, force the parsedisplayname with STGM_CREATE to
  2632. // make sure we get the pidl (because it may have been moved or deleted by now) if fForce==TRUE.
  2633. HRESULT CMergedFolder::_ForceParseDisplayName(LPCITEMIDLIST pidlAbsNamespace, LPTSTR pszDisplayName, BOOL fForce, BOOL *pfOthersInWrap, LPITEMIDLIST *ppidl)
  2634. {
  2635. *ppidl = NULL;
  2636. *pfOthersInWrap = FALSE;
  2637. HRESULT hr = S_OK;
  2638. // set up a bindctx: will have STGM_CREATE if fForce is passed, NULL otherwise
  2639. IBindCtx *pbc;
  2640. if (fForce)
  2641. {
  2642. hr = BindCtx_CreateWithMode(STGM_CREATE, &pbc);
  2643. }
  2644. else
  2645. {
  2646. pbc = NULL;
  2647. }
  2648. if (SUCCEEDED(hr))
  2649. {
  2650. CMergedFldrNamespace *pnsWrap = NULL;
  2651. CMergedFldrNamespace *pnsLoop;
  2652. for (int i = 0; SUCCEEDED(hr) && (pnsLoop = _Namespace(i)); i++)
  2653. {
  2654. HRESULT hrLoop = E_FAIL; // not propagated since ParseDisplayName can fail and that's okay
  2655. LPITEMIDLIST pidlNamespace;
  2656. // so if the namespace pnsLoop is a parent to the pidl, we know it
  2657. // came from there so use the bindctx to force creation if necessary
  2658. if (ILIsParent(pnsLoop->GetIDList(), pidlAbsNamespace, FALSE))
  2659. {
  2660. hrLoop = pnsLoop->Folder()->ParseDisplayName(NULL, pbc, pszDisplayName, NULL, &pidlNamespace, NULL);
  2661. }
  2662. else if (_ShouldMergeNamespaces(pnsWrap, pnsLoop))
  2663. {
  2664. pnsWrap = pnsLoop;
  2665. // only if we're merging, tack on other namespace's pidls.
  2666. hrLoop = pnsLoop->Folder()->ParseDisplayName(NULL, NULL, pszDisplayName, NULL, &pidlNamespace, NULL);
  2667. }
  2668. if (SUCCEEDED(hrLoop))
  2669. {
  2670. if (*ppidl)
  2671. {
  2672. *pfOthersInWrap = TRUE;
  2673. }
  2674. hr = _WrapAddIDList(pidlNamespace, i, ppidl);
  2675. ILFree(pidlNamespace);
  2676. }
  2677. }
  2678. // it could be NULL
  2679. if (pbc)
  2680. {
  2681. pbc->Release();
  2682. }
  2683. }
  2684. return hr;
  2685. }
  2686. // this takes an absolute pidl in the given namespace and converts it to a merged pidl.
  2687. // if the item does not actually exist in the namespace, it forces creation if fForce==TRUE (so if
  2688. // a changenotify comes in on that namespace it will process it normally, even if the
  2689. // underlying item has been moved or deleted).
  2690. HRESULT CMergedFolder::_AbsPidlToAbsWrap(CMergedFldrNamespace *pns, LPCITEMIDLIST pidl, BOOL fForce, BOOL *pfOthersInWrap, LPITEMIDLIST *ppidl)
  2691. {
  2692. LPCITEMIDLIST pidlRel = ILFindChild(pns->GetIDList(), pidl);
  2693. HRESULT hr = SHILClone(_pidl, ppidl);
  2694. if (SUCCEEDED(hr))
  2695. {
  2696. IBindCtx *pbc;
  2697. hr = SHCreateSkipBindCtx(NULL, &pbc); // skip all other binding
  2698. if (SUCCEEDED(hr))
  2699. {
  2700. CMergedFolder *pmfMerged = this;
  2701. pmfMerged->AddRef();
  2702. IShellFolder *psfNamespace = pns->Folder();
  2703. psfNamespace->AddRef();
  2704. HRESULT hrLoop = S_OK;
  2705. while (SUCCEEDED(hr) && SUCCEEDED(hrLoop) && !ILIsEmpty(pidlRel))
  2706. {
  2707. hr = E_OUTOFMEMORY;
  2708. LPITEMIDLIST pidlRelFirst = ILCloneFirst(pidlRel);
  2709. if (pidlRelFirst)
  2710. {
  2711. TCHAR szRelPath[MAX_PATH];
  2712. hr = DisplayNameOf(psfNamespace, pidlRelFirst, SHGDN_FORPARSING | SHGDN_INFOLDER, szRelPath, ARRAYSIZE(szRelPath));
  2713. if (SUCCEEDED(hr))
  2714. {
  2715. LPITEMIDLIST pidlNextPart;
  2716. hr = pmfMerged->_ForceParseDisplayName(pidl, szRelPath, fForce, pfOthersInWrap, &pidlNextPart);
  2717. if (SUCCEEDED(hr))
  2718. {
  2719. // shilappend frees pidlnextpart
  2720. hr = SHILAppend(pidlNextPart, ppidl);
  2721. }
  2722. }
  2723. if (SUCCEEDED(hr))
  2724. {
  2725. // advance and clean up
  2726. IShellFolder *psfFree = psfNamespace;
  2727. psfNamespace = NULL;
  2728. hrLoop = psfFree->BindToObject(pidlRelFirst, pbc, IID_PPV_ARG(IShellFolder, &psfNamespace));
  2729. psfFree->Release();
  2730. if (SUCCEEDED(hrLoop))
  2731. {
  2732. CMergedFolder *pmfFree = pmfMerged;
  2733. pmfMerged = NULL;
  2734. hrLoop = pmfFree->BindToObject(ILFindLastID(*ppidl), pbc, CLSID_MergedFolder, (void **) &pmfMerged);
  2735. pmfFree->Release();
  2736. }
  2737. pidlRel = ILGetNext(pidlRel);
  2738. }
  2739. ILFree(pidlRelFirst);
  2740. }
  2741. }
  2742. if (pmfMerged)
  2743. pmfMerged->Release();
  2744. if (psfNamespace)
  2745. psfNamespace->Release();
  2746. pbc->Release();
  2747. }
  2748. }
  2749. return hr;
  2750. }
  2751. // new translateids.
  2752. // when the start menu works a little better this whole interface can be revised.
  2753. HRESULT CMergedFolder::_NewTranslateIDs(
  2754. LONG *plEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2,
  2755. LPITEMIDLIST * ppidlOut1, LPITEMIDLIST * ppidlOut2,
  2756. LONG *plEvent2, LPITEMIDLIST *ppidlOut1Event2,
  2757. LPITEMIDLIST *ppidlOut2Event2)
  2758. {
  2759. if (!plEvent || !ppidlOut1 || !ppidlOut2)
  2760. return E_INVALIDARG;
  2761. // If they are already wrapped, don't wrap twice.
  2762. if (SUCCEEDED(_IsWrap(ILFindLastID(pidl1))) ||
  2763. SUCCEEDED(_IsWrap(ILFindLastID(pidl2))))
  2764. {
  2765. // We don't want to wrap twice.
  2766. return E_INVALIDARG;
  2767. }
  2768. if (!_hdpaNamespaces)
  2769. return E_FAIL;
  2770. HRESULT hr = E_FAIL;
  2771. switch (*plEvent)
  2772. {
  2773. case SHCNE_EXTENDED_EVENT:
  2774. case SHCNE_ASSOCCHANGED:
  2775. case SHCNE_UPDATEIMAGE:
  2776. return S_OK;
  2777. }
  2778. LONG lEvent = *plEvent;
  2779. *plEvent2 = (LONG)-1;
  2780. *ppidlOut1Event2 = NULL;
  2781. *ppidlOut2Event2 = NULL;
  2782. *ppidlOut1 = (LPITEMIDLIST)pidl1;
  2783. *ppidlOut2 = (LPITEMIDLIST)pidl2;
  2784. CMergedFldrNamespace *pns1, *pns2;
  2785. int iShellFolder1, iShellFolder2;
  2786. BOOL fPidl1IsChild, fPidl2IsChild;
  2787. // Get the information about these Simple pidls: Are they our Children? If so, what _Namespace?
  2788. fPidl1IsChild = _IsChildIDInternal(pidl1, FALSE, &iShellFolder1);
  2789. if (fPidl1IsChild)
  2790. {
  2791. pns1 = _Namespace(iShellFolder1);
  2792. }
  2793. fPidl2IsChild = _IsChildIDInternal(pidl2, FALSE, &iShellFolder2);
  2794. if (fPidl2IsChild)
  2795. {
  2796. pns2 = _Namespace(iShellFolder2);
  2797. }
  2798. // and is either a child?
  2799. if (fPidl1IsChild || fPidl2IsChild)
  2800. {
  2801. hr = S_OK;
  2802. BOOL fOthersInNamespace1, fOthersInNamespace2;
  2803. BOOL fFolderEvent = FALSE;
  2804. switch (*plEvent)
  2805. {
  2806. case SHCNE_MKDIR:
  2807. fFolderEvent = TRUE;
  2808. case SHCNE_CREATE:
  2809. if (fPidl1IsChild)
  2810. {
  2811. hr = _AbsPidlToAbsWrap(pns1, pidl1, FALSE, &fOthersInNamespace1, ppidlOut1);
  2812. if (SUCCEEDED(hr) && fOthersInNamespace1 && !_fDontMerge)
  2813. {
  2814. // whoops, it was already here and this create should become a rename
  2815. // since we're just merging in with the existing pidl.
  2816. *plEvent = fFolderEvent ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
  2817. // this new wrapped one is what it's renamed TO so bump it to ppidlOut2.
  2818. *ppidlOut2 = *ppidlOut1;
  2819. // strip it to get what it used to be.
  2820. hr = _WrapRemoveIDListAbs(*ppidlOut2, iShellFolder1, ppidlOut1);
  2821. }
  2822. }
  2823. break;
  2824. case SHCNE_RMDIR:
  2825. fFolderEvent = TRUE;
  2826. case SHCNE_DELETE:
  2827. if (fPidl1IsChild)
  2828. {
  2829. hr = _AbsPidlToAbsWrap(pns1, pidl1, TRUE, &fOthersInNamespace1, ppidlOut1);
  2830. if (SUCCEEDED(hr) && fOthersInNamespace1 && !_fDontMerge)
  2831. {
  2832. // whoops, there are still more parts to it and it should become a rename
  2833. // since we're just unmerging from the existing pidl.
  2834. *plEvent = fFolderEvent ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
  2835. // strip it to get the "deleted" version
  2836. hr = _WrapRemoveIDListAbs(*ppidlOut1, iShellFolder1, ppidlOut2);
  2837. }
  2838. }
  2839. break;
  2840. case SHCNE_RENAMEFOLDER:
  2841. fFolderEvent = TRUE;
  2842. case SHCNE_RENAMEITEM:
  2843. if (fPidl1IsChild)
  2844. {
  2845. // this is just like a delete.
  2846. *plEvent = fFolderEvent ? SHCNE_RMDIR : SHCNE_DELETE;
  2847. hr = _AbsPidlToAbsWrap(pns1, pidl1, TRUE, &fOthersInNamespace1, ppidlOut1);
  2848. if (SUCCEEDED(hr) && fOthersInNamespace1 && !_fDontMerge)
  2849. {
  2850. // whoops, there are still more parts to it and it should become a rename
  2851. // since we're just unmerging from the existing pidl.
  2852. *plEvent = fFolderEvent ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
  2853. // strip it to get the "deleted" version
  2854. hr = _WrapRemoveIDListAbs(*ppidlOut1, iShellFolder1, ppidlOut2);
  2855. }
  2856. // set ourselves up so that if fPidl2IsChild, it will write into the second event.
  2857. plEvent = plEvent2;
  2858. ppidlOut1 = ppidlOut1Event2;
  2859. ppidlOut2 = ppidlOut2Event2;
  2860. }
  2861. if (fPidl2IsChild)
  2862. {
  2863. // this is just like a create.
  2864. *plEvent = fFolderEvent ? SHCNE_MKDIR : SHCNE_CREATE;
  2865. hr = _AbsPidlToAbsWrap(pns2, pidl2, FALSE, &fOthersInNamespace2, ppidlOut1);
  2866. if (SUCCEEDED(hr) && fOthersInNamespace2 && !_fDontMerge)
  2867. {
  2868. // whoops, it was already here and this create should become a rename
  2869. // since we're just merging in with the existing pidl.
  2870. *plEvent = fFolderEvent ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM;
  2871. // this new wrapped one is what it's renamed TO so bump it to ppidlOut2.
  2872. *ppidlOut2 = *ppidlOut1;
  2873. // strip it to get what it used to be.
  2874. hr = _WrapRemoveIDListAbs(*ppidlOut2, iShellFolder2, ppidlOut1);
  2875. }
  2876. }
  2877. break;
  2878. case SHCNE_UPDATEDIR:
  2879. case SHCNE_UPDATEITEM:
  2880. case SHCNE_MEDIAINSERTED:
  2881. case SHCNE_MEDIAREMOVED:
  2882. hr = _AbsPidlToAbsWrap(pns1, pidl1, FALSE, &fOthersInNamespace1, ppidlOut1);
  2883. break;
  2884. default:
  2885. break;
  2886. }
  2887. }
  2888. return hr;
  2889. }
  2890. STDMETHODIMP CMergedFolder::IsChildID(LPCITEMIDLIST pidlKid, BOOL fImmediate)
  2891. {
  2892. return _IsChildIDInternal(pidlKid, fImmediate, NULL) ? S_OK : S_FALSE;
  2893. }
  2894. STDMETHODIMP CMergedFolder::IsEqualID(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  2895. {
  2896. // This used to return E_NOTIMPL. I'm kinda overloading the interface to mean:
  2897. // is this equal tp any of your _Namespaces.
  2898. HRESULT hr = S_FALSE;
  2899. CMergedFldrNamespace *pns;
  2900. for (int i = 0; (hr == S_FALSE) && (pns = _Namespace(i)); i++)
  2901. {
  2902. if (pidl1)
  2903. {
  2904. if (ILIsEqual(pns->GetIDList(), pidl1))
  2905. hr = S_OK;
  2906. }
  2907. else if (pidl2) // If you pass a pidl2 it means: Is pidl2 a parent of one of my _Namespaces?
  2908. {
  2909. if (ILIsParent(pidl2, pns->GetIDList(), FALSE))
  2910. hr = S_OK;
  2911. }
  2912. }
  2913. return hr;
  2914. }
  2915. typedef struct
  2916. {
  2917. HWND hwnd;
  2918. UINT uMsg;
  2919. LONG lEvents;
  2920. } REGISTERNOTIFYINFO;
  2921. int CMergedFolder::_SetNotifyProc(void *pv, void *pvParam)
  2922. {
  2923. CMergedFldrNamespace *pns = (CMergedFldrNamespace*)pv;
  2924. if (pvParam)
  2925. {
  2926. REGISTERNOTIFYINFO *prni = (REGISTERNOTIFYINFO*)pvParam;
  2927. pns->RegisterNotify(prni->hwnd, prni->uMsg, prni->lEvents);
  2928. }
  2929. else
  2930. {
  2931. pns->UnregisterNotify();
  2932. }
  2933. return 1;
  2934. }
  2935. STDMETHODIMP CMergedFolder::Register(HWND hwnd, UINT uMsg, long lEvents)
  2936. {
  2937. if (_fInShellView)
  2938. {
  2939. // only register the alias if we have no parent folder
  2940. // the merged folder at the junction point can take care of the registration
  2941. // for everybody.
  2942. if (_pidl && !_pmfParent)
  2943. {
  2944. CMergedFldrNamespace *pns;
  2945. for (int i = 0; pns = _Namespace(i); i++)
  2946. {
  2947. if (!ILIsEqual(pns->GetIDList(), _pidl))
  2948. {
  2949. SHChangeNotifyRegisterAlias(pns->GetIDList(), _pidl);
  2950. }
  2951. }
  2952. }
  2953. }
  2954. else if (_hdpaNamespaces)
  2955. {
  2956. REGISTERNOTIFYINFO rni = {hwnd, uMsg, lEvents};
  2957. DPA_EnumCallback(_hdpaNamespaces, _SetNotifyProc, &rni);
  2958. }
  2959. return S_OK;
  2960. }
  2961. STDMETHODIMP CMergedFolder::Unregister()
  2962. {
  2963. if (_hdpaNamespaces)
  2964. {
  2965. DPA_EnumCallback(_hdpaNamespaces, _SetNotifyProc, NULL);
  2966. }
  2967. return S_OK;
  2968. }
  2969. BOOL CMergedFolder::_IsChildIDInternal(LPCITEMIDLIST pidlKid, BOOL fImmediate, int* piShellFolder)
  2970. {
  2971. // This is basically the same Method as the interface method, but returns the shell folder
  2972. // that it came from.
  2973. BOOL fChild = FALSE;
  2974. //At this point we should have a translated pidl
  2975. if (SUCCEEDED(_IsWrap(pidlKid)))
  2976. {
  2977. LPCITEMIDLIST pidlRelKid = ILFindLastID(pidlKid);
  2978. if (pidlRelKid)
  2979. {
  2980. CMergedFldrNamespace *pns;
  2981. for (int i = 0; !fChild && (pns = _Namespace(i)); i++)
  2982. {
  2983. if (ILIsParent(pns->GetIDList(), pidlKid, fImmediate) &&
  2984. !ILIsEqual(pns->GetIDList(), pidlKid))
  2985. {
  2986. fChild = TRUE;
  2987. if (piShellFolder)
  2988. *piShellFolder = i;
  2989. }
  2990. }
  2991. }
  2992. }
  2993. else if (pidlKid)
  2994. {
  2995. CMergedFldrNamespace *pns;
  2996. for (int i = 0; !fChild && (pns = _Namespace(i)); i++)
  2997. {
  2998. if (ILIsParent(pns->GetIDList(), pidlKid, fImmediate))
  2999. {
  3000. fChild = TRUE;
  3001. if (piShellFolder)
  3002. *piShellFolder = i;
  3003. }
  3004. }
  3005. }
  3006. return fChild;
  3007. }
  3008. HRESULT CMergedFolder::_SearchForPidl(int iNamespace, CMergedFldrNamespace *pns, LPCITEMIDLIST pidl, BOOL fFolder, int* piIndex, CMergedFldrItem** ppmfi)
  3009. {
  3010. int iIndex = -1;
  3011. *ppmfi = NULL;
  3012. TCHAR szDisplayName[MAX_PATH];
  3013. if (SUCCEEDED(DisplayNameOf(pns->Folder(), pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szDisplayName, ARRAYSIZE(szDisplayName))))
  3014. {
  3015. SEARCH_FOR_PIDL sfp;
  3016. sfp.pszDisplayName = szDisplayName;
  3017. sfp.fFolder = fFolder;
  3018. sfp.self = this;
  3019. if (_fPartialMerge)
  3020. {
  3021. sfp.iNamespace = iNamespace;
  3022. }
  3023. else
  3024. {
  3025. sfp.iNamespace = -1;
  3026. }
  3027. iIndex = DPA_Search(_hdpaObjects, &sfp, 0, _SearchByName, NULL, DPAS_SORTED);
  3028. if (iIndex >= 0)
  3029. {
  3030. *ppmfi = _GetObject(iIndex);
  3031. }
  3032. }
  3033. if (piIndex)
  3034. *piIndex = iIndex;
  3035. if (*ppmfi)
  3036. return S_OK;
  3037. return S_FALSE;
  3038. }
  3039. HRESULT CMergedFolder::_GetTargetUIObjectOf(IShellFolder *psf, HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl, REFIID riid, UINT *prgf, void **ppv)
  3040. {
  3041. *ppv = NULL;
  3042. IPersistFolder3 *ppf3;
  3043. HRESULT hr = psf->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf3));
  3044. if (SUCCEEDED(hr))
  3045. {
  3046. PERSIST_FOLDER_TARGET_INFO pfti;
  3047. hr = ppf3->GetFolderTargetInfo(&pfti);
  3048. if (SUCCEEDED(hr) && pfti.pidlTargetFolder)
  3049. {
  3050. IShellFolder *psfTarget;
  3051. hr = SHBindToObjectEx(NULL, pfti.pidlTargetFolder, NULL, IID_PPV_ARG(IShellFolder, &psfTarget));
  3052. if (SUCCEEDED(hr))
  3053. {
  3054. DWORD dwAttrib = SFGAO_VALIDATE;
  3055. hr = psfTarget->GetAttributesOf(cidl, apidl, &dwAttrib);
  3056. if (SUCCEEDED(hr))
  3057. {
  3058. hr = psfTarget->GetUIObjectOf(hwnd, cidl, apidl, riid, prgf, ppv);
  3059. }
  3060. psfTarget->Release();
  3061. }
  3062. ILFree(pfti.pidlTargetFolder);
  3063. }
  3064. else
  3065. {
  3066. hr = E_FAIL;
  3067. }
  3068. ppf3->Release();
  3069. }
  3070. if (FAILED(hr))
  3071. {
  3072. DWORD dwAttrib = SFGAO_VALIDATE;
  3073. hr = psf->GetAttributesOf(cidl, apidl, &dwAttrib);
  3074. if (SUCCEEDED(hr))
  3075. {
  3076. hr = psf->GetUIObjectOf(hwnd, cidl, apidl, riid, prgf, ppv);
  3077. }
  3078. }
  3079. return hr;
  3080. }
  3081. HRESULT CMergedFolder::_GetContextMenu(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl, REFIID riid, void **ppv)
  3082. {
  3083. HRESULT hr = E_OUTOFMEMORY;
  3084. UINT iNumCommon = 0;
  3085. LPITEMIDLIST *apidlCommon = new LPITEMIDLIST[cidl];
  3086. if (apidlCommon)
  3087. {
  3088. UINT iNumUser = 0;
  3089. LPITEMIDLIST *apidlUser = new LPITEMIDLIST[cidl];
  3090. if (apidlUser)
  3091. {
  3092. IShellFolder *psf, *psfCommon, *psfUser; // not ref counted
  3093. LPITEMIDLIST pidl;
  3094. for (UINT i = 0; i < cidl; i++)
  3095. {
  3096. if (SUCCEEDED(_NamespaceForItem(apidl[i], ASFF_COMMON, ASFF_COMMON, &psf, &pidl, NULL, TRUE)))
  3097. {
  3098. apidlCommon[iNumCommon++] = pidl;
  3099. psfCommon = psf;
  3100. }
  3101. if (SUCCEEDED(_NamespaceForItem(apidl[i], ASFF_COMMON, 0, &psf, &pidl, NULL, TRUE)))
  3102. {
  3103. apidlUser[iNumUser++] = pidl;
  3104. psfUser = psf;
  3105. }
  3106. }
  3107. IContextMenu *pcmCommon = NULL;
  3108. if (iNumCommon)
  3109. {
  3110. _GetTargetUIObjectOf(psfCommon, hwnd, iNumCommon, (LPCITEMIDLIST *)apidlCommon, IID_X_PPV_ARG(IContextMenu, NULL, &pcmCommon));
  3111. }
  3112. IContextMenu *pcmUser = NULL;
  3113. if (iNumUser)
  3114. {
  3115. _GetTargetUIObjectOf(psfUser, hwnd, iNumUser, (LPCITEMIDLIST *)apidlUser, IID_X_PPV_ARG(IContextMenu, NULL, &pcmUser));
  3116. }
  3117. BOOL fMerge = _fInShellView || (cidl == 1) && _IsFolder(apidl[0]);
  3118. if (fMerge && (pcmCommon || pcmUser))
  3119. {
  3120. hr = CMergedFldrContextMenu_CreateInstance(hwnd, this, cidl, apidl, pcmCommon, pcmUser, (IContextMenu**)ppv);
  3121. }
  3122. else if (pcmUser)
  3123. {
  3124. hr = pcmUser->QueryInterface(riid, ppv);
  3125. }
  3126. else if (pcmCommon)
  3127. {
  3128. hr = pcmCommon->QueryInterface(riid, ppv);
  3129. }
  3130. else
  3131. {
  3132. hr = E_FAIL;
  3133. }
  3134. for (i = 0; i < iNumCommon; i++)
  3135. {
  3136. ILFree(apidlCommon[i]);
  3137. }
  3138. for (i = 0; i < iNumUser; i++)
  3139. {
  3140. ILFree(apidlUser[i]);
  3141. }
  3142. ATOMICRELEASE(pcmCommon);
  3143. ATOMICRELEASE(pcmUser);
  3144. delete [] apidlUser;
  3145. }
  3146. delete [] apidlCommon;
  3147. }
  3148. return hr;
  3149. }
  3150. // out:
  3151. // *ppsf unreffed psf, don't call ->Release()!
  3152. // *ppidlItem clone of pidl in pidlWrap (nested pidl)
  3153. HRESULT CMergedFolder::_NamespaceForItem(LPCITEMIDLIST pidlWrap, ULONG dwAttribMask, ULONG dwAttrib,
  3154. IShellFolder **ppsf, LPITEMIDLIST *ppidlItem, CMergedFldrNamespace **ppns, BOOL fExact)
  3155. {
  3156. if (ppsf)
  3157. *ppsf = NULL;
  3158. if (ppns)
  3159. *ppns = NULL;
  3160. // first try to get the prefered name space based
  3161. HRESULT hr = E_UNEXPECTED; // assume failure from here
  3162. CMergedFldrNamespace *pns;
  3163. LPITEMIDLIST pidl;
  3164. for (UINT i = 0; SUCCEEDED(_GetSubPidl(pidlWrap, i, NULL, &pidl, &pns)); i++)
  3165. {
  3166. if ((dwAttribMask & dwAttrib) == (dwAttribMask & pns->FolderAttrib()))
  3167. {
  3168. hr = S_OK; // don't free, we're going to hand this one back.
  3169. break;
  3170. }
  3171. ILFree(pidl);
  3172. }
  3173. // not found, fall back to the first name space in the wrapped pidl
  3174. if (!fExact && FAILED(hr))
  3175. {
  3176. hr = _GetSubPidl(pidlWrap, 0, NULL, &pidl, &pns);
  3177. }
  3178. // it succeeded, so lets pass out the information that the caller wanted
  3179. if (SUCCEEDED(hr))
  3180. {
  3181. if (ppsf)
  3182. *ppsf = pns->Folder();
  3183. if (ppns)
  3184. *ppns = pns;
  3185. if (ppidlItem)
  3186. {
  3187. // transfer ownership
  3188. *ppidlItem = pidl;
  3189. }
  3190. else
  3191. {
  3192. ILFree(pidl);
  3193. }
  3194. }
  3195. return hr;
  3196. }
  3197. BOOL CMergedFolder::_NamespaceMatches(ULONG dwAttribMask, ULONG dwAttrib, LPCGUID pguid, CMergedFldrNamespace *pns)
  3198. {
  3199. BOOL fRet = FALSE;
  3200. dwAttrib &= dwAttribMask;
  3201. if (dwAttrib == (dwAttribMask & pns->FolderAttrib()))
  3202. {
  3203. // If ASFF_MERGESAMEGUID is set, then the GUID must also match
  3204. if (!(dwAttrib & ASFF_MERGESAMEGUID) ||
  3205. IsEqualGUID(*pguid, pns->GetGUID()))
  3206. {
  3207. fRet = TRUE;
  3208. }
  3209. }
  3210. return fRet;
  3211. }
  3212. // find a name space based on its attributes
  3213. // dwAttribMask is a mask of the bits to test
  3214. // dwAttrib is the value of the bits in the mask, tested for equality
  3215. // pguid is the GUID to match if dwAttrib includes ASFF_MERGESAMEGUID
  3216. //
  3217. // pnSrcID is optional out param
  3218. HRESULT CMergedFolder::_FindNamespace(ULONG dwAttribMask, ULONG dwAttrib, LPCGUID pguid,
  3219. CMergedFldrNamespace **ppns, BOOL fFallback)
  3220. {
  3221. *ppns = NULL;
  3222. CMergedFldrNamespace *pns;
  3223. int i;
  3224. // first look for a matching namespace.
  3225. for (i = 0; !*ppns && (pns = _Namespace(i)); i++)
  3226. {
  3227. if (!_ShouldSuspend(pns->GetGUID()) && _NamespaceMatches(dwAttribMask, dwAttrib, pguid, pns))
  3228. {
  3229. *ppns = pns;
  3230. }
  3231. }
  3232. if (fFallback && !*ppns)
  3233. {
  3234. // if the matching namespace got scoped out, fall back to another.
  3235. for (i = 0; !*ppns && (pns = _Namespace(i)); i++)
  3236. {
  3237. if (!_ShouldSuspend(pns->GetGUID()))
  3238. {
  3239. *ppns = pns;
  3240. }
  3241. }
  3242. }
  3243. return *ppns ? S_OK : E_FAIL;
  3244. }
  3245. int CMergedFolder::_AcquireObjects()
  3246. {
  3247. _fAcquiring = TRUE;
  3248. HDPA hdpa2 = NULL;
  3249. ASSERT(_hdpaObjects == NULL);
  3250. CMergedFldrNamespace *pns;
  3251. for (int i = 0; pns = _Namespace(i); i++)
  3252. {
  3253. if (_ShouldSuspend(pns->GetGUID()))
  3254. {
  3255. continue;
  3256. }
  3257. HDPA *phdpa = (i == 0) ? &_hdpaObjects : &hdpa2;
  3258. IEnumIDList *peid;
  3259. if (S_OK == pns->Folder()->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &peid))
  3260. {
  3261. if (!*phdpa)
  3262. *phdpa = DPA_Create(4);
  3263. if (*phdpa)
  3264. {
  3265. LPITEMIDLIST pidl;
  3266. ULONG cEnum;
  3267. while (S_OK == peid->Next(1, &pidl, &cEnum))
  3268. {
  3269. CMergedFldrItem* pmfiEnum = new CMergedFldrItem;
  3270. if (pmfiEnum)
  3271. {
  3272. // This is ok, the memory just gets
  3273. LPITEMIDLIST pidlWrap;
  3274. if (SUCCEEDED(_CreateWrap(pidl, i, &pidlWrap)) &&
  3275. pmfiEnum->Init(SAFECAST(this, IAugmentedShellFolder2*), pidlWrap, i))
  3276. {
  3277. if (DPA_AppendPtr(*phdpa, pmfiEnum) != -1)
  3278. pmfiEnum = NULL; // don't free below
  3279. }
  3280. if (pmfiEnum)
  3281. delete pmfiEnum;
  3282. }
  3283. ILFree(pidl);
  3284. }
  3285. }
  3286. peid->Release();
  3287. }
  3288. // if we have only hdpa2 but not _hdpaObjects, do a switcheroo.
  3289. if (hdpa2 && !_hdpaObjects)
  3290. {
  3291. _hdpaObjects = hdpa2;
  3292. hdpa2 = NULL;
  3293. }
  3294. // now that we have both hdpa's (or one) let's merge them.
  3295. if (_hdpaObjects && hdpa2)
  3296. {
  3297. DPA_Merge(_hdpaObjects, hdpa2, DPAM_UNION, _Compare, _Merge, (LPARAM)this);
  3298. DPA_DestroyCallback(hdpa2, _DestroyObjectsProc, NULL);
  3299. hdpa2 = NULL;
  3300. }
  3301. else if (_hdpaObjects)
  3302. {
  3303. DPA_Sort(_hdpaObjects, _Compare, (LPARAM)this);
  3304. }
  3305. }
  3306. _fAcquiring = FALSE;
  3307. return _ObjectCount();
  3308. }
  3309. int CMergedFolder::_DestroyObjectsProc(void *pv, void *pvData)
  3310. {
  3311. CMergedFldrItem* pmfiEnum = (CMergedFldrItem*)pv;
  3312. if (pmfiEnum)
  3313. delete pmfiEnum;
  3314. return TRUE;
  3315. }
  3316. void CMergedFolder::_FreeObjects()
  3317. {
  3318. if (!_fAcquiring && _hdpaObjects)
  3319. {
  3320. DPA_DestroyCallback(_hdpaObjects, _DestroyObjectsProc, NULL);
  3321. _hdpaObjects = NULL;
  3322. }
  3323. }
  3324. int CMergedFolder::_DestroyNamespacesProc(void *pv, void *pvData)
  3325. {
  3326. CMergedFldrNamespace* p = (CMergedFldrNamespace*)pv;
  3327. if (p)
  3328. delete p;
  3329. return TRUE;
  3330. }
  3331. void CMergedFolder::_FreeNamespaces()
  3332. {
  3333. if (_hdpaNamespaces)
  3334. {
  3335. DPA_DestroyCallback(_hdpaNamespaces, _DestroyNamespacesProc, NULL);
  3336. _hdpaNamespaces = NULL;
  3337. }
  3338. }
  3339. HRESULT CMergedFolder::_GetPidl(int* piPos, DWORD grfEnumFlags, LPITEMIDLIST* ppidl)
  3340. {
  3341. *ppidl = NULL;
  3342. if (_hdpaObjects == NULL)
  3343. _AcquireObjects();
  3344. if (_hdpaObjects == NULL)
  3345. return E_OUTOFMEMORY;
  3346. BOOL fWantFolders = 0 != (grfEnumFlags & SHCONTF_FOLDERS),
  3347. fWantNonFolders = 0 != (grfEnumFlags & SHCONTF_NONFOLDERS),
  3348. fWantHidden = 0 != (grfEnumFlags & SHCONTF_INCLUDEHIDDEN),
  3349. fWantAll = 0 != (grfEnumFlags & SHCONTF_STORAGE);
  3350. HRESULT hr = S_FALSE;
  3351. while (*piPos < _ObjectCount())
  3352. {
  3353. CMergedFldrItem* pmfiEnum = _GetObject(*piPos);
  3354. if (pmfiEnum)
  3355. {
  3356. BOOL fFolder = 0 != (pmfiEnum->GetFolderAttrib() & SFGAO_FOLDER),
  3357. fHidden = 0 != (pmfiEnum->GetFolderAttrib() & SFGAO_HIDDEN);
  3358. if (fWantAll ||
  3359. (!fHidden || fWantHidden) &&
  3360. ((fFolder && fWantFolders) || (!fFolder && fWantNonFolders)))
  3361. {
  3362. // Copy out the pidl
  3363. hr = SHILClone(pmfiEnum->GetIDList(), ppidl);
  3364. break;
  3365. }
  3366. else
  3367. {
  3368. (*piPos)++;
  3369. }
  3370. }
  3371. }
  3372. return hr;
  3373. }
  3374. HRESULT CMergedFolder::_GetOverlayInfo(LPCITEMIDLIST pidl, int *pIndex, DWORD dwFlags)
  3375. {
  3376. int iOverlayIndex = -1;
  3377. HRESULT hr = S_OK;
  3378. CMergedFldrNamespace *pns;
  3379. if (_fCDBurn)
  3380. {
  3381. hr = E_OUTOFMEMORY;
  3382. LPITEMIDLIST pidlInNamespace;
  3383. if (SUCCEEDED(_GetSubPidl(pidl, 0, NULL, &pidlInNamespace, &pns)))
  3384. {
  3385. ASSERTMSG(!_fDontMerge || _GetSourceCount(pidl) == 1, "item for overlay should have exactly one wrapped pidl if not merged");
  3386. LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
  3387. if (pidlFirst)
  3388. {
  3389. if (_GetSourceCount(pidlFirst) > 1)
  3390. {
  3391. // an overlay to indicate overwrite
  3392. iOverlayIndex = pns->GetConflictOverlayIndex();
  3393. }
  3394. if (iOverlayIndex == -1)
  3395. {
  3396. // overlay to indicate staged
  3397. iOverlayIndex = pns->GetDefaultOverlayIndex();
  3398. }
  3399. if (iOverlayIndex == -1)
  3400. {
  3401. // use the one provided by the namespace
  3402. iOverlayIndex = pns->GetNamespaceOverlayIndex(pidlInNamespace);
  3403. }
  3404. ILFree(pidlFirst);
  3405. }
  3406. ILFree(pidlInNamespace);
  3407. }
  3408. }
  3409. ASSERT(pIndex);
  3410. *pIndex = (dwFlags == SIOM_OVERLAYINDEX) ? iOverlayIndex : INDEXTOOVERLAYMASK(iOverlayIndex);
  3411. return hr;
  3412. }
  3413. HRESULT CMergedFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex)
  3414. {
  3415. return (*pIndex == OI_ASYNC) ? E_PENDING : _GetOverlayInfo(pidl, pIndex, SIOM_OVERLAYINDEX);
  3416. }
  3417. HRESULT CMergedFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIconIndex)
  3418. {
  3419. return _GetOverlayInfo(pidl, pIconIndex, SIOM_ICONINDEX);
  3420. }
  3421. STDMETHODIMP CMergedFolder::BindToParent(LPCITEMIDLIST pidl, REFIID riid, void **ppv, LPITEMIDLIST *ppidlLast)
  3422. {
  3423. // tybeam: nobody seems to call this any more.
  3424. // if this is ever needed tell me to add it back.
  3425. return E_NOTIMPL;
  3426. }
  3427. HRESULT CMergedFolder::_AddComposite(const COMPFOLDERINIT *pcfi)
  3428. {
  3429. HRESULT hr = E_FAIL;
  3430. LPITEMIDLIST pidl = NULL;
  3431. switch (pcfi->uType)
  3432. {
  3433. case CFITYPE_CSIDL:
  3434. SHGetSpecialFolderLocation(NULL, pcfi->csidl, &pidl);
  3435. break;
  3436. case CFITYPE_PIDL:
  3437. pidl = ILClone(pcfi->pidl);
  3438. break;
  3439. case CFITYPE_PATH:
  3440. pidl = ILCreateFromPath(pcfi->pszPath);
  3441. break;
  3442. default:
  3443. ASSERT(FALSE);
  3444. }
  3445. if (pidl)
  3446. {
  3447. hr = AddNameSpace(NULL, NULL, pidl, ASFF_DEFNAMESPACE_ALL);
  3448. ILFree(pidl);
  3449. }
  3450. return hr;
  3451. }
  3452. STDMETHODIMP CMergedFolder::InitComposite(WORD wSignature, REFCLSID refclsid, CFINITF cfiFlags, ULONG celt, const COMPFOLDERINIT *rgCFs)
  3453. {
  3454. HRESULT hr = S_OK;
  3455. _clsid = refclsid;
  3456. ASSERTMSG(cfiFlags == CFINITF_FLAT, "merged folder doesn't support listing namespaces as children any more");
  3457. if (cfiFlags != CFINITF_FLAT)
  3458. {
  3459. hr = E_INVALIDARG;
  3460. }
  3461. if (SUCCEEDED(hr))
  3462. {
  3463. for (ULONG i = 0; SUCCEEDED(hr) && (i < celt); i++)
  3464. {
  3465. hr = _AddComposite(rgCFs + i);
  3466. }
  3467. }
  3468. return hr;
  3469. }
  3470. STDMETHODIMP CMergedFolder::_DeleteItemByIDList(LPCITEMIDLIST pidl)
  3471. {
  3472. LPITEMIDLIST pidlSrc;
  3473. CMergedFldrNamespace *pns;
  3474. HRESULT hr = _NamespaceForItem(pidl, ASFF_DEFNAMESPACE_BINDSTG, ASFF_DEFNAMESPACE_BINDSTG, NULL, &pidlSrc, &pns, FALSE);
  3475. if (SUCCEEDED(hr))
  3476. {
  3477. IStorage *pstg;
  3478. hr = pns->Folder()->QueryInterface(IID_PPV_ARG(IStorage, &pstg));
  3479. if (SUCCEEDED(hr))
  3480. {
  3481. TCHAR szName[MAX_PATH];
  3482. hr = DisplayNameOf(pns->Folder(), pidlSrc, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  3483. if (SUCCEEDED(hr))
  3484. {
  3485. hr = pstg->DestroyElement(szName);
  3486. }
  3487. pstg->Release();
  3488. }
  3489. ILFree(pidlSrc);
  3490. }
  3491. return hr;
  3492. }
  3493. HRESULT CMergedFolder::_GetDestinationStorage(DWORD grfMode, IStorage **ppstg)
  3494. {
  3495. CMergedFldrNamespace *pns;
  3496. HRESULT hr = _FindNamespace(ASFF_DEFNAMESPACE_BINDSTG, ASFF_DEFNAMESPACE_BINDSTG, NULL, &pns, TRUE);
  3497. if (SUCCEEDED(hr))
  3498. {
  3499. IShellFolder *psf;
  3500. LPCITEMIDLIST pidlLast;
  3501. hr = SHBindToIDListParent(pns->GetIDList(), IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
  3502. if (SUCCEEDED(hr))
  3503. {
  3504. // SHGetAttributes doesn't help for SFGAO_VALIDATE
  3505. DWORD dwAttrib = SFGAO_VALIDATE;
  3506. hr = psf->GetAttributesOf(1, &pidlLast, &dwAttrib);
  3507. psf->Release();
  3508. }
  3509. }
  3510. if (SUCCEEDED(hr))
  3511. {
  3512. IBindCtx *pbc;
  3513. hr = BindCtx_CreateWithMode(grfMode, &pbc);
  3514. if (SUCCEEDED(hr))
  3515. {
  3516. hr = SHBindToObjectEx(NULL, pns->GetIDList(), pbc, IID_PPV_ARG(IStorage, ppstg));
  3517. pbc->Release();
  3518. }
  3519. }
  3520. else if (_pmfParent)
  3521. {
  3522. // the current pidl doesnt have the storage target namespace, so create it using our parent.
  3523. IStorage *pstgParent;
  3524. hr = _pmfParent->_GetDestinationStorage(grfMode, &pstgParent);
  3525. if (SUCCEEDED(hr))
  3526. {
  3527. TCHAR szName[MAX_PATH];
  3528. hr = DisplayNameOf((IShellFolder*)(void*)_pmfParent, ILFindLastID(_pidl), SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  3529. if (SUCCEEDED(hr))
  3530. {
  3531. hr = pstgParent->CreateStorage(szName, STGM_READWRITE, 0, 0, ppstg);
  3532. }
  3533. pstgParent->Release();
  3534. }
  3535. }
  3536. return hr;
  3537. }
  3538. STDMETHODIMP CMergedFolder::_StgCreate(LPCITEMIDLIST pidl, DWORD grfMode, REFIID riid, void **ppv)
  3539. {
  3540. HRESULT hr = S_OK;
  3541. // first we need to ensure that the folder exists.
  3542. if (_pstg == NULL)
  3543. {
  3544. hr = _GetDestinationStorage(grfMode, &_pstg);
  3545. }
  3546. if (SUCCEEDED(hr))
  3547. {
  3548. TCHAR szName[MAX_PATH];
  3549. hr = DisplayNameOf((IShellFolder*)(void*)this, pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  3550. if (SUCCEEDED(hr))
  3551. {
  3552. if (IsEqualIID(riid, IID_IStorage))
  3553. {
  3554. hr = _pstg->CreateStorage(szName, grfMode, 0, 0, (IStorage **) ppv);
  3555. }
  3556. else if (IsEqualIID(riid, IID_IStream))
  3557. {
  3558. hr = _pstg->CreateStream(szName, grfMode, 0, 0, (IStream **) ppv);
  3559. }
  3560. else
  3561. {
  3562. hr = E_INVALIDARG;
  3563. }
  3564. }
  3565. }
  3566. return hr;
  3567. }
  3568. // enumerator object
  3569. class CMergedFldrEnum : public IEnumIDList
  3570. {
  3571. public:
  3572. // IUnknown
  3573. STDMETHOD (QueryInterface) (REFIID, void **);
  3574. STDMETHOD_(ULONG,AddRef) ();
  3575. STDMETHOD_(ULONG,Release) ();
  3576. // IEnumIDList
  3577. STDMETHODIMP Next(ULONG, LPITEMIDLIST*, ULONG*);
  3578. STDMETHODIMP Skip(ULONG);
  3579. STDMETHODIMP Reset();
  3580. STDMETHODIMP Clone(IEnumIDList**);
  3581. CMergedFldrEnum(CMergedFolder*pmf, DWORD grfEnumFlags, int iPos = 0);
  3582. private:
  3583. ~CMergedFldrEnum();
  3584. LONG _cRef;
  3585. CMergedFolder*_pmf;
  3586. DWORD _grfEnumFlags;
  3587. int _iPos;
  3588. };
  3589. CMergedFldrEnum::CMergedFldrEnum(CMergedFolder *pfm, DWORD grfEnumFlags, int iPos) :
  3590. _cRef(1), _iPos(iPos), _pmf(pfm), _grfEnumFlags(grfEnumFlags)
  3591. {
  3592. _pmf->AddRef();
  3593. }
  3594. CMergedFldrEnum::~CMergedFldrEnum()
  3595. {
  3596. _pmf->Release();
  3597. }
  3598. STDMETHODIMP CMergedFldrEnum::QueryInterface(REFIID riid, void **ppv)
  3599. {
  3600. static const QITAB qit[] = {
  3601. QITABENT(CMergedFldrEnum, IEnumIDList),
  3602. { 0 }
  3603. };
  3604. return QISearch(this, qit, riid, ppv);
  3605. }
  3606. STDMETHODIMP_(ULONG) CMergedFldrEnum::AddRef()
  3607. {
  3608. return InterlockedIncrement(&_cRef);
  3609. }
  3610. STDMETHODIMP_(ULONG) CMergedFldrEnum::Release()
  3611. {
  3612. ASSERT( 0 != _cRef );
  3613. ULONG cRef = InterlockedDecrement(&_cRef);
  3614. if ( 0 == cRef )
  3615. {
  3616. delete this;
  3617. }
  3618. return cRef;
  3619. }
  3620. // IEnumIDList
  3621. STDMETHODIMP CMergedFldrEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  3622. {
  3623. int iStart = _iPos;
  3624. int cFetched = 0;
  3625. HRESULT hr = S_OK;
  3626. if (!(celt > 0 && rgelt) || (NULL == pceltFetched && celt > 1))
  3627. return E_INVALIDARG;
  3628. *rgelt = 0;
  3629. while (hr == S_OK && (_iPos - iStart) < (int)celt)
  3630. {
  3631. LPITEMIDLIST pidl;
  3632. hr = _pmf->_GetPidl(&_iPos, _grfEnumFlags, &pidl);
  3633. if (hr == S_OK)
  3634. {
  3635. rgelt[cFetched] = pidl;
  3636. cFetched++;
  3637. }
  3638. _iPos++;
  3639. }
  3640. if (pceltFetched)
  3641. *pceltFetched = cFetched;
  3642. return celt == (ULONG)cFetched ? S_OK : S_FALSE;
  3643. }
  3644. STDMETHODIMP CMergedFldrEnum::Skip(ULONG celt)
  3645. {
  3646. _iPos += celt;
  3647. return S_OK;
  3648. }
  3649. STDMETHODIMP CMergedFldrEnum::Reset()
  3650. {
  3651. _iPos = 0;
  3652. return S_OK;
  3653. }
  3654. STDMETHODIMP CMergedFldrEnum::Clone(IEnumIDList **ppenum)
  3655. {
  3656. *ppenum = new CMergedFldrEnum(_pmf, _grfEnumFlags, _iPos);
  3657. return *ppenum ? S_OK : E_OUTOFMEMORY;
  3658. }
  3659. HRESULT CMergedFldrEnum_CreateInstance(CMergedFolder*pmf, DWORD grfFlags, IEnumIDList **ppenum)
  3660. {
  3661. CMergedFldrEnum *penum = new CMergedFldrEnum(pmf, grfFlags);
  3662. if (!penum)
  3663. return E_OUTOFMEMORY;
  3664. HRESULT hr = penum->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
  3665. penum->Release();
  3666. return hr;
  3667. }
  3668. // Drop Target handler
  3669. class CMergedFldrDropTarget : public IDropTarget,
  3670. public IObjectWithSite
  3671. {
  3672. public:
  3673. // IUnknown
  3674. STDMETHOD (QueryInterface) (REFIID, void **);
  3675. STDMETHOD_(ULONG,AddRef) ();
  3676. STDMETHOD_(ULONG,Release) ();
  3677. // IDropTarget
  3678. STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  3679. STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  3680. STDMETHODIMP DragLeave(void);
  3681. STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  3682. // IObjectWithSite
  3683. STDMETHODIMP SetSite(IUnknown *punkSite);
  3684. STDMETHODIMP GetSite(REFIID riid, void **ppvSite);
  3685. CMergedFldrDropTarget(CMergedFolder*pmf, HWND hwnd);
  3686. private:
  3687. ~CMergedFldrDropTarget();
  3688. HRESULT _CreateOtherNameSpace(IShellFolder **ppsf);
  3689. HRESULT _FindTargetNamespace(CMergedFldrNamespace *pnsToMatch, CMergedFldrNamespace **ppns, CMergedFldrNamespace **ppnsMatched);
  3690. HRESULT _CreateFolder(IShellFolder *psf, LPCWSTR pszName, REFIID riid, void **ppv);
  3691. LPITEMIDLIST _FolderIDListFromData(IDataObject *pdtobj);
  3692. BOOL _IsCommonIDList(LPCITEMIDLIST pidlItem);
  3693. HRESULT _SetDropEffectFolders();
  3694. void _DestroyDropEffectFolders();
  3695. LONG _cRef;
  3696. CMergedFolder *_pmf;
  3697. IDropTarget *_pdt;
  3698. IDataObject *_pdo;
  3699. HWND _hwnd;
  3700. BOOL _fSrcIsCommon; // is _pdt a common programs folder (or its child)
  3701. LPITEMIDLIST _pidlSrcFolder; // where the source comes from
  3702. DWORD _grfKeyState;
  3703. DWORD _dwDragEnterEffect;
  3704. };
  3705. CMergedFldrDropTarget::CMergedFldrDropTarget(CMergedFolder*pfm, HWND hwnd) :
  3706. _cRef(1),
  3707. _pmf(pfm),
  3708. _hwnd(hwnd)
  3709. {
  3710. _pmf->AddRef();
  3711. }
  3712. CMergedFldrDropTarget::~CMergedFldrDropTarget()
  3713. {
  3714. _pmf->Release();
  3715. ASSERT(!_pdt);
  3716. ASSERT(!_pdo);
  3717. }
  3718. STDMETHODIMP CMergedFldrDropTarget::QueryInterface(REFIID riid, void **ppv)
  3719. {
  3720. static const QITAB qit[] =
  3721. {
  3722. QITABENT(CMergedFldrDropTarget, IDropTarget),
  3723. QITABENT(CMergedFldrDropTarget, IObjectWithSite),
  3724. { 0 }
  3725. };
  3726. return QISearch(this, qit, riid, ppv);
  3727. }
  3728. STDMETHODIMP_(ULONG) CMergedFldrDropTarget::AddRef()
  3729. {
  3730. return InterlockedIncrement(&_cRef);
  3731. }
  3732. STDMETHODIMP_(ULONG) CMergedFldrDropTarget::Release()
  3733. {
  3734. ASSERT( 0 != _cRef );
  3735. ULONG cRef = InterlockedDecrement(&_cRef);
  3736. if ( 0 == cRef )
  3737. {
  3738. delete this;
  3739. }
  3740. return cRef;
  3741. }
  3742. // is this pidl in the "All Users" part of the name space
  3743. BOOL CMergedFldrDropTarget::_IsCommonIDList(LPCITEMIDLIST pidlItem)
  3744. {
  3745. BOOL bRet = FALSE;
  3746. LPITEMIDLIST pidlCommon;
  3747. if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_STARTMENU, &pidlCommon)))
  3748. {
  3749. bRet = ILIsParent(pidlCommon, pidlItem, FALSE);
  3750. ILFree(pidlCommon);
  3751. }
  3752. if (!bRet &&
  3753. SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_PROGRAMS, &pidlCommon)))
  3754. {
  3755. bRet = ILIsParent(pidlCommon, pidlItem, FALSE);
  3756. ILFree(pidlCommon);
  3757. }
  3758. if (!bRet &&
  3759. SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, &pidlCommon)))
  3760. {
  3761. bRet = ILIsParent(pidlCommon, pidlItem, FALSE);
  3762. ILFree(pidlCommon);
  3763. }
  3764. return bRet;
  3765. }
  3766. LPITEMIDLIST CMergedFldrDropTarget::_FolderIDListFromData(IDataObject *pdtobj)
  3767. {
  3768. LPITEMIDLIST pidlFolder = NULL;
  3769. STGMEDIUM medium = {0};
  3770. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  3771. if (pida)
  3772. {
  3773. pidlFolder = ILClone(HIDA_GetPIDLFolder(pida));
  3774. HIDA_ReleaseStgMedium(pida, &medium);
  3775. }
  3776. return pidlFolder;
  3777. }
  3778. HRESULT CMergedFldrDropTarget::_CreateFolder(IShellFolder *psf, LPCWSTR pszName, REFIID riid, void **ppv)
  3779. {
  3780. *ppv = NULL;
  3781. IStorage *pstg;
  3782. HRESULT hr = psf->QueryInterface(IID_PPV_ARG(IStorage, &pstg));
  3783. if (SUCCEEDED(hr))
  3784. {
  3785. DWORD grfMode = STGM_READWRITE;
  3786. IStorage *pstgNew;
  3787. hr = pstg->OpenStorage(pszName, NULL, grfMode, 0, 0, &pstgNew);
  3788. if (FAILED(hr))
  3789. {
  3790. // try create if it's not there
  3791. hr = pstg->CreateStorage(pszName, grfMode, 0, 0, &pstgNew);
  3792. }
  3793. if (SUCCEEDED(hr))
  3794. {
  3795. hr = pstgNew->QueryInterface(riid, ppv);
  3796. pstgNew->Release();
  3797. }
  3798. pstg->Release();
  3799. }
  3800. return hr;
  3801. }
  3802. HRESULT CMergedFldrDropTarget::_FindTargetNamespace(CMergedFldrNamespace *pnsToMatch, CMergedFldrNamespace **ppnsDstRoot, CMergedFldrNamespace **ppnsMatched)
  3803. {
  3804. *ppnsDstRoot = NULL;
  3805. *ppnsMatched = NULL;
  3806. // if the source of the drag drop is in one of our name spaces
  3807. // we prefer that as the target name space
  3808. for (CMergedFolder*pmf = this->_pmf; pmf && (NULL == *ppnsDstRoot); pmf = pmf->_Parent())
  3809. {
  3810. pmf->_FindNamespace(pnsToMatch->FolderAttrib(), pnsToMatch->FolderAttrib(), &pnsToMatch->GetGUID(), ppnsMatched);
  3811. CMergedFldrNamespace *pns;
  3812. for (int i = 0; (pns = pmf->_Namespace(i)) && (NULL == *ppnsDstRoot); i++)
  3813. {
  3814. if (pmf->_ShouldMergeNamespaces(*ppnsMatched, pns) &&
  3815. ILFindChild(pns->GetIDList(), _pidlSrcFolder))
  3816. {
  3817. *ppnsDstRoot = pns; // if the source is in one of our name spaces
  3818. }
  3819. }
  3820. ASSERT(NULL != *ppnsMatched); // pnsToMatch must exist above us
  3821. // If merging is disabled, then we don't want parent namespaces
  3822. // to infect us.
  3823. if (pmf->_fDontMerge)
  3824. {
  3825. break;
  3826. }
  3827. }
  3828. if (NULL == *ppnsDstRoot)
  3829. {
  3830. // if the source was not found, find the target name space based on
  3831. // 1) if the source data is an "All Users" item find the first common _Namespace
  3832. if (_fSrcIsCommon)
  3833. {
  3834. for (pmf = this->_pmf; pmf && (NULL == *ppnsDstRoot); pmf = pmf->_Parent())
  3835. {
  3836. pmf->_FindNamespace(pnsToMatch->FolderAttrib(), pnsToMatch->FolderAttrib(), &pnsToMatch->GetGUID(), ppnsMatched);
  3837. pmf->_FindNamespace(ASFF_COMMON, ASFF_COMMON, NULL, ppnsDstRoot);
  3838. }
  3839. }
  3840. // 2) find the first NON common name space for this guy
  3841. for (pmf = this->_pmf; pmf && (NULL == *ppnsDstRoot); pmf = pmf->_Parent())
  3842. {
  3843. pmf->_FindNamespace(pnsToMatch->FolderAttrib(), pnsToMatch->FolderAttrib(), &pnsToMatch->GetGUID(), ppnsMatched);
  3844. pmf->_FindNamespace(ASFF_COMMON, 0, NULL, ppnsDstRoot); // search for non common items
  3845. }
  3846. }
  3847. if (NULL == *ppnsMatched && NULL != *ppnsDstRoot)
  3848. {
  3849. delete *ppnsDstRoot;
  3850. *ppnsDstRoot = NULL;
  3851. }
  3852. return *ppnsDstRoot ? S_OK : E_FAIL;
  3853. }
  3854. HRESULT CMergedFldrDropTarget::_CreateOtherNameSpace(IShellFolder **ppsf)
  3855. {
  3856. *ppsf = NULL;
  3857. HRESULT hr = E_FAIL;
  3858. if (_pidlSrcFolder &&
  3859. (!_fSrcIsCommon || AffectAllUsers(_hwnd)))
  3860. {
  3861. CMergedFldrNamespace *pnsDstRoot; // name space we want to create this new item in
  3862. CMergedFldrNamespace *pnsSrc;
  3863. CMergedFldrNamespace *pnsStart = this->_pmf->_Namespace(0);
  3864. if (pnsStart)
  3865. {
  3866. hr = _FindTargetNamespace(pnsStart, &pnsDstRoot, &pnsSrc);
  3867. if (SUCCEEDED(hr))
  3868. {
  3869. LPCITEMIDLIST pidlChild = ILFindChild(pnsSrc->GetIDList(), pnsStart->GetIDList());
  3870. ASSERT(pidlChild && !ILIsEmpty(pidlChild)); // pnsSrc is a parent of pnsStart
  3871. IShellFolder *psfDst = pnsDstRoot->Folder();
  3872. psfDst->AddRef();
  3873. IShellFolder *psfSrc = pnsSrc->Folder();
  3874. psfSrc->AddRef();
  3875. for (LPCITEMIDLIST pidl = pidlChild; !ILIsEmpty(pidl) && SUCCEEDED(hr); pidl = _ILNext(pidl))
  3876. {
  3877. LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
  3878. if (pidlFirst)
  3879. {
  3880. WCHAR szName[MAX_PATH];
  3881. hr = DisplayNameOf(psfSrc, pidlFirst, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  3882. if (SUCCEEDED(hr))
  3883. {
  3884. IShellFolder *psfNew;
  3885. hr = _CreateFolder(psfDst, szName, IID_PPV_ARG(IShellFolder, &psfNew));
  3886. if (SUCCEEDED(hr))
  3887. {
  3888. psfDst->Release();
  3889. psfDst = psfNew;
  3890. }
  3891. else
  3892. {
  3893. break;
  3894. }
  3895. }
  3896. // now advance to the next folder in the source name space
  3897. IShellFolder *psfNew;
  3898. hr = psfSrc->BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfNew));
  3899. if (SUCCEEDED(hr))
  3900. {
  3901. psfSrc->Release();
  3902. psfSrc = psfNew;
  3903. }
  3904. else
  3905. {
  3906. break;
  3907. }
  3908. ILFree(pidlFirst);
  3909. }
  3910. else
  3911. hr = E_FAIL;
  3912. }
  3913. psfSrc->Release();
  3914. if (SUCCEEDED(hr))
  3915. *ppsf = psfDst; // copy out our ref
  3916. else
  3917. psfDst->Release();
  3918. }
  3919. }
  3920. }
  3921. return hr;
  3922. }
  3923. HRESULT CMergedFldrDropTarget::_SetDropEffectFolders()
  3924. {
  3925. INT i, cFolders = 0;
  3926. // Compute the number of folders which have special drop effects
  3927. for (i = 0; i < _pmf->_NamespaceCount(); i++)
  3928. {
  3929. if (_pmf->_Namespace(i)->GetDropEffect() != 0)
  3930. cFolders++;
  3931. }
  3932. // If the number is > 0 then lets add to the IDataObject the clipboard format
  3933. // which defines this information.
  3934. HRESULT hr = S_OK;
  3935. if ((cFolders > 0) || (_pmf->_dwDropEffect != 0))
  3936. {
  3937. DWORD cb = sizeof(DROPEFFECTFOLDERLIST) + sizeof(DROPEFFECTFOLDER) * (cFolders - 1);
  3938. DROPEFFECTFOLDERLIST *pdefl = (DROPEFFECTFOLDERLIST*)LocalAlloc(LPTR, cb);
  3939. if (pdefl)
  3940. {
  3941. pdefl->cFolders = cFolders;
  3942. pdefl->dwDefaultDropEffect = _pmf->_dwDropEffect; // default effect for the folders.
  3943. // fill the array backwards with the folders we have and their desired effects
  3944. for (i = 0, cFolders--; cFolders >= 0; i++)
  3945. {
  3946. DWORD dwDropEffect = _pmf->_Namespace(i)->GetDropEffect();
  3947. if (dwDropEffect != 0)
  3948. {
  3949. pdefl->aFolders[cFolders].dwDropEffect = dwDropEffect;
  3950. StrCpyN(pdefl->aFolders[cFolders].wszPath, _pmf->_Namespace(i)->GetDropFolder(), ARRAYSIZE(pdefl->aFolders[cFolders].wszPath));
  3951. cFolders--;
  3952. }
  3953. }
  3954. ASSERTMSG(g_cfDropEffectFolderList != 0, "Clipboard format for CFSTR_DROPEFFECTFOLDERS not registered");
  3955. hr = DataObj_SetBlob(_pdo, g_cfDropEffectFolderList, pdefl, cb);
  3956. LocalFree(pdefl);
  3957. }
  3958. else
  3959. {
  3960. hr = E_OUTOFMEMORY;
  3961. }
  3962. }
  3963. return hr;
  3964. }
  3965. // nuke the modified drop effects
  3966. void CMergedFldrDropTarget::_DestroyDropEffectFolders()
  3967. {
  3968. // there are no DROPEFFECTFOLDERs so subtract off the [1] array
  3969. DWORD cb = sizeof(DROPEFFECTFOLDERLIST) - sizeof(DROPEFFECTFOLDER);
  3970. DROPEFFECTFOLDERLIST *pdefl = (DROPEFFECTFOLDERLIST*)LocalAlloc(LPTR, cb);
  3971. if (pdefl)
  3972. {
  3973. pdefl->cFolders = 0;
  3974. pdefl->dwDefaultDropEffect = DROPEFFECT_NONE; // means do default handling
  3975. ASSERTMSG(g_cfDropEffectFolderList != 0, "Clipboard format for CFSTR_DROPEFFECTFOLDERS not registered");
  3976. DataObj_SetBlob(_pdo, g_cfDropEffectFolderList, pdefl, cb);
  3977. LocalFree(pdefl);
  3978. }
  3979. }
  3980. HRESULT CMergedFldrDropTarget::SetSite(IUnknown *punkSite)
  3981. {
  3982. IUnknown_SetSite(_pdt, punkSite);
  3983. return S_OK;
  3984. }
  3985. HRESULT CMergedFldrDropTarget::GetSite(REFIID riid, void **ppvSite)
  3986. {
  3987. return IUnknown_GetSite(_pdt, riid, ppvSite);
  3988. }
  3989. HRESULT CMergedFldrDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  3990. {
  3991. ASSERT(!_fSrcIsCommon);
  3992. ASSERT(_pdt == NULL);
  3993. ASSERT(_pidlSrcFolder == NULL);
  3994. ASSERT(_pdo == NULL);
  3995. IUnknown_Set((IUnknown**)&_pdo, pdtobj);
  3996. _SetDropEffectFolders();
  3997. _pidlSrcFolder = _FolderIDListFromData(pdtobj);
  3998. if (_pidlSrcFolder)
  3999. _fSrcIsCommon = _IsCommonIDList(_pidlSrcFolder);
  4000. HRESULT hr;
  4001. CMergedFldrNamespace *pns;
  4002. if (_fSrcIsCommon)
  4003. {
  4004. // if the item is comming from an "All Users" folder we want to target
  4005. // the common folder. if that does not exist yet then we continue on
  4006. // with a NULL _pdt and create that name space when the drop happens
  4007. hr = _pmf->_FindNamespace(ASFF_COMMON, ASFF_COMMON, NULL, &pns);
  4008. }
  4009. else
  4010. {
  4011. // not "All Users" item, get the default name space (if there is one)
  4012. hr = _pmf->_FindNamespace(ASFF_DEFNAMESPACE_VIEWOBJ, ASFF_DEFNAMESPACE_VIEWOBJ, NULL, &pns);
  4013. }
  4014. if (SUCCEEDED(hr) || _pmf->_fCDBurn)
  4015. {
  4016. if (_pmf->_fCDBurn)
  4017. {
  4018. IShellExtInit *psei;
  4019. hr = CoCreateInstance(CLSID_CDBurn, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellExtInit, &psei));
  4020. if (SUCCEEDED(hr))
  4021. {
  4022. hr = psei->Initialize(_pmf->_pidl, NULL, NULL);
  4023. if (SUCCEEDED(hr))
  4024. {
  4025. hr = psei->QueryInterface(IID_PPV_ARG(IDropTarget, &_pdt));
  4026. }
  4027. psei->Release();
  4028. }
  4029. }
  4030. else
  4031. {
  4032. hr = pns->Folder()->CreateViewObject(_hwnd, IID_PPV_ARG(IDropTarget, &_pdt));
  4033. }
  4034. if (SUCCEEDED(hr))
  4035. {
  4036. _pdt->DragEnter(pdtobj, grfKeyState, pt, pdwEffect);
  4037. }
  4038. }
  4039. _grfKeyState = grfKeyState;
  4040. _dwDragEnterEffect = *pdwEffect;
  4041. return S_OK;
  4042. }
  4043. HRESULT CMergedFldrDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  4044. {
  4045. _grfKeyState = grfKeyState;
  4046. return _pdt ? _pdt->DragOver(grfKeyState, pt, pdwEffect) : S_OK;
  4047. }
  4048. HRESULT CMergedFldrDropTarget::DragLeave(void)
  4049. {
  4050. if (_pdt)
  4051. {
  4052. _pdt->DragLeave();
  4053. IUnknown_SetSite(_pdt, NULL);
  4054. IUnknown_Set((IUnknown **)&_pdt, NULL);
  4055. }
  4056. if (_pdo)
  4057. {
  4058. _DestroyDropEffectFolders();
  4059. IUnknown_Set((IUnknown**)&_pdo, NULL);
  4060. }
  4061. _fSrcIsCommon = 0;
  4062. ILFree(_pidlSrcFolder); // accepts NULL
  4063. _pidlSrcFolder = NULL;
  4064. return S_OK;
  4065. }
  4066. HRESULT CMergedFldrDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  4067. {
  4068. HRESULT hr = S_OK;
  4069. if (!_pdt)
  4070. {
  4071. // we came here because we don't have _pdt which means that
  4072. // there is only one folder in our _Namespace and that's not
  4073. // the one we have to drop on. so we need to create it now
  4074. IShellFolder *psf;
  4075. hr = _CreateOtherNameSpace(&psf);
  4076. if (SUCCEEDED(hr))
  4077. {
  4078. hr = psf->CreateViewObject(_hwnd, IID_PPV_ARG(IDropTarget, &_pdt));
  4079. if (SUCCEEDED(hr))
  4080. _pdt->DragEnter(pdtobj, _grfKeyState, pt, &_dwDragEnterEffect);
  4081. psf->Release();
  4082. }
  4083. }
  4084. if (_pdt)
  4085. {
  4086. hr = _pdt->Drop(pdtobj, grfKeyState, pt, pdwEffect);
  4087. DragLeave();
  4088. }
  4089. return S_OK;
  4090. }
  4091. HRESULT CMergedFldrDropTarget_CreateInstance(CMergedFolder*pmf, HWND hwndOwner, IDropTarget **ppdt)
  4092. {
  4093. CMergedFldrDropTarget *pdt = new CMergedFldrDropTarget(pmf, hwndOwner);
  4094. if (!pdt)
  4095. return E_OUTOFMEMORY;
  4096. HRESULT hr = pdt->QueryInterface(IID_PPV_ARG(IDropTarget, ppdt));
  4097. pdt->Release();
  4098. return hr;
  4099. }
  4100. // context menu handling.
  4101. class CMergedFldrContextMenu : public IContextMenu3,
  4102. public IObjectWithSite
  4103. {
  4104. public:
  4105. // IUnknown
  4106. STDMETHOD (QueryInterface)(REFIID, void**);
  4107. STDMETHOD_(ULONG, AddRef)();
  4108. STDMETHOD_(ULONG, Release)();
  4109. // IContextMenu
  4110. STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  4111. STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici);
  4112. STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax);
  4113. // IContextMenu2
  4114. STDMETHODIMP HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
  4115. // IContextMenu3
  4116. STDMETHODIMP HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult);
  4117. // IObjectWithSite
  4118. STDMETHODIMP SetSite(IUnknown *punkSite);
  4119. STDMETHODIMP GetSite(REFIID riid, void **ppvSite);
  4120. CMergedFldrContextMenu(HWND hwnd, IContextMenu *pcmCommon, IContextMenu *pcmUser);
  4121. private:
  4122. ~CMergedFldrContextMenu();
  4123. HRESULT _Initialize(CMergedFolder *pmf, UINT cidl, LPCITEMIDLIST *apidl);
  4124. BOOL _IsMergedCommand(LPCMINVOKECOMMANDINFO pici);
  4125. HRESULT _InvokeCanonical(IContextMenu *pcm, LPCMINVOKECOMMANDINFO pici);
  4126. HRESULT _InvokeMergedCommand(LPCMINVOKECOMMANDINFO pici);
  4127. IContextMenu* _DefaultMenu();
  4128. LONG _cRef;
  4129. IContextMenu * _pcmCommon;
  4130. IContextMenu * _pcmUser;
  4131. UINT _cidl;
  4132. LPITEMIDLIST *_apidl;
  4133. CMergedFolder *_pmfParent;
  4134. UINT _idFirst;
  4135. HWND _hwnd;
  4136. friend HRESULT CMergedFldrContextMenu_CreateInstance(HWND hwnd, CMergedFolder *pmf, UINT cidl, LPCITEMIDLIST *apidl, IContextMenu *pcmCommon, IContextMenu *pcmUser, IContextMenu **ppcm);
  4137. };
  4138. CMergedFldrContextMenu::CMergedFldrContextMenu(HWND hwnd, IContextMenu *pcmCommon, IContextMenu *pcmUser)
  4139. {
  4140. ASSERT(pcmCommon || pcmUser); // need at least one of these
  4141. _cRef = 1;
  4142. _hwnd = hwnd;
  4143. IUnknown_Set((IUnknown **)&_pcmCommon, pcmCommon);
  4144. IUnknown_Set((IUnknown **)&_pcmUser, pcmUser);
  4145. }
  4146. CMergedFldrContextMenu::~CMergedFldrContextMenu()
  4147. {
  4148. ATOMICRELEASE(_pcmCommon);
  4149. ATOMICRELEASE(_pcmUser);
  4150. ATOMICRELEASE(_pmfParent);
  4151. for (UINT i = 0; i < _cidl; i++)
  4152. {
  4153. ILFree(_apidl[i]);
  4154. }
  4155. }
  4156. IContextMenu* CMergedFldrContextMenu::_DefaultMenu()
  4157. {
  4158. ASSERT(_pcmUser || _pcmCommon); // need at least one of these; this is pretty much guaranteed since we're given them
  4159. // at constructor time.
  4160. return _pcmUser ? _pcmUser : _pcmCommon;
  4161. }
  4162. HRESULT CMergedFldrContextMenu::_Initialize(CMergedFolder *pmf, UINT cidl, LPCITEMIDLIST *apidl)
  4163. {
  4164. _pmfParent = pmf;
  4165. if (_pmfParent)
  4166. {
  4167. _pmfParent->AddRef();
  4168. }
  4169. HRESULT hr = E_OUTOFMEMORY;
  4170. LPITEMIDLIST *apidlNew = new LPITEMIDLIST[cidl];
  4171. if (apidlNew)
  4172. {
  4173. hr = S_OK;
  4174. for (UINT i = 0; SUCCEEDED(hr) && i < cidl; i++)
  4175. {
  4176. hr = SHILClone(apidl[i], &(apidlNew[i]));
  4177. }
  4178. if (SUCCEEDED(hr))
  4179. {
  4180. _apidl = apidlNew;
  4181. _cidl = cidl;
  4182. }
  4183. else
  4184. {
  4185. for (i = 0; i < cidl; i++)
  4186. {
  4187. ILFree(apidlNew[i]);
  4188. }
  4189. delete [] apidlNew;
  4190. }
  4191. }
  4192. return hr;
  4193. }
  4194. STDMETHODIMP CMergedFldrContextMenu::QueryInterface(REFIID riid, void **ppv)
  4195. {
  4196. static const QITAB qit[] = {
  4197. QITABENTMULTI(CMergedFldrContextMenu, IContextMenu, IContextMenu3),
  4198. QITABENTMULTI(CMergedFldrContextMenu, IContextMenu2, IContextMenu3),
  4199. QITABENT(CMergedFldrContextMenu, IContextMenu3),
  4200. QITABENT(CMergedFldrContextMenu, IObjectWithSite),
  4201. { 0 },
  4202. };
  4203. return QISearch(this, qit, riid, ppv);
  4204. }
  4205. STDMETHODIMP_(ULONG) CMergedFldrContextMenu::AddRef()
  4206. {
  4207. return InterlockedIncrement(&_cRef);
  4208. }
  4209. STDMETHODIMP_(ULONG) CMergedFldrContextMenu::Release()
  4210. {
  4211. ASSERT( 0 != _cRef );
  4212. ULONG cRef = InterlockedDecrement(&_cRef);
  4213. if ( 0 == cRef )
  4214. {
  4215. delete this;
  4216. }
  4217. return cRef;
  4218. }
  4219. HRESULT CMergedFldrContextMenu::SetSite(IUnknown *punkSite)
  4220. {
  4221. // setsite/getsite will always be matched because _pcmUser and _pcmCommon never change
  4222. // after the constructor is called.
  4223. IUnknown_SetSite(_DefaultMenu(), punkSite);
  4224. return S_OK;
  4225. }
  4226. HRESULT CMergedFldrContextMenu::GetSite(REFIID riid, void **ppvSite)
  4227. {
  4228. return IUnknown_GetSite(_DefaultMenu(), riid, ppvSite);
  4229. }
  4230. HRESULT CMergedFldrContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  4231. {
  4232. HRESULT hr;
  4233. if (_pmfParent->_fInShellView)
  4234. {
  4235. hr = _DefaultMenu()->QueryContextMenu(hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
  4236. }
  4237. else
  4238. {
  4239. if (hmenu)
  4240. {
  4241. HMENU hmContext = SHLoadMenuPopup(HINST_THISDLL, MENU_SM_CONTEXTMENU);
  4242. if (hmContext)
  4243. {
  4244. int i;
  4245. if (!_pcmCommon || !_pcmUser)
  4246. {
  4247. DeleteMenu(hmContext, SMIDM_OPENCOMMON, MF_BYCOMMAND);
  4248. DeleteMenu(hmContext, SMIDM_EXPLORECOMMON, MF_BYCOMMAND);
  4249. }
  4250. _idFirst = idCmdFirst;
  4251. i = Shell_MergeMenus(hmenu, hmContext, -1, idCmdFirst, idCmdLast, MM_ADDSEPARATOR);
  4252. DestroyMenu(hmContext);
  4253. // Make it look "Shell Like"
  4254. SetMenuDefaultItem(hmenu, 0, MF_BYPOSITION);
  4255. hr = ResultFromShort(i);
  4256. }
  4257. else
  4258. hr = E_OUTOFMEMORY;
  4259. }
  4260. else
  4261. hr = E_INVALIDARG;
  4262. }
  4263. return hr;
  4264. }
  4265. BOOL CMergedFldrContextMenu::_IsMergedCommand(LPCMINVOKECOMMANDINFO pici)
  4266. {
  4267. HRESULT hr = S_OK;
  4268. WCHAR szVerb[32];
  4269. if (!IS_INTRESOURCE(pici->lpVerb))
  4270. {
  4271. lstrcpyn(szVerb, (LPCWSTR)pici->lpVerb, ARRAYSIZE(szVerb));
  4272. }
  4273. else
  4274. {
  4275. hr = _DefaultMenu()->GetCommandString((UINT_PTR)pici->lpVerb, GCS_VERBW, NULL, (LPSTR)szVerb, ARRAYSIZE(szVerb));
  4276. }
  4277. BOOL fRet = FALSE;
  4278. if (SUCCEEDED(hr))
  4279. {
  4280. if ((lstrcmpi(szVerb, c_szOpen) == 0) ||
  4281. (lstrcmpi(szVerb, c_szExplore) == 0))
  4282. {
  4283. fRet = TRUE;
  4284. }
  4285. }
  4286. return fRet;
  4287. }
  4288. HRESULT CMergedFldrContextMenu::_InvokeMergedCommand(LPCMINVOKECOMMANDINFO pici)
  4289. {
  4290. // this code is mostly to navigate into folders from the shell view.
  4291. // the reason why we have to do this manually is because the context menus for
  4292. // the different namespaces have dataobjects which refer only to one namespace.
  4293. // when the navigate happens, that means we get passed bogus non-merged pidls
  4294. // into our BindToObject which screws things up.
  4295. ASSERT(_pmfParent->_fInShellView);
  4296. BOOL fHasFolders = FALSE;
  4297. BOOL fHasNonFolders = FALSE;
  4298. for (UINT i = 0; i < _cidl; i++)
  4299. {
  4300. if (_pmfParent->_IsFolder(_apidl[i]))
  4301. {
  4302. fHasFolders = TRUE;
  4303. }
  4304. else
  4305. {
  4306. fHasNonFolders = TRUE;
  4307. }
  4308. }
  4309. HRESULT hr;
  4310. if (fHasFolders && _IsMergedCommand(pici))
  4311. {
  4312. if (!fHasNonFolders)
  4313. {
  4314. // reinit the defcm with a new dataobject
  4315. IShellExtInit *psei;
  4316. hr = _DefaultMenu()->QueryInterface(IID_PPV_ARG(IShellExtInit, &psei));
  4317. if (SUCCEEDED(hr))
  4318. {
  4319. IDataObject *pdo;
  4320. hr = SHCreateFileDataObject(_pmfParent->_pidl, _cidl, (LPCITEMIDLIST *)_apidl, NULL, &pdo);
  4321. if (SUCCEEDED(hr))
  4322. {
  4323. hr = psei->Initialize(_pmfParent->_pidl, pdo, NULL);
  4324. if (SUCCEEDED(hr))
  4325. {
  4326. hr = _DefaultMenu()->InvokeCommand(pici);
  4327. }
  4328. pdo->Release();
  4329. }
  4330. psei->Release();
  4331. }
  4332. }
  4333. else
  4334. {
  4335. // to open both folders and items simultaneously means we'd have to
  4336. // get a new context menu for just the items.
  4337. // we can do this if this scenario comes up.
  4338. hr = E_FAIL;
  4339. }
  4340. }
  4341. else
  4342. {
  4343. hr = _DefaultMenu()->InvokeCommand(pici);
  4344. }
  4345. return hr;
  4346. }
  4347. const ICIVERBTOIDMAP c_sIDVerbMap[] =
  4348. {
  4349. { L"delete", "delete", SMIDM_DELETE, SMIDM_DELETE, },
  4350. { L"rename", "rename", SMIDM_RENAME, SMIDM_RENAME, },
  4351. { L"properties", "properties", SMIDM_PROPERTIES, SMIDM_PROPERTIES, },
  4352. };
  4353. HRESULT CMergedFldrContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  4354. {
  4355. HRESULT hr = E_FAIL;
  4356. CMINVOKECOMMANDINFOEX ici = {0};
  4357. memcpy(&ici, pici, min(sizeof(ici), pici->cbSize));
  4358. ici.cbSize = sizeof(ici);
  4359. if (_pmfParent->_fInShellView)
  4360. {
  4361. hr = _InvokeMergedCommand((LPCMINVOKECOMMANDINFO)&ici);
  4362. }
  4363. else
  4364. {
  4365. UINT id;
  4366. hr = SHMapICIVerbToCmdID((LPCMINVOKECOMMANDINFO)&ici, c_sIDVerbMap, ARRAYSIZE(c_sIDVerbMap), &id);
  4367. if (SUCCEEDED(hr))
  4368. {
  4369. // The below sets an ansi verb only, make sure no lpVerbW is used
  4370. ici.fMask &= (~CMIC_MASK_UNICODE);
  4371. switch (id)
  4372. {
  4373. case SMIDM_OPEN:
  4374. case SMIDM_EXPLORE:
  4375. case SMIDM_OPENCOMMON:
  4376. case SMIDM_EXPLORECOMMON:
  4377. {
  4378. ASSERT(!_pmfParent->_fInShellView);
  4379. IContextMenu * pcm;
  4380. if (id == SMIDM_OPEN || id == SMIDM_EXPLORE)
  4381. {
  4382. pcm = _DefaultMenu();
  4383. }
  4384. else
  4385. {
  4386. pcm = _pcmCommon;
  4387. }
  4388. hr = SHInvokeCommandOnContextMenu(ici.hwnd, NULL, pcm, ici.fMask,
  4389. (id == SMIDM_EXPLORE || id == SMIDM_EXPLORECOMMON) ? "explore" : "open");
  4390. }
  4391. break;
  4392. case SMIDM_PROPERTIES:
  4393. hr = SHInvokeCommandOnContextMenu(ici.hwnd, NULL, _DefaultMenu(), ici.fMask, "properties");
  4394. break;
  4395. case SMIDM_DELETE:
  4396. ici.lpVerb = "delete";
  4397. if (_pcmUser)
  4398. {
  4399. hr = SHInvokeCommandOnContextMenu(ici.hwnd, NULL, _pcmUser, ici.fMask, "delete");
  4400. }
  4401. else
  4402. {
  4403. ASSERT(_pcmCommon);
  4404. ici.fMask |= CMIC_MASK_FLAG_NO_UI;
  4405. if (AffectAllUsers(_hwnd))
  4406. hr = SHInvokeCommandOnContextMenu(ici.hwnd, NULL, _pcmCommon, ici.fMask, "delete");
  4407. else
  4408. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  4409. }
  4410. break;
  4411. case SMIDM_RENAME:
  4412. ASSERT(0);
  4413. hr = E_NOTIMPL; // sftbar picks this off
  4414. break;
  4415. default:
  4416. ASSERTMSG(FALSE, "shouldn't have unknown command");
  4417. hr = E_INVALIDARG;
  4418. }
  4419. }
  4420. }
  4421. return hr;
  4422. }
  4423. HRESULT CMergedFldrContextMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pwReserved, LPSTR pszName, UINT cchMax)
  4424. {
  4425. HRESULT hr = E_NOTIMPL;
  4426. switch (uType)
  4427. {
  4428. case GCS_VERBA:
  4429. case GCS_VERBW:
  4430. hr = SHMapCmdIDToVerb(idCmd, c_sIDVerbMap, ARRAYSIZE(c_sIDVerbMap), pszName, cchMax, GCS_VERBW == uType);
  4431. }
  4432. if (FAILED(hr))
  4433. {
  4434. hr = _DefaultMenu()->GetCommandString(idCmd, uType, pwReserved, pszName, cchMax);
  4435. }
  4436. return hr;
  4437. }
  4438. HRESULT CMergedFldrContextMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
  4439. {
  4440. HRESULT hr;
  4441. if (_pmfParent->_fInShellView)
  4442. {
  4443. IContextMenu2 *pcm2;
  4444. hr = _DefaultMenu()->QueryInterface(IID_PPV_ARG(IContextMenu2, &pcm2));
  4445. if (SUCCEEDED(hr))
  4446. {
  4447. hr = pcm2->HandleMenuMsg(uMsg, wParam, lParam);
  4448. pcm2->Release();
  4449. }
  4450. }
  4451. else
  4452. {
  4453. hr = E_NOTIMPL;
  4454. }
  4455. return hr;
  4456. }
  4457. HRESULT CMergedFldrContextMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
  4458. {
  4459. HRESULT hr;
  4460. if (_pmfParent->_fInShellView)
  4461. {
  4462. IContextMenu3 *pcm3;
  4463. hr = _DefaultMenu()->QueryInterface(IID_PPV_ARG(IContextMenu3, &pcm3));
  4464. if (SUCCEEDED(hr))
  4465. {
  4466. hr = pcm3->HandleMenuMsg2(uMsg, wParam, lParam, plResult);
  4467. pcm3->Release();
  4468. }
  4469. }
  4470. else
  4471. {
  4472. hr = E_NOTIMPL;
  4473. }
  4474. return hr;
  4475. }
  4476. HRESULT CMergedFldrContextMenu_CreateInstance(HWND hwnd, CMergedFolder *pmf, UINT cidl, LPCITEMIDLIST *apidl, IContextMenu *pcmCommon, IContextMenu *pcmUser, IContextMenu **ppcm)
  4477. {
  4478. HRESULT hr = E_OUTOFMEMORY;
  4479. CMergedFldrContextMenu* pcm = new CMergedFldrContextMenu(hwnd, pcmCommon, pcmUser);
  4480. if (pcm)
  4481. {
  4482. hr = pcm->_Initialize(pmf, cidl, apidl);
  4483. if (SUCCEEDED(hr))
  4484. {
  4485. hr = pcm->QueryInterface(IID_PPV_ARG(IContextMenu, ppcm));
  4486. }
  4487. pcm->Release();
  4488. }
  4489. return hr;
  4490. }
  4491. class CMergedCategorizer : public ICategorizer,
  4492. public IShellExtInit
  4493. {
  4494. public:
  4495. // IUnknown
  4496. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  4497. STDMETHODIMP_(ULONG) AddRef(void);
  4498. STDMETHODIMP_(ULONG) Release(void);
  4499. // ICategorizer
  4500. STDMETHODIMP GetDescription(LPWSTR pszDesc, UINT cch);
  4501. STDMETHODIMP GetCategory(UINT cidl, LPCITEMIDLIST * apidl, DWORD* rgCategoryIds);
  4502. STDMETHODIMP GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci);
  4503. STDMETHODIMP CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2);
  4504. // IShellExtInit
  4505. STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID);
  4506. CMergedCategorizer();
  4507. private:
  4508. ~CMergedCategorizer();
  4509. long _cRef;
  4510. CMergedFolder *_pmf;
  4511. };
  4512. // Type Categorizer
  4513. STDAPI CMergedCategorizer_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  4514. {
  4515. HRESULT hr = E_OUTOFMEMORY;
  4516. CMergedCategorizer *pmc = new CMergedCategorizer();
  4517. if (pmc)
  4518. {
  4519. hr = pmc->QueryInterface(riid, ppv);
  4520. pmc->Release();
  4521. }
  4522. return hr;
  4523. }
  4524. CMergedCategorizer::CMergedCategorizer() :
  4525. _cRef(1)
  4526. {
  4527. }
  4528. CMergedCategorizer::~CMergedCategorizer()
  4529. {
  4530. ATOMICRELEASE(_pmf);
  4531. }
  4532. HRESULT CMergedCategorizer::QueryInterface(REFIID riid, void **ppv)
  4533. {
  4534. static const QITAB qit[] =
  4535. {
  4536. QITABENT(CMergedCategorizer, ICategorizer),
  4537. QITABENT(CMergedCategorizer, IShellExtInit),
  4538. { 0 },
  4539. };
  4540. return QISearch(this, qit, riid, ppv);
  4541. }
  4542. ULONG CMergedCategorizer::AddRef()
  4543. {
  4544. return InterlockedIncrement(&_cRef);
  4545. }
  4546. ULONG CMergedCategorizer::Release()
  4547. {
  4548. ASSERT( 0 != _cRef );
  4549. ULONG cRef = InterlockedDecrement(&_cRef);
  4550. if ( 0 == cRef )
  4551. {
  4552. delete this;
  4553. }
  4554. return cRef;
  4555. }
  4556. HRESULT CMergedCategorizer::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID)
  4557. {
  4558. ATOMICRELEASE(_pmf);
  4559. return SHBindToObjectEx(NULL, pidlFolder, NULL, CLSID_MergedFolder, (void**)&_pmf);
  4560. }
  4561. HRESULT CMergedCategorizer::GetDescription(LPWSTR pszDesc, UINT cch)
  4562. {
  4563. LoadString(HINST_THISDLL, IDS_WHICHFOLDER_COL, pszDesc, cch);
  4564. return S_OK;
  4565. }
  4566. HRESULT CMergedCategorizer::GetCategory(UINT cidl, LPCITEMIDLIST *apidl, DWORD *rgCategoryIds)
  4567. {
  4568. HRESULT hr = E_ACCESSDENIED;
  4569. if (_pmf)
  4570. {
  4571. for (UINT i = 0; i < cidl; i++)
  4572. {
  4573. rgCategoryIds[i] = -1;
  4574. CMergedFldrNamespace *pns;
  4575. UINT uSrcID;
  4576. for (UINT j = 0; SUCCEEDED(_pmf->_GetSubPidl(apidl[i], j, &uSrcID, NULL, &pns)); j++)
  4577. {
  4578. if (_pmf->_ShouldSuspend(pns->GetGUID()))
  4579. {
  4580. continue;
  4581. }
  4582. rgCategoryIds[i] = uSrcID;
  4583. if (ASFF_DEFNAMESPACE_ATTRIB & pns->FolderAttrib())
  4584. {
  4585. // if we have more than one, keep the one from the defnamespace for attrib.
  4586. break;
  4587. }
  4588. }
  4589. }
  4590. hr = S_OK;
  4591. }
  4592. return hr;
  4593. }
  4594. HRESULT CMergedCategorizer::GetCategoryInfo(DWORD dwCategoryId, CATEGORY_INFO* pci)
  4595. {
  4596. CMergedFldrNamespace *pns;
  4597. HRESULT hr = _pmf->_Namespace(dwCategoryId, &pns);
  4598. if (SUCCEEDED(hr))
  4599. {
  4600. hr = pns->GetLocation(pci->wszName, ARRAYSIZE(pci->wszName));
  4601. }
  4602. return hr;
  4603. }
  4604. HRESULT CMergedCategorizer::CompareCategory(CATSORT_FLAGS csfFlags, DWORD dwCategoryId1, DWORD dwCategoryId2)
  4605. {
  4606. if (dwCategoryId1 == dwCategoryId2)
  4607. return ResultFromShort(0);
  4608. else if (dwCategoryId1 > dwCategoryId2)
  4609. return ResultFromShort(-1);
  4610. else
  4611. return ResultFromShort(1);
  4612. }
  4613. #define NORMALEVENTS (SHCNE_RENAMEITEM | SHCNE_RENAMEFOLDER | SHCNE_CREATE | SHCNE_DELETE | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM | SHCNE_MKDIR | SHCNE_RMDIR)
  4614. #define CDBURNEVENTS (SHCNE_MEDIAREMOVED | SHCNE_MEDIAINSERTED)
  4615. CMergedFolderViewCB::CMergedFolderViewCB(CMergedFolder *pmf) :
  4616. CBaseShellFolderViewCB(pmf->_pidl, (pmf->_fCDBurn) ? CDBURNEVENTS|NORMALEVENTS : NORMALEVENTS),
  4617. _pmf(pmf)
  4618. {
  4619. _pmf->AddRef();
  4620. }
  4621. CMergedFolderViewCB::~CMergedFolderViewCB()
  4622. {
  4623. _pmf->Release();
  4624. }
  4625. HRESULT CMergedFolderViewCB::_RefreshObjectsWithSameName(IShellFolderView *psfv, LPITEMIDLIST pidl)
  4626. {
  4627. TCHAR szName[MAX_PATH];
  4628. HRESULT hr = DisplayNameOf(SAFECAST(_pmf, IShellFolder2 *), pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
  4629. if (SUCCEEDED(hr))
  4630. {
  4631. CMergedFldrNamespace *pns;
  4632. for (int i = 0; SUCCEEDED(hr) && (pns = _pmf->_Namespace(i)); i++)
  4633. {
  4634. LPITEMIDLIST pidlItem;
  4635. if (SUCCEEDED(pns->Folder()->ParseDisplayName(NULL, NULL, szName, NULL, &pidlItem, NULL)))
  4636. {
  4637. LPITEMIDLIST pidlItemWrap;
  4638. hr = _pmf->_CreateWrap(pidlItem, i, &pidlItemWrap);
  4639. if (SUCCEEDED(hr))
  4640. {
  4641. UINT uItem;
  4642. hr = psfv->UpdateObject(pidlItemWrap, pidlItemWrap, &uItem);
  4643. ILFree(pidlItemWrap);
  4644. }
  4645. ILFree(pidlItem);
  4646. }
  4647. }
  4648. }
  4649. return hr;
  4650. }
  4651. HRESULT CMergedFolderViewCB::_OnFSNotify(DWORD pv, LPCITEMIDLIST *ppidl, LPARAM lp)
  4652. {
  4653. // bail out early if we're merging.
  4654. // S_OK indicates to defview to do what it usually does
  4655. if (!_pmf->_fDontMerge)
  4656. return S_OK;
  4657. IShellFolderView *psfv;
  4658. HRESULT hr = IUnknown_GetSite(SAFECAST(this, IShellFolderViewCB*), IID_PPV_ARG(IShellFolderView, &psfv));
  4659. if (SUCCEEDED(hr))
  4660. {
  4661. LONG lEvent = (LONG) lp;
  4662. LPITEMIDLIST pidl1 = ppidl[0] ? ILFindChild(_pidl, ppidl[0]) : NULL;
  4663. LPITEMIDLIST pidl2 = ppidl[1] ? ILFindChild(_pidl, ppidl[1]) : NULL;
  4664. UINT uItem;
  4665. switch (lEvent)
  4666. {
  4667. case SHCNE_RENAMEFOLDER:
  4668. case SHCNE_RENAMEITEM:
  4669. // we need to handle the in/out for the view
  4670. if (pidl1 && pidl2)
  4671. {
  4672. if (SUCCEEDED(psfv->UpdateObject(pidl1, pidl2, &uItem)))
  4673. hr = S_FALSE;
  4674. _RefreshObjectsWithSameName(psfv, pidl1);
  4675. _RefreshObjectsWithSameName(psfv, pidl2);
  4676. }
  4677. else if (pidl1)
  4678. {
  4679. if (SUCCEEDED(psfv->RemoveObject(pidl1, &uItem)))
  4680. hr = S_FALSE;
  4681. _RefreshObjectsWithSameName(psfv, pidl1);
  4682. }
  4683. else if (pidl2)
  4684. {
  4685. if (SUCCEEDED(psfv->AddObject(pidl2, &uItem)))
  4686. hr = S_FALSE;
  4687. _RefreshObjectsWithSameName(psfv, pidl2);
  4688. }
  4689. break;
  4690. case SHCNE_CREATE:
  4691. case SHCNE_MKDIR:
  4692. ASSERTMSG(pidl1 != NULL, "incoming notify should be child of _pidl because thats what we were listening for");
  4693. if (SUCCEEDED(psfv->AddObject(pidl1, &uItem)))
  4694. hr = S_FALSE;
  4695. _RefreshObjectsWithSameName(psfv, pidl1);
  4696. break;
  4697. case SHCNE_DELETE:
  4698. case SHCNE_RMDIR:
  4699. ASSERTMSG(pidl1 != NULL, "incoming notify should be child of _pidl because thats what we were listening for");
  4700. if (SUCCEEDED(psfv->RemoveObject(pidl1, &uItem)))
  4701. hr = S_FALSE;
  4702. _RefreshObjectsWithSameName(psfv, pidl1);
  4703. break;
  4704. default:
  4705. break;
  4706. }
  4707. psfv->Release();
  4708. }
  4709. return hr;
  4710. }
  4711. STDMETHODIMP CMergedFolderViewCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  4712. {
  4713. switch (uMsg)
  4714. {
  4715. HANDLE_MSG(0, SFVM_FSNOTIFY, _OnFSNotify);
  4716. default:
  4717. return E_NOTIMPL;
  4718. }
  4719. return S_OK;
  4720. }
  4721. HRESULT CMergedFolderViewCB_CreateInstance(CMergedFolder *pmf, IShellFolderViewCB **ppsfvcb)
  4722. {
  4723. HRESULT hr = E_OUTOFMEMORY;
  4724. CMergedFolderViewCB *pcmfvcb = new CMergedFolderViewCB(pmf);
  4725. if (pcmfvcb)
  4726. {
  4727. hr = pcmfvcb->QueryInterface(IID_PPV_ARG(IShellFolderViewCB, ppsfvcb));
  4728. pcmfvcb->Release();
  4729. }
  4730. return hr;
  4731. }