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.

3781 lines
124 KiB

  1. #include "shellprv.h"
  2. #include "ids.h"
  3. #include "infotip.h"
  4. #include "datautil.h"
  5. #include <caggunk.h>
  6. #include "pidl.h"
  7. #include "fstreex.h"
  8. #include "views.h"
  9. #include "shitemid.h"
  10. #include "ole2dup.h"
  11. #include "deskfldr.h"
  12. #include "prop.h"
  13. #include "util.h" // GetVariantFromRegistryValue
  14. #include "defcm.h"
  15. #include "cowsite.h"
  16. #pragma hdrstop
  17. //
  18. // HACKHACK: GUIDs for _IsInNameSpace hack
  19. //
  20. // {D6277990-4C6A-11CF-8D87-00AA0060F5BF}
  21. const GUID CLSID_ScheduledTasks = { 0xD6277990, 0x4C6A, 0x11CF, { 0x8D, 0x87, 0x00, 0xAA, 0x00, 0x60, 0xF5, 0xBF } };
  22. // {7007ACC7-3202-11D1-AAD2-00805FC1270E}
  23. const GUID CLSID_NetworkConnections = { 0x7007ACC7, 0x3202, 0x11D1, { 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E } };
  24. //
  25. // delegate regitems are a special type of IDLIST. they share the same type as a regular REGITEM however
  26. // they have a different IDLIST format. we are unable to change the format of REGITEM IDLISTs so to facilitate
  27. // delegate objects we store items using the following format:
  28. //
  29. // <DELEGATEITEMID> <folder specific data> <DELEGATEITEMDATA>
  30. //
  31. // DELEGATEITEMID
  32. // is a shell structure which contains information about the folder specific information,
  33. // and a regular ITEMIDLIST header.
  34. //
  35. // <folder specific data>
  36. // this is specific to the IShellFolder that is being merged into the namespace.
  37. //
  38. // <DELEGATEITEMDATA>
  39. // this contains a signature so we can determine if the IDLIST is a special delegate,
  40. // and the CLSID of the IShellFolder which owns this data.
  41. //
  42. // all delegate regitems are allocated using the IDelegateMalloc
  43. // {5e591a74-df96-48d3-8d67-1733bcee28ba}
  44. const GUID CLSID_DelegateItemSig = { 0x5e591a74, 0xdf96, 0x48d3, {0x8d, 0x67, 0x17, 0x33, 0xbc, 0xee, 0x28, 0xba} };
  45. typedef struct
  46. {
  47. CLSID clsidSignature; // == CLSID_DelegateItemSig (indicating this is a delegate object)
  48. CLSID clsidShellFolder; // == CLSID of IShellFolder implementing this delegateitem
  49. } DELEGATEITEMDATA;
  50. typedef UNALIGNED DELEGATEITEMDATA *PDELEGATEITEMDATA;
  51. typedef const UNALIGNED DELEGATEITEMDATA *PCDELEGATEITEMDATA;
  52. // IDREGITEM as implemented in NT5 Beta 3, breaks the ctrlfldr IShellFolder of downlevel
  53. // platforms. The clsid is interpreted as the IDCONTROL's oName and oInfo and these offsets
  54. // are way to big for the following buffer (cBuf). On downlevel platform, when we are lucky,
  55. // the offset is still in memory readable by our process, and we just do random stuff. When
  56. // unlucky we try to read memory which we do not have access to and crash. The IDREGITEMEX
  57. // struct solves this by putting padding between bOrder and the CLSID and filling these bytes
  58. // with 0's. When persisted, downlevel platform will interpret these 0's as oName, oInfo and
  59. // as L'\0' at the beggining of cBuf. A _bFlagsLegacy was also added to handle the NT5 Beta3
  60. // persisted pidls. (stephstm, 7/15/99)
  61. // Note: in the case where CRegFldr::_cbPadding == 0, IDREGITEMEX.rgbPadding[0] is at
  62. // same location as the IDREGITEM.clsid
  63. #pragma pack(1)
  64. typedef struct
  65. {
  66. WORD cb;
  67. BYTE bFlags;
  68. BYTE bOrder;
  69. BYTE rgbPadding[16]; // at least 16 to store the clsid
  70. } IDREGITEMEX;
  71. typedef UNALIGNED IDREGITEMEX *LPIDREGITEMEX;
  72. typedef const UNALIGNED IDREGITEMEX *LPCIDREGITEMEX;
  73. #pragma pack()
  74. STDAPI_(BOOL) IsNameListedUnderKey(LPCTSTR pszFileName, LPCTSTR pszKey);
  75. C_ASSERT(sizeof(IDREGITEMEX) == sizeof(IDREGITEM));
  76. EXTERN_C const IDLREGITEM c_idlNet =
  77. {
  78. {sizeof(IDREGITEM), SHID_ROOT_REGITEM, SORT_ORDER_NETWORK,
  79. { 0x208D2C60, 0x3AEA, 0x1069, 0xA2,0xD7,0x08,0x00,0x2B,0x30,0x30,0x9D, },}, // CLSID_NetworkPlaces
  80. 0,
  81. } ;
  82. EXTERN_C const IDLREGITEM c_idlDrives =
  83. {
  84. {sizeof(IDREGITEM), SHID_ROOT_REGITEM, SORT_ORDER_DRIVES,
  85. { 0x20D04FE0, 0x3AEA, 0x1069, 0xA2,0xD8,0x08,0x00,0x2B,0x30,0x30,0x9D, },}, // CLSID_MyComputer
  86. 0,
  87. } ;
  88. EXTERN_C const IDLREGITEM c_idlInetRoot =
  89. {
  90. {sizeof(IDREGITEM), SHID_ROOT_REGITEM, SORT_ORDER_INETROOT,
  91. { 0x871C5380, 0x42A0, 0x1069, 0xA2,0xEA,0x08,0x00,0x2B,0x30,0x30,0x9D, },}, // CSIDL_Internet
  92. 0,
  93. } ;
  94. EXTERN_C const IDREGITEM c_aidlConnections[] =
  95. {
  96. {sizeof(IDREGITEM), SHID_ROOT_REGITEM, SORT_ORDER_DRIVES,
  97. { 0x20D04FE0, 0x3AEA, 0x1069, 0xA2,0xD8,0x08,0x00,0x2B,0x30,0x30,0x9D, },}, // CLSID_MyComputer
  98. {sizeof(IDREGITEM), SHID_COMPUTER_REGITEM, 0,
  99. { 0x21EC2020, 0x3AEA, 0x1069, 0xA2,0xDD,0x08,0x00,0x2B,0x30,0x30,0x9D, },}, // CLSID_ControlPanel
  100. {sizeof(IDREGITEM), SHID_CONTROLPANEL_REGITEM, 0,
  101. { 0x7007ACC7, 0x3202, 0x11D1, 0xAA,0xD2,0x00,0x80,0x5F,0xC1,0x27,0x0E, },}, // CLSID_NetworkConnections
  102. { 0 },
  103. };
  104. enum
  105. {
  106. REGORDERTYPE_OUTERBEFORE = -1,
  107. REGORDERTYPE_REQITEM = 0,
  108. REGORDERTYPE_REGITEM,
  109. REGORDERTYPE_DELEGATE,
  110. REGORDERTYPE_OUTERAFTER
  111. };
  112. //
  113. // class that implements the regitems folder
  114. //
  115. // CLSID_RegFolder {0997898B-0713-11d2-A4AA-00C04F8EEB3E}
  116. const GUID CLSID_RegFolder = { 0x997898b, 0x713, 0x11d2, { 0xa4, 0xaa, 0x0, 0xc0, 0x4f, 0x8e, 0xeb, 0x3e } };
  117. class CRegFolderEnum; // forward
  118. class CRegFolder : public CAggregatedUnknown,
  119. public IShellFolder2,
  120. public IContextMenuCB,
  121. public IShellIconOverlay
  122. {
  123. public:
  124. // IUnknown
  125. STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj)
  126. { return CAggregatedUnknown::QueryInterface(riid, ppvObj); };
  127. STDMETHODIMP_(ULONG) AddRef(void)
  128. { return CAggregatedUnknown::AddRef(); };
  129. //
  130. // PowerDesk98 passes a CFSFolder to CRegFolder::Release(), so validate
  131. // the pointer before proceeding down the path to iniquity.
  132. //
  133. STDMETHODIMP_(ULONG) Release(void)
  134. { return _dwSignature == c_dwSignature ?
  135. CAggregatedUnknown::Release() : 0; };
  136. // IShellFolder
  137. STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszName,
  138. ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes);
  139. STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList);
  140. STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut);
  141. STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj);
  142. STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  143. STDMETHODIMP CreateViewObject (HWND hwndOwner, REFIID riid, void **ppvOut);
  144. STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG *rgfInOut);
  145. STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl,
  146. REFIID riid, UINT * prgfInOut, void **ppvOut);
  147. STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName);
  148. STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags,
  149. LPITEMIDLIST * ppidlOut);
  150. // IShellFolder2
  151. STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid);
  152. STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum);
  153. STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay);
  154. STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState);
  155. STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv);
  156. STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
  157. STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid);
  158. // IShellIconOverlay
  159. STDMETHODIMP GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex);
  160. STDMETHODIMP GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIconIndex);
  161. // IContextMenuCB
  162. STDMETHODIMP CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj,
  163. UINT uMsg, WPARAM wParam, LPARAM lParam);
  164. // IRegItemFolder
  165. STDMETHODIMP Initialize(REGITEMSINFO *pri);
  166. protected:
  167. CRegFolder(IUnknown *punkOuter);
  168. ~CRegFolder();
  169. // used by the CAggregatedUnknown stuff
  170. HRESULT v_InternalQueryInterface(REFIID riid,void **ppvObj);
  171. HRESULT _GetOverlayInfo(LPCIDLREGITEM pidlr, int *pIndex, BOOL fIconIndex);
  172. LPCITEMIDLIST _GetFolderIDList();
  173. LPCIDLREGITEM _AnyRegItem(UINT cidl, LPCITEMIDLIST apidl[]);
  174. BOOL _AllDelegates(UINT cidl, LPCITEMIDLIST *apidl, IShellFolder **ppsf);
  175. int _ReqItemIndex(LPCIDLREGITEM pidlr);
  176. BYTE _GetOrderPure(LPCIDLREGITEM pidlr);
  177. BYTE _GetOrder(LPCIDLREGITEM pidlr);
  178. int _GetOrderType(LPCITEMIDLIST pidl);
  179. void _GetNameSpaceKey(LPCIDLREGITEM pidlr, LPTSTR pszKeyName, UINT cchKeyName);
  180. LPCIDLREGITEM _IsReg(LPCITEMIDLIST pidl);
  181. PDELEGATEITEMID _IsDelegate(LPCIDLREGITEM pidlr);
  182. HRESULT _CreateDelegateFolder(const CLSID* pclsid, REFIID riid, void **ppv);
  183. HRESULT _GetDelegateFolder(PDELEGATEITEMID pidld, REFIID riid, void **ppv);
  184. BOOL _IsInNameSpace(LPCIDLREGITEM pidlr);
  185. HDCA _ItemArray();
  186. HDCA _DelItemArray();
  187. HRESULT _InitFromMachine(IUnknown *punk, BOOL bEnum);
  188. HRESULT _CreateAndInit(LPCIDLREGITEM pidlr, LPBC pbc, REFIID riid, void **ppv);
  189. HRESULT _BindToItem(LPCIDLREGITEM pidlr, LPBC pbc, REFIID riid, void **ppv, BOOL bOneLevel);
  190. HRESULT _GetInfoTip(LPCIDLREGITEM pidlr, void **ppv);
  191. HRESULT _GetRegItemColumnFromRegistry(LPCIDLREGITEM pidlr, LPCTSTR pszColumnName, LPTSTR pszColumnData, int cchColumnData);
  192. HRESULT _GetRegItemVariantFromRegistry(LPCIDLREGITEM pidlr, LPCTSTR pszColumnName, VARIANT *pv);
  193. void _GetClassKeys(LPCIDLREGITEM pidlr, HKEY *phkCLSID, HKEY *phkBase);
  194. HRESULT _GetDisplayNameFromSelf(LPCIDLREGITEM pidlr, DWORD dwFlags, LPTSTR pszName, UINT cchName);
  195. HRESULT _GetDisplayName(LPCIDLREGITEM pidlr, DWORD dwFlags, LPTSTR pszName, UINT cchName);
  196. HRESULT _DeleteRegItem(LPCIDLREGITEM pidlr);
  197. BOOL _GetDeleteMessage(LPCIDLREGITEM pidlr, LPTSTR pszMsg, int cchMax);
  198. HRESULT _ParseNextLevel(HWND hwnd, LPBC pbc, LPCIDLREGITEM pidlr,
  199. LPOLESTR pwzRest, LPITEMIDLIST *ppidlOut, ULONG *pdwAttributes);
  200. HRESULT _ParseGUIDName(HWND hwnd, LPBC pbc, LPOLESTR pwzDisplayName,
  201. LPITEMIDLIST *ppidlOut, ULONG *pdwAttributes);
  202. HRESULT _ParseThroughItem(LPCIDLREGITEM pidlr, HWND hwnd, LPBC pbc,
  203. LPOLESTR pszName, ULONG *pchEaten,
  204. LPITEMIDLIST *ppidlOut, ULONG *pdwAttributes);
  205. HRESULT _SetAttributes(LPCIDLREGITEM pidlr, BOOL bPerUser, DWORD dwMask, DWORD dwNewBits);
  206. LONG _RegOpenCLSIDUSKey(CLSID clsid, PHUSKEY phk);
  207. ULONG _GetPerUserAttributes(LPCIDLREGITEM pidlr);
  208. HRESULT _AttributesOf(LPCIDLREGITEM pidlr, DWORD dwAttributesNeeded, DWORD *pdwAttributes);
  209. HRESULT _CreateDefExtIconKey(HKEY hkey, UINT cidl, LPCITEMIDLIST *apidl, int iItem,
  210. REFIID riid, void** ppvOut);
  211. BOOL _CanDelete(LPCIDLREGITEM pidlr);
  212. void _Delete(HWND hwnd, UINT uFlags, IDataObject *pdtobj);
  213. HRESULT _AssocCreate(LPCIDLREGITEM pidl, REFIID riid, void **ppv);
  214. //
  215. // inline
  216. //
  217. // Will probably not be expanded inline as _GetOrderPure is a behemoth of a fct
  218. void _FillIDList(const CLSID *pclsid, IDLREGITEM *pidlr)
  219. {
  220. pidlr->idri.cb = sizeof(pidlr->idri) + (WORD)_cbPadding;
  221. pidlr->idri.bFlags = _bFlags;
  222. _SetPIDLRCLSID(pidlr, pclsid);
  223. pidlr->idri.bOrder = _GetOrderPure((LPCIDLREGITEM)pidlr);
  224. };
  225. BOOL _IsDesktop() { return ILIsEmpty(_GetFolderIDList()); }
  226. int _MapToOuterColNo(int iCol);
  227. // CompareIDs Helpers
  228. int _CompareIDsOriginal(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  229. int _CompareIDsFolderFirst(UINT iColumn, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  230. int _CompareIDsAlphabetical(UINT iColumn, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  231. BOOL _IsFolder(LPCITEMIDLIST pidl);
  232. HRESULT _CreateViewObjectFor(LPCIDLREGITEM pidlr, HWND hwnd, REFIID riid, void **ppv, BOOL bOneLevel);
  233. private:
  234. inline UNALIGNED const CLSID& _GetPIDLRCLSID(LPCIDLREGITEM pidlr);
  235. inline void _SetPIDLRCLSID(LPIDLREGITEM pidlr, const CLSID *pclsid);
  236. inline IDLREGITEM* _CreateAndFillIDLREGITEM(const CLSID *pclsid);
  237. BOOL _GetNameFromCache(REFCLSID rclsid, DWORD dwFlags, LPTSTR pszName, UINT cchName);
  238. inline void _ClearNameFromCache();
  239. void _SaveNameInCache(REFCLSID rclsid, DWORD dwFlags, LPTSTR pszName);
  240. private:
  241. enum { c_dwSignature = 0x38394450 }; // "PD98" - PowerDesk 98 hack
  242. DWORD _dwSignature;
  243. LPTSTR _pszMachine;
  244. LPITEMIDLIST _pidl;
  245. IShellFolder2 *_psfOuter;
  246. IShellIconOverlay *_psioOuter;
  247. IPersistFreeThreadedObject *_pftoReg; // cached pointer of last free threaded bind
  248. int _iTypeOuter; // default sort order for outer items
  249. LPCTSTR _pszRegKey;
  250. LPCTSTR _pszSesKey;
  251. REGITEMSPOLICY* _pPolicy;
  252. TCHAR _chRegItem; // parsing prefix, must be TEXT(':')
  253. BYTE _bFlags; // flags field for PIDL construction
  254. DWORD _dwDefAttributes; // default attributes for items
  255. int _nRequiredItems; // # of required items
  256. DWORD _dwSortAttrib; // sorting attributes
  257. DWORD _cbPadding; // see comment in views.h
  258. BYTE _bFlagsLegacy; // see comment in views.h
  259. CLSID _clsidAttributesCache;
  260. ULONG _dwAttributesCache;
  261. ULONG _dwAttributesCacheValid;
  262. LONG _lNameCacheInterlock;
  263. DWORD _dwNameCacheTime;
  264. CLSID _clsidNameCache;
  265. DWORD _dwFlagsNameCache;
  266. TCHAR _szNameCache[64];
  267. REQREGITEM *_aReqItems;
  268. IPersistFreeThreadedObject *_pftoDelegate;
  269. CRITICAL_SECTION _cs;
  270. BOOL _fcs;
  271. friend DWORD CALLBACK _RegFolderPropThreadProc(void *pv);
  272. friend HRESULT CRegFolder_CreateInstance(REGITEMSINFO *pri, IUnknown *punkOuter, REFIID riid, void **ppv);
  273. friend CRegFolderEnum;
  274. };
  275. class CRegFolderEnum : public CObjectWithSite, IEnumIDList
  276. {
  277. public:
  278. // IUnknown
  279. STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  280. STDMETHODIMP_(ULONG) AddRef();
  281. STDMETHODIMP_(ULONG) Release();
  282. // IEnumIDList
  283. STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
  284. STDMETHODIMP Skip(ULONG celt) { return E_NOTIMPL; };
  285. STDMETHODIMP Reset();
  286. STDMETHODIMP Clone(IEnumIDList **ppenum) { return E_NOTIMPL; };
  287. // IObjectWithSite
  288. STDMETHODIMP SetSite(IUnknown* punkSite); // we need to override this
  289. protected:
  290. CRegFolderEnum(CRegFolder* prf, DWORD grfFlags, IEnumIDList* pesf, HDCA dcaItems, HDCA dcaDel, REGITEMSPOLICY* pPolicy);
  291. ~CRegFolderEnum();
  292. BOOL _IsRestricted();
  293. BOOL _WrongMachine();
  294. BOOL _TestFolderness(DWORD dwAttribItem);
  295. BOOL _TestHidden(LPCIDLREGITEM pidlRegItem);
  296. BOOL _TestHiddenInWebView(LPCLSID clsidRegItem);
  297. BOOL _TestHiddenInDomain(LPCLSID clsidRegItem);
  298. private:
  299. LONG _cRef;
  300. CRegFolder* _prf; // reg item folder
  301. IEnumIDList* _peidl;
  302. DWORD _grfFlags; // guy we are wrapping
  303. REGITEMSPOLICY* _pPolicy; // controls what items are visible
  304. HDCA _hdca; // DCA of regitem objects
  305. INT _iCur;
  306. HDCA _hdcaDel; // delegate shell folder;
  307. INT _iCurDel; // index into the delegate folder DCA.
  308. IEnumIDList *_peidlDel; // delegate folder enumerator
  309. friend CRegFolder;
  310. };
  311. STDAPI CDelegateMalloc_Create(void *pv, SIZE_T cbSize, WORD wOuter, IMalloc **ppmalloc);
  312. HRESULT ShowHideIconOnlyOnDesktop(const CLSID *pclsid, int StartIndex, int EndIndex, BOOL fHide);
  313. //
  314. // Construction / Destruction and aggregation
  315. //
  316. CRegFolder::CRegFolder(IUnknown *punkOuter) :
  317. _dwSignature(c_dwSignature),
  318. CAggregatedUnknown(punkOuter),
  319. _pidl(NULL),
  320. _pszMachine(NULL),
  321. _psfOuter(NULL),
  322. _lNameCacheInterlock(-1)
  323. {
  324. DllAddRef();
  325. }
  326. CRegFolder::~CRegFolder()
  327. {
  328. IUnknown *punkCached = (IUnknown *)InterlockedExchangePointer((void**)&_pftoReg, NULL);
  329. if (punkCached)
  330. punkCached->Release();
  331. punkCached = (IUnknown *)InterlockedExchangePointer((void**)&_pftoDelegate, NULL);
  332. if (punkCached)
  333. punkCached->Release();
  334. ILFree(_pidl);
  335. Str_SetPtr(&_pszMachine, NULL);
  336. LocalFree(_aReqItems);
  337. SHReleaseOuterInterface(_GetOuter(), (IUnknown **)&_psfOuter); // release _psfOuter
  338. SHReleaseOuterInterface(_GetOuter(), (IUnknown **)&_psioOuter);
  339. if (_fcs)
  340. {
  341. DeleteCriticalSection(&_cs);
  342. }
  343. DllRelease();
  344. }
  345. HRESULT CRegFolder::v_InternalQueryInterface(REFIID riid, void **ppv)
  346. {
  347. static const QITAB qit[] = {
  348. QITABENTMULTI(CRegFolder, IShellFolder, IShellFolder2), // IID_IShellFolder
  349. QITABENT(CRegFolder, IShellFolder2), // IID_IShellFolder2
  350. QITABENT(CRegFolder, IShellIconOverlay), // IID_IShellIconOverlay
  351. { 0 },
  352. };
  353. HRESULT hr = QISearch(this, qit, riid, ppv);
  354. if (FAILED(hr) && IsEqualIID(CLSID_RegFolder, riid))
  355. {
  356. *ppv = this; // not ref counted
  357. hr = S_OK;
  358. }
  359. return hr;
  360. }
  361. //
  362. // get the pidl used to initialize this namespace
  363. //
  364. LPCITEMIDLIST CRegFolder::_GetFolderIDList()
  365. {
  366. if (!_pidl)
  367. SHGetIDListFromUnk(_psfOuter, &_pidl);
  368. return _pidl;
  369. }
  370. //
  371. // check to see if this pidl is a regitem
  372. //
  373. LPCIDLREGITEM CRegFolder::_IsReg(LPCITEMIDLIST pidl)
  374. {
  375. if (pidl && !ILIsEmpty(pidl))
  376. {
  377. LPCIDLREGITEM pidlr = (LPCIDLREGITEM)pidl;
  378. if ((pidlr->idri.bFlags == _bFlags) &&
  379. ((pidl->mkid.cb >= (sizeof(pidlr->idri) + (WORD)_cbPadding)) || _IsDelegate(pidlr)))
  380. {
  381. return pidlr;
  382. }
  383. else if (_cbPadding && _bFlagsLegacy && (pidlr->idri.bFlags == _bFlagsLegacy))
  384. {
  385. // We needed to add padding to the Control Panel regitems. There was CP
  386. // regitems out there without the padding. If there is padding and we fail
  387. // the above case then maybe we are dealing with one of these. (stephstm)
  388. return pidlr;
  389. }
  390. }
  391. return NULL;
  392. }
  393. PDELEGATEITEMID CRegFolder::_IsDelegate(LPCIDLREGITEM pidlr)
  394. {
  395. PDELEGATEITEMID pidld = (PDELEGATEITEMID)pidlr; // save casting below
  396. if ((pidld->cbSize > sizeof(*pidld)) &&
  397. /* note, (int) casts needed to force signed evaluation as we need the < 0 case */
  398. (((int)pidld->cbSize - (int)pidld->cbInner) >= (int)sizeof(DELEGATEITEMDATA)))
  399. {
  400. PDELEGATEITEMDATA pdeidl = (PDELEGATEITEMDATA)&pidld->rgb[pidld->cbInner];
  401. const CLSID clsid = pdeidl->clsidSignature; // alignment
  402. if (IsEqualGUID(clsid, CLSID_DelegateItemSig))
  403. {
  404. return pidld;
  405. }
  406. }
  407. return NULL;
  408. }
  409. BOOL CRegFolder::_AllDelegates(UINT cidl, LPCITEMIDLIST *apidl, IShellFolder **ppsf)
  410. {
  411. *ppsf = NULL;
  412. PDELEGATEITEMID pidld = NULL;
  413. CLSID clsid, clsidFirst;
  414. for (UINT i = 0; i < cidl; i++)
  415. {
  416. LPCIDLREGITEM pidlr = _IsReg(apidl[i]);
  417. if (pidlr)
  418. {
  419. pidld = _IsDelegate(pidlr);
  420. if (pidld)
  421. {
  422. if (i == 0)
  423. {
  424. // get the clsid of the first guy
  425. clsidFirst = _GetPIDLRCLSID(pidlr);
  426. }
  427. else if (clsid = _GetPIDLRCLSID(pidlr), clsidFirst != clsid)
  428. {
  429. pidld = NULL; // not from the same delegate
  430. break;
  431. }
  432. }
  433. else
  434. {
  435. break;
  436. }
  437. }
  438. else
  439. {
  440. pidld = NULL;
  441. break;
  442. }
  443. }
  444. return pidld && SUCCEEDED(_GetDelegateFolder(pidld, IID_PPV_ARG(IShellFolder, ppsf)));
  445. }
  446. __inline IPersistFreeThreadedObject *ExchangeFTO(IPersistFreeThreadedObject **ppfto, IPersistFreeThreadedObject *pfto)
  447. {
  448. return (IPersistFreeThreadedObject *)InterlockedExchangePointer((void**)ppfto, pfto);
  449. }
  450. HRESULT CRegFolder::_CreateDelegateFolder(const CLSID* pclsid, REFIID riid, void **ppv)
  451. {
  452. HRESULT hr;
  453. *ppv = NULL;
  454. // try using the cached delegate (if it exists)
  455. IPersistFreeThreadedObject *pfto = ExchangeFTO(&_pftoDelegate, NULL);
  456. if (pfto)
  457. {
  458. CLSID clsidT;
  459. if (SUCCEEDED(pfto->GetClassID(&clsidT)) && IsEqualGUID(clsidT, *pclsid))
  460. {
  461. // if this fails, ppv will still be NULL
  462. // so we will create a new cache item...
  463. hr = pfto->QueryInterface(riid, ppv);
  464. }
  465. }
  466. if (NULL == *ppv)
  467. {
  468. IDelegateFolder *pdel;
  469. hr = SHExtCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IDelegateFolder, &pdel));
  470. if (SUCCEEDED(hr))
  471. {
  472. DELEGATEITEMDATA delid = { 0 };
  473. delid.clsidSignature = CLSID_DelegateItemSig;
  474. delid.clsidShellFolder = *pclsid;
  475. IMalloc *pm;
  476. hr = CDelegateMalloc_Create(&delid, sizeof(delid), _bFlags, &pm);
  477. if (SUCCEEDED(hr))
  478. {
  479. hr = pdel->SetItemAlloc(pm);
  480. if (SUCCEEDED(hr))
  481. {
  482. IPersistFolder *ppf;
  483. if (SUCCEEDED(pdel->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf))))
  484. {
  485. hr = ppf->Initialize(_GetFolderIDList());
  486. ppf->Release();
  487. }
  488. if (SUCCEEDED(hr))
  489. hr = pdel->QueryInterface(riid, ppv);
  490. }
  491. pm->Release();
  492. }
  493. // now, we might be able to cache this guy if he is "free threaded"
  494. if (SUCCEEDED(hr))
  495. {
  496. if (pfto)
  497. {
  498. pfto->Release();
  499. pfto = NULL;
  500. }
  501. if (SUCCEEDED(pdel->QueryInterface(IID_PPV_ARG(IPersistFreeThreadedObject, &pfto))))
  502. {
  503. SHPinDllOfCLSID(pclsid);
  504. }
  505. }
  506. pdel->Release();
  507. }
  508. }
  509. if (pfto)
  510. {
  511. pfto = ExchangeFTO(&_pftoDelegate, pfto);
  512. if (pfto)
  513. pfto->Release(); // protect against race condition or re-entrancy
  514. }
  515. return hr;
  516. }
  517. HRESULT CRegFolder::_GetDelegateFolder(PDELEGATEITEMID pidld, REFIID riid, void **ppv)
  518. {
  519. PDELEGATEITEMDATA pdeidl = (PDELEGATEITEMDATA)&pidld->rgb[pidld->cbInner];
  520. CLSID clsid = pdeidl->clsidShellFolder; // alignment
  521. return _CreateDelegateFolder(&clsid, riid, ppv);
  522. }
  523. //
  524. // returns a ~REFERENCE~ to the CLSID in the pidlr. HintHint: the ref has
  525. // the same scope as the pidlr. This is to replace the pidlr->idri.clsid
  526. // usage. (stephstm)
  527. //
  528. UNALIGNED const CLSID& CRegFolder::_GetPIDLRCLSID(LPCIDLREGITEM pidlr)
  529. {
  530. #ifdef DEBUG
  531. if (_cbPadding && (_bFlagsLegacy != pidlr->idri.bFlags))
  532. {
  533. LPIDREGITEMEX pidriex = (LPIDREGITEMEX)&(pidlr->idri);
  534. for (DWORD i = 0; i < _cbPadding; ++i)
  535. {
  536. ASSERT(0 == pidriex->rgbPadding[i]);
  537. }
  538. }
  539. #endif
  540. PDELEGATEITEMID pidld = _IsDelegate(pidlr);
  541. if (pidld)
  542. {
  543. PDELEGATEITEMDATA pdeidl = (PDELEGATEITEMDATA)&pidld->rgb[pidld->cbInner];
  544. return pdeidl->clsidShellFolder;
  545. }
  546. return (pidlr->idri.bFlags != _bFlagsLegacy) ?
  547. // return the new padded clsid
  548. ((UNALIGNED CLSID&)((LPIDREGITEMEX)&(pidlr->idri))->rgbPadding[_cbPadding]) :
  549. // return the old non-padded clsid
  550. (pidlr->idri.clsid);
  551. }
  552. // This fct is called only for IDREGITEMs created within this file. It is not
  553. // called for existing PIDL, so we do not need to check if it is a legacy pidl.
  554. void CRegFolder::_SetPIDLRCLSID(LPIDLREGITEM pidlr, const CLSID *pclsid)
  555. {
  556. LPIDREGITEMEX pidriex = (LPIDREGITEMEX)&(pidlr->idri);
  557. ((UNALIGNED CLSID&)pidriex->rgbPadding[_cbPadding]) = *pclsid;
  558. ZeroMemory(pidriex->rgbPadding, _cbPadding);
  559. }
  560. IDLREGITEM* CRegFolder::_CreateAndFillIDLREGITEM(const CLSID *pclsid)
  561. {
  562. IDLREGITEM* pidlRegItem = (IDLREGITEM*)_ILCreate(sizeof(IDLREGITEM) + _cbPadding);
  563. if (pidlRegItem)
  564. {
  565. _FillIDList(pclsid, pidlRegItem);
  566. }
  567. return pidlRegItem;
  568. }
  569. //
  570. // Returns: ptr to the first reg item if there are any
  571. //
  572. LPCIDLREGITEM CRegFolder::_AnyRegItem(UINT cidl, LPCITEMIDLIST apidl[])
  573. {
  574. for (UINT i = 0; i < cidl; i++)
  575. {
  576. LPCIDLREGITEM pidlr = _IsReg(apidl[i]);
  577. if (pidlr)
  578. return pidlr;
  579. }
  580. return NULL;
  581. }
  582. int CRegFolder::_ReqItemIndex(LPCIDLREGITEM pidlr)
  583. {
  584. const CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment
  585. for (int i = _nRequiredItems - 1; i >= 0; i--)
  586. {
  587. if (IsEqualGUID(clsid, *_aReqItems[i].pclsid))
  588. {
  589. break;
  590. }
  591. }
  592. return i;
  593. }
  594. //
  595. // a sort order of 0 means there is not specified sort order for this item
  596. //
  597. BYTE CRegFolder::_GetOrderPure(LPCIDLREGITEM pidlr)
  598. {
  599. BYTE bRet;
  600. int i = _ReqItemIndex(pidlr);
  601. if (i != -1)
  602. {
  603. bRet = _aReqItems[i].bOrder;
  604. }
  605. else
  606. {
  607. HKEY hkey;
  608. TCHAR szKey[MAX_PATH], szCLSID[GUIDSTR_MAX];
  609. HRESULT hr;
  610. SHStringFromGUID(_GetPIDLRCLSID(pidlr), szCLSID, ARRAYSIZE(szCLSID));
  611. bRet = 128; // default for items that do not register a SortOrderIndex
  612. hr = StringCchPrintf(szKey, ARRAYSIZE(szKey), TEXT("CLSID\\%s"), szCLSID);
  613. if (SUCCEEDED(hr))
  614. {
  615. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, szKey, NULL, KEY_QUERY_VALUE, &hkey))
  616. {
  617. DWORD dwOrder, cbSize = sizeof(dwOrder);
  618. if (SHQueryValueEx(hkey, TEXT("SortOrderIndex"), NULL, NULL, (BYTE *)&dwOrder, &cbSize) == ERROR_SUCCESS)
  619. {
  620. // B#221890 - PowerDesk assumes that it can do this:
  621. // Desktop -> First child -> Third child
  622. // and it will get the C: drive. This means that
  623. // My Computer must be the first regitem. So any items
  624. // in front of My Computer are put immediately behind.
  625. if ((SHGetAppCompatFlags(ACF_MYCOMPUTERFIRST) & ACF_MYCOMPUTERFIRST) &&
  626. dwOrder <= SORT_ORDER_DRIVES)
  627. dwOrder = SORT_ORDER_DRIVES + 1;
  628. bRet = (BYTE)dwOrder;
  629. }
  630. RegCloseKey(hkey);
  631. }
  632. }
  633. }
  634. return bRet;
  635. }
  636. BYTE CRegFolder::_GetOrder(LPCIDLREGITEM pidlr)
  637. {
  638. if (!_IsDelegate(pidlr))
  639. {
  640. // If the bOrder values are less than 0x40, then they are the old values
  641. // Therefore compute the new bOrder values for these cases.
  642. if (pidlr->idri.bOrder <= 0x40)
  643. return _GetOrderPure(pidlr);
  644. else
  645. return pidlr->idri.bOrder;
  646. }
  647. else
  648. return 128;
  649. }
  650. //
  651. // validate that this item exists in this name space (look in the registry)
  652. //
  653. BOOL CRegFolder::_IsInNameSpace(LPCIDLREGITEM pidlr)
  654. {
  655. TCHAR szKeyName[MAX_PATH];
  656. if (_IsDelegate(pidlr))
  657. return FALSE; // its a delegate, therefore by default its transient
  658. if (_ReqItemIndex(pidlr) >= 0)
  659. return TRUE;
  660. // HACKHACK: we will return TRUE for Printers, N/W connections and Scheduled tasks
  661. // since they've been moved from My Computer to Control Panel and they
  662. // don't really care where they live
  663. const CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment
  664. if (IsEqualGUID(CLSID_Printers, clsid) ||
  665. IsEqualGUID(CLSID_NetworkConnections, clsid) ||
  666. IsEqualGUID(CLSID_ScheduledTasks, clsid))
  667. {
  668. return TRUE;
  669. }
  670. _GetNameSpaceKey(pidlr, szKeyName, ARRAYSIZE(szKeyName));
  671. // Note that we do not look in the session key,
  672. // since it by definition is transient
  673. return SHRegSubKeyExists(HKEY_LOCAL_MACHINE, szKeyName) ||
  674. SHRegSubKeyExists(HKEY_CURRENT_USER, szKeyName);
  675. }
  676. //
  677. // The "Session key" is a volatile registry key unique to this session.
  678. // A session is a single continuous logon. If Explorer crashes and is
  679. // auto-restarted, the two Explorers share the same session. But if you
  680. // log off and back on, that new Explorer is a new session.
  681. //
  682. //
  683. // The s_SessionKeyName is the name of the session key relative to
  684. // REGSTR_PATH_EXPLORER\SessionInfo. On NT, this is normally the
  685. // Authentication ID, but we pre-initialize it to something safe so
  686. // we don't fault if for some reason we can't get to it. Since
  687. // Win95 supports only one session at a time, it just stays at the
  688. // default value.
  689. //
  690. // Sometimes we want to talk about the full path (SessionInfo\BlahBlah)
  691. // and sometimes just the partial path (BlahBlah) so we wrap it inside
  692. // this goofy structure.
  693. //
  694. union SESSIONKEYNAME {
  695. TCHAR szPath[12+16+1];
  696. struct {
  697. TCHAR szSessionInfo[12]; // strlen("SessionInfo\\")
  698. TCHAR szName[16+1]; // 16 = two DWORDs converted to hex
  699. };
  700. } s_SessionKeyName = {
  701. { TEXT("SessionInfo\\.Default") }
  702. };
  703. BOOL g_fHaveSessionKeyName = FALSE;
  704. //
  705. // samDesired = a registry security access mask, or the special value
  706. // 0xFFFFFFFF to delete the session key.
  707. // phk = receives the session key on success
  708. //
  709. // NOTE! Only Explorer should delete the session key (when the user
  710. // logs off).
  711. //
  712. STDAPI SHCreateSessionKey(REGSAM samDesired, HKEY *phk)
  713. {
  714. LONG lRes;
  715. *phk = NULL;
  716. if (!g_fHaveSessionKeyName)
  717. {
  718. ENTERCRITICAL;
  719. //
  720. // Build the name of the session key. We use the authentication ID
  721. // which is guaranteed to be unique forever. We can't use the
  722. // Hydra session ID since that can be recycled.
  723. //
  724. // Note: Do not use OpenThreadToken since it will fail if the
  725. // thread is not impersonating. People who do impersonation
  726. // cannot use SHCreateSessionKey anyway since we cache the
  727. // session key on the assumption that there is no impersonation
  728. // going on. (And besides, HKCU is wrong when impersonating.)
  729. //
  730. HANDLE hToken;
  731. if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
  732. {
  733. TOKEN_STATISTICS stats;
  734. DWORD cbOut;
  735. if (GetTokenInformation(hToken, TokenStatistics, &stats, sizeof(stats), &cbOut))
  736. {
  737. StringCchPrintf(s_SessionKeyName.szName, ARRAYSIZE(s_SessionKeyName.szName), // ok to truncate
  738. TEXT("%08x%08x"),
  739. stats.AuthenticationId.HighPart,
  740. stats.AuthenticationId.LowPart); // should never truncate
  741. g_fHaveSessionKeyName = TRUE;
  742. }
  743. CloseHandle(hToken);
  744. }
  745. LEAVECRITICAL;
  746. }
  747. HKEY hkExplorer = SHGetShellKey(SHELLKEY_HKCU_EXPLORER, NULL, TRUE);
  748. if (hkExplorer)
  749. {
  750. if (samDesired != 0xFFFFFFFF)
  751. {
  752. DWORD dwDisposition;
  753. lRes = RegCreateKeyEx(hkExplorer, s_SessionKeyName.szPath, 0,
  754. NULL,
  755. REG_OPTION_VOLATILE,
  756. samDesired,
  757. NULL,
  758. phk,
  759. &dwDisposition);
  760. }
  761. else
  762. {
  763. lRes = SHDeleteKey(hkExplorer, s_SessionKeyName.szPath);
  764. }
  765. RegCloseKey(hkExplorer);
  766. }
  767. else
  768. {
  769. lRes = ERROR_ACCESS_DENIED;
  770. }
  771. return HRESULT_FROM_WIN32(lRes);
  772. }
  773. //
  774. // We use a HDCA to store the CLSIDs for the regitems in this folder, this call returns
  775. // that HDCA>
  776. //
  777. HDCA CRegFolder::_ItemArray()
  778. {
  779. HDCA hdca = DCA_Create();
  780. if (hdca)
  781. {
  782. for (int i = 0; i < _nRequiredItems; i++)
  783. {
  784. DCA_AddItem(hdca, *_aReqItems[i].pclsid);
  785. }
  786. DCA_AddItemsFromKey(hdca, HKEY_LOCAL_MACHINE, _pszRegKey);
  787. DCA_AddItemsFromKey(hdca, HKEY_CURRENT_USER, _pszRegKey);
  788. if (_pszSesKey)
  789. {
  790. HKEY hkSession;
  791. if (SUCCEEDED(SHCreateSessionKey(KEY_QUERY_VALUE, &hkSession)))
  792. {
  793. DCA_AddItemsFromKey(hdca, hkSession, _pszSesKey);
  794. RegCloseKey(hkSession);
  795. }
  796. }
  797. }
  798. return hdca;
  799. }
  800. HDCA CRegFolder::_DelItemArray()
  801. {
  802. HDCA hdca = DCA_Create();
  803. if (hdca)
  804. {
  805. TCHAR szKey[MAX_PATH*2];
  806. HRESULT hr = StringCchPrintf(szKey, ARRAYSIZE(szKey), TEXT("%s\\DelegateFolders"), _pszRegKey);
  807. if (SUCCEEDED(hr))
  808. {
  809. DCA_AddItemsFromKey(hdca, HKEY_LOCAL_MACHINE, szKey);
  810. DCA_AddItemsFromKey(hdca, HKEY_CURRENT_USER, szKey);
  811. }
  812. if (_pszSesKey)
  813. {
  814. HKEY hkSession;
  815. if (SUCCEEDED(SHCreateSessionKey(KEY_QUERY_VALUE, &hkSession)))
  816. {
  817. hr = StringCchPrintf(szKey, ARRAYSIZE(szKey), TEXT("%s\\DelegateFolders"), _pszSesKey);
  818. if (SUCCEEDED(hr))
  819. {
  820. DCA_AddItemsFromKey(hdca, hkSession, szKey);
  821. }
  822. RegCloseKey(hkSession);
  823. }
  824. }
  825. }
  826. return hdca;
  827. }
  828. //
  829. // Given our cached machine name, attempt get the object on that machine.
  830. //
  831. HRESULT CRegFolder::_InitFromMachine(IUnknown *punk, BOOL bEnum)
  832. {
  833. HRESULT hr = S_OK;
  834. if (_pszMachine)
  835. {
  836. // prior to Win2K there was IRemoteComputerA/W, we removed IRemoteComputerA and
  837. // made IRemoteComputer map to the W version of the API, therefore we
  838. // thunk the string to its wide version before calling the Initialize method. (daviddv 102099)
  839. IRemoteComputer * premc;
  840. hr = punk->QueryInterface(IID_PPV_ARG(IRemoteComputer, &premc));
  841. if (SUCCEEDED(hr))
  842. {
  843. WCHAR wszName[MAX_PATH];
  844. SHTCharToUnicode(_pszMachine, wszName, ARRAYSIZE(wszName));
  845. hr = premc->Initialize(wszName, bEnum);
  846. premc->Release();
  847. }
  848. }
  849. return hr;
  850. }
  851. //
  852. // Given a pidl, lets get an instance of the namespace that provides it.
  853. // - handles caching accordingly
  854. //
  855. HRESULT CRegFolder::_CreateAndInit(LPCIDLREGITEM pidlr, LPBC pbc, REFIID riid, void **ppv)
  856. {
  857. *ppv = NULL;
  858. HRESULT hr = E_FAIL;
  859. PDELEGATEITEMID pidld = _IsDelegate(pidlr);
  860. if (pidld)
  861. {
  862. IShellFolder *psf;
  863. hr = _GetDelegateFolder(pidld, IID_PPV_ARG(IShellFolder, &psf));
  864. if (SUCCEEDED(hr))
  865. {
  866. hr = psf->BindToObject((LPCITEMIDLIST)pidlr, pbc, riid, ppv);
  867. psf->Release();
  868. }
  869. }
  870. else
  871. {
  872. CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment
  873. // try using the cached pointer
  874. IPersistFreeThreadedObject *pfto = ExchangeFTO(&_pftoReg, NULL);
  875. if (pfto)
  876. {
  877. CLSID clsidT;
  878. if (SUCCEEDED(pfto->GetClassID(&clsidT)) && IsEqualGUID(clsidT, clsid))
  879. {
  880. // if this fails, ppv will still be NULL
  881. // so we will create a new cache item...
  882. hr = pfto->QueryInterface(riid, ppv);
  883. }
  884. }
  885. // cache failed, cocreate it ourself
  886. if (NULL == *ppv)
  887. {
  888. OBJCOMPATFLAGS ocf = SHGetObjectCompatFlags(NULL, &clsid);
  889. if (!(OBJCOMPATF_UNBINDABLE & ocf))
  890. {
  891. //
  892. // HACKHACK - some regitems can only be CoCreated with IID_IShellFolder
  893. // specifically the hummingbird shellext will DebugBreak() bringing
  894. // down the shell... but we can CoCreate() and then QI after...
  895. //
  896. hr = SHExtCoCreateInstance(NULL, &clsid, NULL,
  897. (OBJCOMPATF_COCREATESHELLFOLDERONLY & ocf) ? IID_IShellFolder : riid , ppv);
  898. if (SUCCEEDED(hr))
  899. {
  900. IUnknown *punk = (IUnknown *)*ppv; // avoid casts below
  901. if ((OBJCOMPATF_COCREATESHELLFOLDERONLY & ocf))
  902. {
  903. hr = punk->QueryInterface(riid, ppv);
  904. punk->Release();
  905. punk = (IUnknown *)*ppv; // avoid casts below
  906. }
  907. if (SUCCEEDED(hr))
  908. {
  909. hr = _InitFromMachine(punk, FALSE);
  910. if (SUCCEEDED(hr))
  911. {
  912. IPersistFolder *ppf;
  913. if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf))))
  914. {
  915. LPITEMIDLIST pidlAbs = ILCombine(_GetFolderIDList(), (LPCITEMIDLIST)pidlr);
  916. if (pidlAbs)
  917. {
  918. hr = ppf->Initialize(pidlAbs);
  919. ILFree(pidlAbs);
  920. }
  921. else
  922. {
  923. hr = E_OUTOFMEMORY;
  924. }
  925. ppf->Release();
  926. }
  927. if (SUCCEEDED(hr))
  928. {
  929. if (pfto)
  930. {
  931. pfto->Release(); // we are going to replace the cache
  932. pfto = NULL;
  933. }
  934. if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IPersistFreeThreadedObject, &pfto))))
  935. {
  936. SHPinDllOfCLSID(&clsid);
  937. }
  938. }
  939. }
  940. if (FAILED(hr))
  941. {
  942. // we're going to return failure -- don't leak the object we created
  943. punk->Release();
  944. *ppv = NULL;
  945. }
  946. }
  947. }
  948. }
  949. }
  950. // recache the pfto
  951. if (pfto)
  952. {
  953. pfto = ExchangeFTO(&_pftoReg, pfto);
  954. if (pfto)
  955. pfto->Release(); // protect against race condition or re-entrancy
  956. }
  957. }
  958. return hr;
  959. }
  960. //
  961. // lets the reg item itself pick off the GetDisplayNameOf() impl for itself. this lets
  962. // MyDocs on the desktop return c:\win\profile\name\My Documents as it's parsing name
  963. //
  964. // returns:
  965. // S_FALSE do normal parsing, reg item did not handel
  966. //
  967. //
  968. HRESULT CRegFolder::_GetDisplayNameFromSelf(LPCIDLREGITEM pidlr, DWORD dwFlags, LPTSTR pszName, UINT cchName)
  969. {
  970. HRESULT hr = S_FALSE; // normal case
  971. const CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment
  972. BOOL bGetFromSelf = FALSE;
  973. const BOOL bForParsingOnly = ((dwFlags & (SHGDN_FORADDRESSBAR | SHGDN_INFOLDER | SHGDN_FORPARSING)) == SHGDN_FORPARSING);
  974. if (bForParsingOnly)
  975. {
  976. if (SHQueryShellFolderValue(&clsid, TEXT("WantsFORPARSING")))
  977. {
  978. bGetFromSelf = TRUE;
  979. }
  980. }
  981. else
  982. {
  983. const BOOL bForParsing = (0 != (dwFlags & SHGDN_FORPARSING));
  984. const BOOL bForAddressBar = (0 != (dwFlags & SHGDN_FORADDRESSBAR));
  985. if (!bForParsing || bForAddressBar)
  986. {
  987. if (SHQueryShellFolderValue(&clsid, TEXT("WantsFORDISPLAY")))
  988. {
  989. bGetFromSelf = TRUE;
  990. }
  991. }
  992. }
  993. if (bGetFromSelf)
  994. {
  995. IShellFolder *psf;
  996. if (SUCCEEDED(_BindToItem(pidlr, NULL, IID_PPV_ARG(IShellFolder, &psf), TRUE)))
  997. {
  998. //
  999. // Pass NULL pidl (c_idlDesktop) to get display name of the folder itself.
  1000. // Note that we can't use DisplayNameOf() because that function
  1001. // eats the S_FALSE that can be returned from the
  1002. // IShellFolder::GetDisplayNameOf implementation to indicate
  1003. // "do the default thing".
  1004. //
  1005. STRRET sr;
  1006. hr = psf->GetDisplayNameOf(&c_idlDesktop, dwFlags, &sr);
  1007. if (S_OK == hr)
  1008. {
  1009. hr = StrRetToBuf(&sr, &c_idlDesktop, pszName, cchName);
  1010. }
  1011. psf->Release();
  1012. }
  1013. }
  1014. return hr;
  1015. }
  1016. //
  1017. // Managing the name cache is tricky because it is hit frequently on
  1018. // multiple threads, and collisions are frequent. E.g., one thread
  1019. // does a SetNameOf+SHChangeNotify, and multiple other threads try
  1020. // to pull the name out simultaneously. If you're not careful, these
  1021. // threads step on each other and some poor schmuck gets invalid data.
  1022. //
  1023. // _lNameCacheInterlock = -1 if nobody is using the name cache, else >= 0
  1024. //
  1025. // Therefore, if InterlockedIncrement(&_lNameCacheInterlock) == 0, then
  1026. // you are the sole owner of the cache. Otherwise, the cache is busy
  1027. // and you should get out.
  1028. //
  1029. // Furthermore, we don't use the name cache if it is more than 500ms stale.
  1030. //
  1031. BOOL CRegFolder::_GetNameFromCache(REFCLSID rclsid, DWORD dwFlags, LPTSTR pszName, UINT cchName)
  1032. {
  1033. BOOL fSuccess = FALSE;
  1034. // Quick check to avoid entering the interlock unnecessarily
  1035. if (rclsid.Data1 == _clsidNameCache.Data1 &&
  1036. GetTickCount() - _dwNameCacheTime < 500)
  1037. {
  1038. if (InterlockedIncrement(&_lNameCacheInterlock) == 0)
  1039. {
  1040. if (IsEqualGUID(rclsid, _clsidNameCache) &&
  1041. (_dwFlagsNameCache == dwFlags))
  1042. {
  1043. StringCchCopy(pszName, cchName, _szNameCache); // ok to truncate
  1044. fSuccess = TRUE;
  1045. }
  1046. }
  1047. InterlockedDecrement(&_lNameCacheInterlock);
  1048. }
  1049. return fSuccess;
  1050. }
  1051. void CRegFolder::_ClearNameFromCache()
  1052. {
  1053. _clsidNameCache = CLSID_NULL;
  1054. }
  1055. void CRegFolder::_SaveNameInCache(REFCLSID rclsid, DWORD dwFlags, LPTSTR pszName)
  1056. {
  1057. if (lstrlen(pszName) < ARRAYSIZE(_szNameCache))
  1058. {
  1059. if (InterlockedIncrement(&_lNameCacheInterlock) == 0)
  1060. {
  1061. StringCchCopy(_szNameCache, ARRAYSIZE(_szNameCache), pszName); // ok to truncate
  1062. _dwFlagsNameCache = dwFlags;
  1063. _clsidNameCache = rclsid;
  1064. _dwNameCacheTime = GetTickCount();
  1065. }
  1066. InterlockedDecrement(&_lNameCacheInterlock);
  1067. }
  1068. }
  1069. //
  1070. // Given a pidl in the regitms folder, get the friendly name for this (trying the user
  1071. // store ones, then the global one).
  1072. //
  1073. #define GUIDSIZE 50
  1074. HRESULT CRegFolder::_GetDisplayName(LPCIDLREGITEM pidlr, DWORD dwFlags, LPTSTR pszName, UINT cchName)
  1075. {
  1076. *pszName = 0;
  1077. PDELEGATEITEMID pidld = _IsDelegate(pidlr);
  1078. if (pidld)
  1079. {
  1080. IShellFolder *psf;
  1081. HRESULT hr = _GetDelegateFolder(pidld, IID_PPV_ARG(IShellFolder, &psf));
  1082. if (SUCCEEDED(hr))
  1083. {
  1084. hr = DisplayNameOf(psf, (LPCITEMIDLIST)pidlr, dwFlags, pszName, cchName);
  1085. psf->Release();
  1086. }
  1087. return hr;
  1088. }
  1089. else
  1090. {
  1091. HKEY hkCLSID;
  1092. CLSID clsid = _GetPIDLRCLSID(pidlr);
  1093. if (_GetNameFromCache(clsid, dwFlags, pszName, cchName))
  1094. {
  1095. // Satisfied from cache; all done!
  1096. }
  1097. else
  1098. {
  1099. HRESULT hr = _GetDisplayNameFromSelf(pidlr, dwFlags, pszName, cchName);
  1100. if (hr != S_FALSE)
  1101. return hr;
  1102. if (dwFlags & SHGDN_FORPARSING)
  1103. {
  1104. if (!(dwFlags & SHGDN_FORADDRESSBAR))
  1105. {
  1106. // Get the parent folder name
  1107. TCHAR szParentName[MAX_PATH];
  1108. szParentName[0] = 0;
  1109. if (!(dwFlags & SHGDN_INFOLDER) && !ILIsEmpty(_GetFolderIDList()))
  1110. {
  1111. SHGetNameAndFlags(_GetFolderIDList(), SHGDN_FORPARSING, szParentName, SIZECHARS(szParentName), NULL);
  1112. hr = StringCchCat(szParentName, ARRAYSIZE(szParentName), TEXT("\\"));
  1113. if (FAILED(hr))
  1114. {
  1115. return hr;
  1116. }
  1117. }
  1118. // Win95 didn't support SHGDN_FORPARSING on regitems; it always
  1119. // returned the display name. Norton Unerase relies on this,
  1120. // because it assumes that if the second character of the FORPARSING
  1121. // name is a colon, then it's a drive. Therefore, we can't return
  1122. // ::{guid} or Norton will fault. (I guess they didn't believe in
  1123. // SFGAO_FILESYSTEM.) So if we are Norton Unerase, then ignore
  1124. // the FORPARSING flag; always get the name for display.
  1125. // And good luck to any user who sets his computer name to something
  1126. // with a colon as the second character...
  1127. if (SHGetAppCompatFlags(ACF_OLDREGITEMGDN) & ACF_OLDREGITEMGDN)
  1128. {
  1129. // In Application compatibility mode turn SHGDN_FORPARSING
  1130. // off and fall thru to the remainder of the function which
  1131. // avoids the ::{GUID} when required.
  1132. dwFlags &= ~SHGDN_FORPARSING;
  1133. }
  1134. else
  1135. {
  1136. // Get this reg folder name
  1137. TCHAR szFolderName[GUIDSIZE + 2];
  1138. szFolderName[0] = szFolderName[1] = _chRegItem;
  1139. SHStringFromGUID(clsid, szFolderName + 2, cchName - 2);
  1140. // Copy the full path into szParentName.
  1141. hr = StringCchCat(szParentName, ARRAYSIZE(szParentName), szFolderName);
  1142. if (FAILED(hr))
  1143. {
  1144. return hr;
  1145. }
  1146. // Copy the full path into the output buffer.
  1147. return StringCchCopy(pszName, cchName, szParentName);
  1148. }
  1149. }
  1150. }
  1151. // Check per-user settings first...
  1152. if ((*pszName == 0) && SUCCEEDED(SHRegGetCLSIDKey(clsid, NULL, TRUE, FALSE, &hkCLSID)))
  1153. {
  1154. DWORD cbName = cchName * sizeof(TCHAR);
  1155. if (ERROR_SUCCESS != SHRegGetValue(hkCLSID, NULL, NULL, SRRF_RT_REG_SZ, NULL, pszName, &cbName))
  1156. *pszName = 0;
  1157. RegCloseKey(hkCLSID);
  1158. }
  1159. // If we have to, use per-machine settings...
  1160. if (*pszName == 0)
  1161. {
  1162. _GetClassKeys(pidlr, &hkCLSID, NULL);
  1163. if (hkCLSID)
  1164. {
  1165. SHLoadLegacyRegUIString(hkCLSID, NULL, pszName, cchName);
  1166. RegCloseKey(hkCLSID);
  1167. }
  1168. }
  1169. // try the required item names, they might not be in the registry
  1170. if (*pszName == 0)
  1171. {
  1172. int iItem = _ReqItemIndex(pidlr);
  1173. if (iItem >= 0)
  1174. LoadString(HINST_THISDLL, _aReqItems[iItem].uNameID, pszName, cchName);
  1175. }
  1176. if (*pszName)
  1177. {
  1178. if (_pszMachine && !(dwFlags & SHGDN_INFOLDER))
  1179. {
  1180. // szName now holds the item name, and _pszMachine holds the machine.
  1181. LPTSTR pszRet = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(IDS_DSPTEMPLATE_WITH_ON),
  1182. SkipServerSlashes(_pszMachine), pszName);
  1183. if (pszRet)
  1184. {
  1185. StringCchCopy(pszName, cchName, pszRet); // ok to truncate
  1186. LocalFree(pszRet);
  1187. }
  1188. }
  1189. _SaveNameInCache(clsid, dwFlags, pszName);
  1190. }
  1191. }
  1192. }
  1193. return *pszName ? S_OK : E_FAIL;
  1194. }
  1195. //
  1196. // get the HKEYs that map to the regitm.
  1197. //
  1198. // NOTE: this function returns a void so that the caller explicitly must check the keys
  1199. // to see if they are non-null before using them.
  1200. //
  1201. void CRegFolder::_GetClassKeys(LPCIDLREGITEM pidlr, HKEY* phkCLSID, HKEY* phkBase)
  1202. {
  1203. HRESULT hr;
  1204. IQueryAssociations *pqa;
  1205. if (phkCLSID)
  1206. *phkCLSID = NULL;
  1207. if (phkBase)
  1208. *phkBase = NULL;
  1209. hr = _AssocCreate(pidlr, IID_PPV_ARG(IQueryAssociations, &pqa));
  1210. if (SUCCEEDED(hr))
  1211. {
  1212. if (phkCLSID)
  1213. {
  1214. hr = pqa->GetKey(0, ASSOCKEY_CLASS, NULL, phkCLSID);
  1215. ASSERT((SUCCEEDED(hr) && *phkCLSID) || (FAILED(hr) && (*phkCLSID == NULL)));
  1216. }
  1217. if (phkBase)
  1218. {
  1219. hr = pqa->GetKey(0, ASSOCKEY_BASECLASS, NULL, phkBase);
  1220. ASSERT((SUCCEEDED(hr) && *phkBase) || (FAILED(hr) && (*phkBase == NULL)));
  1221. }
  1222. pqa->Release();
  1223. }
  1224. }
  1225. // {9EAC43C0-53EC-11CE-8230-CA8A32CF5494}
  1226. static const GUID GUID_WINAMP = { 0x9eac43c0, 0x53ec, 0x11ce, { 0x82, 0x30, 0xca, 0x8a, 0x32, 0xcf, 0x54, 0x94 } };
  1227. #define SZ_BROKEN_WINAMP_VERB TEXT("OpenFileOrPlayList")
  1228. // IQA - Move this to Legacy Mapper.
  1229. void _MaybeDoWinAmpHack(UNALIGNED REFGUID rguid)
  1230. {
  1231. if (IsEqualGUID(rguid, GUID_WINAMP))
  1232. {
  1233. // WinAmp writes in "OpenFileOrPlayList" as default value under shell, but they
  1234. // don't write a corresponding "OpenFileorPlayList" verb key. So we need to whack
  1235. // the registry into shape for them. Otherwise, they won't get the default verb
  1236. // they want (due to an NT5 change in CDefExt_QueryContextMenu's behavior).
  1237. TCHAR szCLSID[GUIDSTR_MAX];
  1238. SHStringFromGUID(rguid, szCLSID, ARRAYSIZE(szCLSID));
  1239. TCHAR szRegKey[GUIDSTR_MAX + 40];
  1240. HRESULT hr;
  1241. hr = StringCchPrintf(szRegKey, ARRAYSIZE(szRegKey), TEXT("CLSID\\%s\\shell"), szCLSID);
  1242. if (SUCCEEDED(hr))
  1243. {
  1244. TCHAR szValue[ARRAYSIZE(SZ_BROKEN_WINAMP_VERB)+2];
  1245. DWORD dwType;
  1246. DWORD dwSize = sizeof(szValue);
  1247. if (SHGetValue(HKEY_CLASSES_ROOT, szRegKey, NULL, &dwType, szValue, &dwSize) == 0)
  1248. {
  1249. if (dwType == REG_SZ && lstrcmp(szValue, SZ_BROKEN_WINAMP_VERB) == 0)
  1250. {
  1251. // Make "open" the default verb
  1252. SHSetValue(HKEY_CLASSES_ROOT, szRegKey, NULL, REG_SZ, TEXT("open"), sizeof(TEXT("open")));
  1253. }
  1254. }
  1255. }
  1256. }
  1257. }
  1258. HRESULT CRegFolder::_AssocCreate(LPCIDLREGITEM pidlr, REFIID riid, void **ppv)
  1259. {
  1260. *ppv = NULL;
  1261. IQueryAssociations *pqa;
  1262. HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
  1263. if (SUCCEEDED(hr))
  1264. {
  1265. WCHAR szCLSID[GUIDSTR_MAX];
  1266. const CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment
  1267. ASSOCF flags = ASSOCF_INIT_NOREMAPCLSID;
  1268. DWORD dwAttributes;
  1269. if ((SUCCEEDED(_AttributesOf(pidlr, SFGAO_FOLDER, &dwAttributes)) &&
  1270. (dwAttributes & SFGAO_FOLDER)) &&
  1271. !SHQueryShellFolderValue(&clsid, TEXT("HideFolderVerbs")))
  1272. flags |= ASSOCF_INIT_DEFAULTTOFOLDER;
  1273. SHStringFromGUIDW(clsid, szCLSID, ARRAYSIZE(szCLSID));
  1274. _MaybeDoWinAmpHack(clsid);
  1275. hr = pqa->Init(flags, szCLSID, NULL, NULL);
  1276. if (SUCCEEDED(hr))
  1277. hr = pqa->QueryInterface(riid, ppv);
  1278. pqa->Release();
  1279. }
  1280. return hr;
  1281. }
  1282. //
  1283. // get the namespace key for this objec.
  1284. //
  1285. void CRegFolder::_GetNameSpaceKey(LPCIDLREGITEM pidlr, LPTSTR pszKeyName, UINT cchKeyName)
  1286. {
  1287. TCHAR szClass[GUIDSTR_MAX];
  1288. SHStringFromGUID(_GetPIDLRCLSID(pidlr), szClass, ARRAYSIZE(szClass));
  1289. StringCchPrintf(pszKeyName, cchKeyName, TEXT("%s\\%s"), _pszRegKey, szClass); // ok to truncate
  1290. }
  1291. BOOL CRegFolder::_CanDelete(LPCIDLREGITEM pidlr)
  1292. {
  1293. DWORD dwAttributes;
  1294. return pidlr &&
  1295. SUCCEEDED(_AttributesOf(pidlr, SFGAO_CANDELETE, &dwAttributes)) &&
  1296. (dwAttributes & SFGAO_CANDELETE);
  1297. }
  1298. //
  1299. // the user is trying to delete an object from a regitem folder, therefore
  1300. // lets look in the IDataObject to see if that includes any regitems, if
  1301. // so then handle their deletion, before passing to the outer guy to
  1302. // handle the other objects.
  1303. //
  1304. #define MAX_REGITEM_WARNTEXT 1024
  1305. void CRegFolder::_Delete(HWND hwnd, UINT uFlags, IDataObject *pdtobj)
  1306. {
  1307. STGMEDIUM medium;
  1308. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  1309. if (pida)
  1310. {
  1311. TCHAR szItemWarning[MAX_REGITEM_WARNTEXT];
  1312. UINT nregfirst = (UINT)-1;
  1313. UINT creg = 0;
  1314. UINT cwarn = 0;
  1315. UINT countfs = 0;
  1316. LPCITEMIDLIST *ppidlFS = NULL;
  1317. // calc number of regitems and index of first
  1318. for (UINT i = 0; i < pida->cidl; i++)
  1319. {
  1320. LPCITEMIDLIST pidl = IDA_GetIDListPtr(pida, i);
  1321. LPCIDLREGITEM pidlr = _IsReg(pidl);
  1322. if (_CanDelete(pidlr))
  1323. {
  1324. TCHAR szTemp[MAX_REGITEM_WARNTEXT];
  1325. creg++;
  1326. if (nregfirst == (UINT)-1)
  1327. nregfirst = i;
  1328. // use a temporary here because _GetDeleteMessage clobbers the buffer
  1329. // when it doesn't get a delete message --ccooney
  1330. if ((cwarn < 2) && _GetDeleteMessage(pidlr, szTemp, ARRAYSIZE(szTemp)))
  1331. {
  1332. StringCchCopy(szItemWarning, ARRAYSIZE(szItemWarning), szTemp); // ok to truncate
  1333. cwarn++;
  1334. }
  1335. }
  1336. else if (!pidlr) //only do this for non reg items
  1337. {
  1338. // alloc an alternate array for FS pidls
  1339. // for simplicitu over alloc in the case where there are reg items
  1340. if (ppidlFS == NULL)
  1341. ppidlFS = (LPCITEMIDLIST *)LocalAlloc(LPTR, pida->cidl * sizeof(LPCITEMIDLIST));
  1342. if (ppidlFS)
  1343. {
  1344. ppidlFS[countfs++] = pidl;
  1345. }
  1346. }
  1347. }
  1348. //
  1349. // compose the confirmation message / ask the user / fry the items...
  1350. //
  1351. if (creg)
  1352. {
  1353. SHELLSTATE ss = {0};
  1354. SHGetSetSettings(&ss, SSF_NOCONFIRMRECYCLE, FALSE);
  1355. if ((uFlags & CMIC_MASK_FLAG_NO_UI) || ss.fNoConfirmRecycle)
  1356. {
  1357. for (i = 0; i < pida->cidl; i++)
  1358. {
  1359. LPCIDLREGITEM pidlr = _IsReg(IDA_GetIDListPtr(pida, i));
  1360. if (_CanDelete(pidlr))
  1361. _DeleteRegItem(pidlr);
  1362. }
  1363. }
  1364. else
  1365. {
  1366. TCHAR szItemName[MAX_PATH];
  1367. TCHAR szWarnText[1024 + MAX_REGITEM_WARNTEXT];
  1368. TCHAR szWarnCaption[128];
  1369. TCHAR szTemp[256];
  1370. MSGBOXPARAMS mbp = {sizeof(mbp), hwnd,
  1371. HINST_THISDLL, szWarnText, szWarnCaption,
  1372. MB_YESNO | MB_USERICON, MAKEINTRESOURCE(IDI_NUKEFILE),
  1373. 0, NULL, 0};
  1374. //
  1375. // so we can tell if we got these later
  1376. //
  1377. *szItemName = 0;
  1378. *szWarnText = 0;
  1379. //
  1380. // if there is only one, retrieve its name
  1381. //
  1382. if (creg == 1)
  1383. {
  1384. LPCIDLREGITEM pidlr = _IsReg(IDA_GetIDListPtr(pida, nregfirst));
  1385. _GetDisplayName(pidlr, SHGDN_NORMAL, szItemName, ARRAYSIZE(szItemName));
  1386. }
  1387. //
  1388. // ask the question "are you sure..."
  1389. //
  1390. if ((pida->cidl == 1) && *szItemName)
  1391. {
  1392. TCHAR szTemp2[256];
  1393. LoadString(HINST_THISDLL, _IsDesktop() ? IDS_CONFIRMDELETEDESKTOPREGITEM : IDS_CONFIRMDELETEREGITEM, szTemp2, ARRAYSIZE(szTemp2));
  1394. StringCchPrintf(szTemp, ARRAYSIZE(szTemp), szTemp2, szItemName); // ok to truncate
  1395. }
  1396. else
  1397. {
  1398. LoadString(HINST_THISDLL, _IsDesktop() ? IDS_CONFIRMDELETEDESKTOPREGITEMS : IDS_CONFIRMDELETEREGITEMS, szTemp, ARRAYSIZE(szTemp));
  1399. }
  1400. StringCchCat(szWarnText, ARRAYSIZE(szWarnText), szTemp); // ok to truncate
  1401. //
  1402. // if there is exactly one special warning message and one item total, add it in
  1403. //
  1404. if (creg == 1 && cwarn == 1 && *szItemWarning)
  1405. {
  1406. StringCchCat(szWarnText, ARRAYSIZE(szWarnText), TEXT("\r\n\n")); // ok to truncate
  1407. StringCchCat(szWarnText, ARRAYSIZE(szWarnText), szItemWarning); // ok to truncate
  1408. }
  1409. else
  1410. {
  1411. if (creg == 1)
  1412. {
  1413. TCHAR szTemp2[256];
  1414. TCHAR szControlPanel[256];
  1415. LPCIDLREGITEM pidlr = _IsReg(IDA_GetIDListPtr(pida, nregfirst));
  1416. CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment
  1417. int idString = (1 == pida->cidl) ?
  1418. IDS_CANTRECYCLEREGITEMS_NAME :
  1419. IDS_CANTRECYCLEREGITEMS_INCL_NAME;
  1420. LoadString(HINST_THISDLL, idString, szTemp, ARRAYSIZE(szTemp));
  1421. if ((IsEqualCLSID(CLSID_NetworkPlaces, clsid)) ||
  1422. (IsEqualCLSID(CLSID_Internet, clsid)) ||
  1423. (IsEqualCLSID(CLSID_MyComputer, clsid)) ||
  1424. (IsEqualCLSID(CLSID_MyDocuments, clsid)))
  1425. {
  1426. LoadString(HINST_THISDLL, IDS_CANTRECYLE_FOLDER, szControlPanel, ARRAYSIZE(szControlPanel));
  1427. }
  1428. else
  1429. {
  1430. LoadString(HINST_THISDLL, IDS_CANTRECYLE_GENERAL, szControlPanel, ARRAYSIZE(szControlPanel));
  1431. }
  1432. StringCchCat(szWarnText, ARRAYSIZE(szWarnText), TEXT("\r\n\n")); // ok to truncate
  1433. StringCchPrintf(szTemp2, ARRAYSIZE(szTemp2), szTemp, szControlPanel); // ok to truncate
  1434. StringCchCat(szWarnText, ARRAYSIZE(szWarnText), szTemp2); // ok to truncate
  1435. }
  1436. //
  1437. // otherwise, say "these items..." or "some of these items..."
  1438. //
  1439. else
  1440. {
  1441. TCHAR szTemp2[256];
  1442. TCHAR szControlPanel[256];
  1443. int idString = (creg == pida->cidl) ? IDS_CANTRECYCLEREGITEMS_ALL : IDS_CANTRECYCLEREGITEMS_SOME;
  1444. LoadString(HINST_THISDLL, idString, szTemp, ARRAYSIZE(szTemp));
  1445. LoadString(HINST_THISDLL, IDS_CANTRECYLE_GENERAL, szControlPanel, ARRAYSIZE(szControlPanel));
  1446. StringCchCat(szWarnText, ARRAYSIZE(szWarnText), TEXT("\r\n\n")); // ok to truncate
  1447. StringCchPrintf(szTemp2, ARRAYSIZE(szTemp2), szTemp, szControlPanel); // ok to truncate
  1448. StringCchCat(szWarnText, ARRAYSIZE(szWarnText), szTemp2); // ok to truncate
  1449. //
  1450. // we just loaded a very vague message
  1451. // don't confuse the user any more by adding random text
  1452. // if these is a special warning, force it to show separately
  1453. //
  1454. if (cwarn == 1)
  1455. cwarn++;
  1456. }
  1457. }
  1458. //
  1459. // finally, the message box caption (also needed in loop below)
  1460. //
  1461. LoadString(HINST_THISDLL, IDS_CONFIRMDELETE_CAPTION, szWarnCaption, ARRAYSIZE(szWarnCaption));
  1462. // make sure the user is cool with it
  1463. if (MessageBoxIndirect(&mbp) == IDYES)
  1464. {
  1465. // go ahead and delete the reg items
  1466. for (i = 0; i < pida->cidl; i++)
  1467. {
  1468. LPCIDLREGITEM pidlr = _IsReg(IDA_GetIDListPtr(pida, i));
  1469. if (_CanDelete(pidlr))
  1470. {
  1471. if ((cwarn > 1) && _GetDeleteMessage(pidlr, szItemWarning, ARRAYSIZE(szItemWarning)))
  1472. {
  1473. if (FAILED(_GetDisplayName(pidlr, SHGDN_NORMAL, szItemName, ARRAYSIZE(szItemName))))
  1474. {
  1475. StringCchCopy(szItemName, ARRAYSIZE(szItemName), szWarnCaption); // ok to truncate
  1476. }
  1477. MessageBox(hwnd, szItemWarning, szItemName, MB_OK | MB_ICONINFORMATION);
  1478. }
  1479. _DeleteRegItem(pidlr);
  1480. }
  1481. }
  1482. }
  1483. }
  1484. }
  1485. // now delete the fs objects
  1486. if (ppidlFS)
  1487. {
  1488. SHInvokeCommandOnPidlArray(hwnd, NULL, (IShellFolder *)this, ppidlFS, countfs, uFlags, "delete");
  1489. LocalFree((HANDLE)ppidlFS);
  1490. }
  1491. HIDA_ReleaseStgMedium(pida, &medium);
  1492. }
  1493. }
  1494. //
  1495. // Delete a regitem given its pidl.
  1496. //
  1497. HRESULT CRegFolder::_DeleteRegItem(LPCIDLREGITEM pidlr)
  1498. {
  1499. if (_IsDelegate(pidlr))
  1500. return E_INVALIDARG;
  1501. HRESULT hr = E_ACCESSDENIED;
  1502. if (_CanDelete(pidlr))
  1503. {
  1504. const CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment
  1505. if (SHQueryShellFolderValue(&clsid, TEXT("HideOnDesktopPerUser")))
  1506. {
  1507. // hide this icon only on desktop for StartPanel on (0) and off (1)
  1508. hr = ShowHideIconOnlyOnDesktop(&clsid, 0, 1, TRUE);
  1509. }
  1510. else if (SHQueryShellFolderValue(&clsid, TEXT("HideAsDeletePerUser")))
  1511. {
  1512. // clear the non enuerated bit to hide this item
  1513. hr = _SetAttributes(pidlr, TRUE, SFGAO_NONENUMERATED, SFGAO_NONENUMERATED);
  1514. }
  1515. else if (SHQueryShellFolderValue(&clsid, TEXT("HideAsDelete")))
  1516. {
  1517. // clear the non enuerated bit to hide this item
  1518. hr = _SetAttributes(pidlr, FALSE, SFGAO_NONENUMERATED, SFGAO_NONENUMERATED);
  1519. }
  1520. else
  1521. {
  1522. // remove from the key to delete it
  1523. TCHAR szKeyName[MAX_PATH];
  1524. _GetNameSpaceKey(pidlr, szKeyName, ARRAYSIZE(szKeyName));
  1525. if ((RegDeleteKey(HKEY_CURRENT_USER, szKeyName) == ERROR_SUCCESS) ||
  1526. (RegDeleteKey(HKEY_LOCAL_MACHINE, szKeyName) == ERROR_SUCCESS))
  1527. {
  1528. hr = S_OK;
  1529. }
  1530. }
  1531. if (SUCCEEDED(hr))
  1532. {
  1533. // tell the world
  1534. LPITEMIDLIST pidlAbs = ILCombine(_GetFolderIDList(), (LPCITEMIDLIST)pidlr);
  1535. if (pidlAbs)
  1536. {
  1537. SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, pidlAbs, NULL);
  1538. ILFree(pidlAbs);
  1539. }
  1540. }
  1541. }
  1542. return hr;
  1543. }
  1544. //
  1545. // Get the prompt to be displayed if the user tries to delete the regitem,
  1546. // this is stored both globally (HKLM) and as s user configured preference.
  1547. //
  1548. BOOL CRegFolder::_GetDeleteMessage(LPCIDLREGITEM pidlr, LPTSTR pszMsg, int cchMax)
  1549. {
  1550. HKEY hk;
  1551. TCHAR szKeyName[MAX_PATH];
  1552. ASSERT(!_IsDelegate(pidlr));
  1553. *pszMsg = 0;
  1554. _GetNameSpaceKey(pidlr, szKeyName, ARRAYSIZE(szKeyName));
  1555. if ((RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, NULL, KEY_QUERY_VALUE, &hk) == ERROR_SUCCESS) ||
  1556. (RegOpenKeyEx(HKEY_CURRENT_USER, szKeyName, NULL, KEY_QUERY_VALUE, &hk) == ERROR_SUCCESS))
  1557. {
  1558. SHLoadRegUIString(hk, REGSTR_VAL_REGITEMDELETEMESSAGE, pszMsg, cchMax);
  1559. RegCloseKey(hk);
  1560. }
  1561. return *pszMsg != 0;
  1562. }
  1563. HRESULT CRegFolder::_GetRegItemColumnFromRegistry(LPCIDLREGITEM pidlr, LPCTSTR pszColumnName, LPTSTR pszColumnData, int cchColumnData)
  1564. {
  1565. HKEY hkCLSID;
  1566. HRESULT hr = E_FAIL;
  1567. _GetClassKeys(pidlr, &hkCLSID, NULL);
  1568. *pszColumnData = 0; // Default string
  1569. if (hkCLSID)
  1570. {
  1571. // Use SHLoadRegUIString to allow the string to be localized
  1572. if (SUCCEEDED(SHLoadRegUIString(hkCLSID, pszColumnName, pszColumnData, cchColumnData)))
  1573. {
  1574. hr = S_OK;
  1575. }
  1576. // FIXED kenwic 052699 #342955
  1577. RegCloseKey(hkCLSID);
  1578. }
  1579. return hr;
  1580. }
  1581. //
  1582. // A more generic version of _GetRegItemColumnFromRegistry which takes a pidlr and a string
  1583. // and finds the corresponding variant value from the registry.
  1584. //
  1585. // pidlr: pidl of the regitem, we open the registry key corresponding to its CLSID
  1586. // pszColumnName: name of the value to took for under the opened key
  1587. // pv: variant to return the value
  1588. //
  1589. HRESULT CRegFolder::_GetRegItemVariantFromRegistry(LPCIDLREGITEM pidlr, LPCTSTR pszColumnName, VARIANT *pv)
  1590. {
  1591. HKEY hkCLSID;
  1592. HRESULT hr = E_FAIL;
  1593. _GetClassKeys(pidlr, &hkCLSID, NULL);
  1594. if (hkCLSID)
  1595. {
  1596. hr = GetVariantFromRegistryValue(hkCLSID, pszColumnName, pv);
  1597. RegCloseKey(hkCLSID);
  1598. }
  1599. return hr;
  1600. }
  1601. //
  1602. // App compat: McAfee Nuts & Bolts Quick Copy has the wrong function
  1603. // signature for CreateViewObject. They implemented it as
  1604. //
  1605. // STDAPI CreateViewObject(HWND hwnd) { return S_OK; }
  1606. //
  1607. // so we must manually reset the stack after the call.
  1608. //
  1609. #ifdef _X86_
  1610. STDAPI SHAppCompatCreateViewObject(IShellFolder *psf, HWND hwnd, REFIID riid, void * *ppv)
  1611. {
  1612. HRESULT hr;
  1613. _asm mov edi, esp
  1614. hr = psf->CreateViewObject(hwnd, riid, ppv);
  1615. _asm mov esp, edi
  1616. // AppCompat - Undelete 2.0 returns S_OK for interfaces that it doesnt support
  1617. // but they do correctly NULL the ppv out param so we check for that as well
  1618. if (SUCCEEDED(hr) && !*ppv)
  1619. hr = E_NOINTERFACE;
  1620. return hr;
  1621. }
  1622. #else
  1623. #define SHAppCompatCreateViewObject(psf, hwnd, riid, ppv) \
  1624. psf->CreateViewObject(hwnd, riid, ppv)
  1625. #endif
  1626. HRESULT CRegFolder::_CreateViewObjectFor(LPCIDLREGITEM pidlr, HWND hwnd, REFIID riid, void **ppv, BOOL bOneLevel)
  1627. {
  1628. IShellFolder *psf;
  1629. HRESULT hr = _BindToItem(pidlr, NULL, IID_PPV_ARG(IShellFolder, &psf), bOneLevel);
  1630. if (SUCCEEDED(hr))
  1631. {
  1632. hr = SHAppCompatCreateViewObject(psf, hwnd, riid, ppv);
  1633. psf->Release();
  1634. }
  1635. else
  1636. *ppv = NULL;
  1637. return hr;
  1638. }
  1639. // Geta an infotip object for the namespace
  1640. HRESULT CRegFolder::_GetInfoTip(LPCIDLREGITEM pidlr, void **ppv)
  1641. {
  1642. HKEY hkCLSID;
  1643. HRESULT hr = E_FAIL;
  1644. _GetClassKeys(pidlr, &hkCLSID, NULL);
  1645. if (hkCLSID)
  1646. {
  1647. DWORD dwQuery, lLen = sizeof(dwQuery);
  1648. // let the regitem code compute the info tip if it wants to...
  1649. if (SHQueryValueEx(hkCLSID, TEXT("QueryForInfoTip"), NULL, NULL, (BYTE *)&dwQuery, &lLen) == ERROR_SUCCESS)
  1650. {
  1651. hr = _CreateViewObjectFor(pidlr, NULL, IID_IQueryInfo, ppv, TRUE);
  1652. }
  1653. else
  1654. {
  1655. hr = E_FAIL;
  1656. }
  1657. // fall back to reading it from the registry
  1658. if (FAILED(hr))
  1659. {
  1660. TCHAR szText[INFOTIPSIZE];
  1661. // Use SHLoadRegUIString to allow the info tip to be localized
  1662. if (SUCCEEDED(SHLoadRegUIString(hkCLSID, TEXT("InfoTip"), szText, ARRAYSIZE(szText))) &&
  1663. szText[0])
  1664. {
  1665. hr = CreateInfoTipFromText(szText, IID_IQueryInfo, ppv); //The InfoTip COM object
  1666. }
  1667. }
  1668. RegCloseKey(hkCLSID);
  1669. }
  1670. return hr;
  1671. }
  1672. // there are 2 forms for parsing syntax we support
  1673. //
  1674. // to parse reg items in this folder
  1675. // ::{clsid reg item} [\ optional extra stuff to parse]
  1676. //
  1677. // to parse items that might live in a delegate folder
  1678. // ::{clsid delegate folder},<delegate folder specific parse string> [\ optional extra stuff to parse]
  1679. //
  1680. // in both cases the optional remander stuff gets passed through to complete
  1681. // the parse in that name space
  1682. HRESULT CRegFolder::_ParseGUIDName(HWND hwnd, LPBC pbc, LPOLESTR pwzDisplayName,
  1683. LPITEMIDLIST *ppidlOut, ULONG *pdwAttributes)
  1684. {
  1685. LPOLESTR pwzNext;
  1686. LPOLESTR pwzDelegateInfo = NULL;
  1687. // Note that we add 2 to skip the RegItem identifier characters
  1688. pwzDisplayName += 2;
  1689. // Skip to a '\\'
  1690. for (pwzNext = pwzDisplayName; *pwzNext && *pwzNext != TEXT('\\'); pwzNext++)
  1691. {
  1692. // if we hit a ',' then eg, ::{GUID},stuff then we assume the stuff is for a delegate
  1693. if ((*pwzNext == TEXT(',')) && !pwzDelegateInfo)
  1694. {
  1695. pwzDelegateInfo = pwzNext + 1; // skip comma
  1696. }
  1697. }
  1698. CLSID clsid;
  1699. HRESULT hr = SHCLSIDFromString(pwzDisplayName, &clsid);
  1700. if (SUCCEEDED(hr))
  1701. {
  1702. if (pwzDelegateInfo)
  1703. {
  1704. IShellFolder *psf;
  1705. if (SUCCEEDED(_CreateDelegateFolder(&clsid, IID_PPV_ARG(IShellFolder, &psf))))
  1706. {
  1707. ULONG chEaten;
  1708. hr = psf->ParseDisplayName(hwnd, pbc, pwzDelegateInfo, &chEaten, ppidlOut, pdwAttributes);
  1709. psf->Release();
  1710. }
  1711. }
  1712. else
  1713. {
  1714. IDLREGITEM* pidlRegItem = _CreateAndFillIDLREGITEM(&clsid);
  1715. if (pidlRegItem)
  1716. {
  1717. if (_IsInNameSpace(pidlRegItem) || (BindCtx_GetMode(pbc, 0) & STGM_CREATE))
  1718. {
  1719. hr = _ParseNextLevel(hwnd, pbc, pidlRegItem, pwzNext, ppidlOut, pdwAttributes);
  1720. }
  1721. else
  1722. hr = E_INVALIDARG;
  1723. ILFree((LPITEMIDLIST)pidlRegItem);
  1724. }
  1725. else
  1726. {
  1727. hr = E_OUTOFMEMORY;
  1728. }
  1729. }
  1730. }
  1731. return hr;
  1732. }
  1733. //
  1734. // ask a (known) regitem to parse a displayname
  1735. //
  1736. HRESULT CRegFolder::_ParseThroughItem(LPCIDLREGITEM pidlr, HWND hwnd, LPBC pbc,
  1737. LPOLESTR pszName, ULONG *pchEaten,
  1738. LPITEMIDLIST *ppidlOut, ULONG *pdwAttributes)
  1739. {
  1740. IShellFolder *psfItem;
  1741. HRESULT hr = _BindToItem(pidlr, pbc, IID_PPV_ARG(IShellFolder, &psfItem), FALSE);
  1742. if (SUCCEEDED(hr))
  1743. {
  1744. LPITEMIDLIST pidlRight;
  1745. hr = psfItem->ParseDisplayName(hwnd, pbc, pszName, pchEaten,
  1746. &pidlRight, pdwAttributes);
  1747. if (SUCCEEDED(hr))
  1748. {
  1749. hr = SHILCombine((LPCITEMIDLIST)pidlr, pidlRight, ppidlOut);
  1750. ILFree(pidlRight);
  1751. }
  1752. psfItem->Release();
  1753. }
  1754. return hr;
  1755. }
  1756. //
  1757. // Parse through the GUID to the namespace below
  1758. //
  1759. HRESULT CRegFolder::_ParseNextLevel(HWND hwnd, LPBC pbc, LPCIDLREGITEM pidlr,
  1760. LPOLESTR pwzRest, LPITEMIDLIST *ppidlOut, ULONG *pdwAttributes)
  1761. {
  1762. if (!*pwzRest)
  1763. {
  1764. // base case for recursive calls
  1765. // pidlNext should be a simple pidl.
  1766. ASSERT(!ILIsEmpty((LPCITEMIDLIST)pidlr) && ILIsEmpty(_ILNext((LPCITEMIDLIST)pidlr)));
  1767. if (pdwAttributes && *pdwAttributes)
  1768. _AttributesOf(pidlr, *pdwAttributes, pdwAttributes);
  1769. return SHILClone((LPCITEMIDLIST)pidlr, ppidlOut);
  1770. }
  1771. ASSERT(*pwzRest == TEXT('\\'));
  1772. ++pwzRest;
  1773. IShellFolder *psfNext;
  1774. HRESULT hr = _BindToItem(pidlr, pbc, IID_PPV_ARG(IShellFolder, &psfNext), FALSE);
  1775. if (SUCCEEDED(hr))
  1776. {
  1777. ULONG chEaten;
  1778. LPITEMIDLIST pidlRest;
  1779. hr = psfNext->ParseDisplayName(hwnd, pbc, pwzRest, &chEaten, &pidlRest, pdwAttributes);
  1780. if (SUCCEEDED(hr))
  1781. {
  1782. hr = SHILCombine((LPCITEMIDLIST)pidlr, pidlRest, ppidlOut);
  1783. SHFree(pidlRest);
  1784. }
  1785. psfNext->Release();
  1786. }
  1787. return hr;
  1788. }
  1789. BOOL _FailForceReturn(HRESULT hr)
  1790. {
  1791. switch (hr)
  1792. {
  1793. case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
  1794. case HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND):
  1795. case HRESULT_FROM_WIN32(ERROR_BAD_NET_NAME):
  1796. case HRESULT_FROM_WIN32(ERROR_BAD_NETPATH):
  1797. case HRESULT_FROM_WIN32(ERROR_CANCELLED):
  1798. return TRUE;
  1799. }
  1800. return FALSE;
  1801. }
  1802. HRESULT CRegFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszName,
  1803. ULONG *pchEaten, LPITEMIDLIST *ppidlOut, ULONG *pdwAttributes)
  1804. {
  1805. HRESULT hr = E_INVALIDARG;
  1806. if (ppidlOut)
  1807. *ppidlOut = NULL;
  1808. if (ppidlOut && pszName)
  1809. {
  1810. // ::{guid} lets you get the pidl for a reg item
  1811. if (*pszName && (pszName[0] == _chRegItem) && (pszName[1] == _chRegItem))
  1812. {
  1813. hr = _ParseGUIDName(hwnd, pbc, pszName, ppidlOut, pdwAttributes);
  1814. }
  1815. else
  1816. {
  1817. // inner folder gets a chance to parse
  1818. hr = _psfOuter->ParseDisplayName(hwnd, pbc, pszName, pchEaten, ppidlOut, pdwAttributes);
  1819. if (FAILED(hr) &&
  1820. !_FailForceReturn(hr) &&
  1821. !SHSkipJunctionBinding(pbc, NULL))
  1822. {
  1823. // loop over all of the items
  1824. HDCA hdca = _ItemArray();
  1825. if (hdca)
  1826. {
  1827. HRESULT hrTemp = E_FAIL;
  1828. for (int i = 0; FAILED(hrTemp) && (i < DCA_GetItemCount(hdca)); i++)
  1829. {
  1830. const CLSID clsid = *DCA_GetItem(hdca, i);
  1831. if (!SHSkipJunction(pbc, &clsid)
  1832. && SHQueryShellFolderValue(&clsid, L"WantsParseDisplayName"))
  1833. {
  1834. IDLREGITEM* pidlRegItem = _CreateAndFillIDLREGITEM(DCA_GetItem(hdca, i));
  1835. if (pidlRegItem)
  1836. {
  1837. hrTemp = _ParseThroughItem(pidlRegItem, hwnd, pbc, pszName, pchEaten, ppidlOut, pdwAttributes);
  1838. }
  1839. ILFree((LPITEMIDLIST)pidlRegItem);
  1840. }
  1841. }
  1842. DCA_Destroy(hdca);
  1843. if (SUCCEEDED(hrTemp) || _FailForceReturn(hrTemp))
  1844. hr = hrTemp;
  1845. else
  1846. hr = E_INVALIDARG; // no one could handle it
  1847. }
  1848. else
  1849. {
  1850. hr = E_OUTOFMEMORY;
  1851. }
  1852. }
  1853. }
  1854. ASSERT(SUCCEEDED(hr) ? *ppidlOut != NULL : *ppidlOut == NULL);
  1855. }
  1856. if (FAILED(hr))
  1857. TraceMsg(TF_WARNING, "CRegFolder::ParseDisplayName(), hr:%x %hs", hr, pszName);
  1858. return hr;
  1859. }
  1860. HRESULT CRegFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenum)
  1861. {
  1862. *ppenum = NULL;
  1863. IEnumIDList *penumOuter;
  1864. HRESULT hr = _psfOuter->EnumObjects(hwnd, grfFlags, &penumOuter);
  1865. if (SUCCEEDED(hr))
  1866. {
  1867. // SUCCEEDED(hr) may be S_FALSE with penumOuter == NULL
  1868. // CRegFolderEnum deals with this just fine
  1869. CRegFolderEnum *preidl = new CRegFolderEnum(this, grfFlags, penumOuter,
  1870. _ItemArray(), _DelItemArray(),
  1871. _pPolicy);
  1872. if (preidl)
  1873. {
  1874. *ppenum = SAFECAST(preidl, IEnumIDList*);
  1875. hr = S_OK;
  1876. }
  1877. else
  1878. hr = E_OUTOFMEMORY;
  1879. if (penumOuter)
  1880. penumOuter->Release(); // _psfOuter returned S_FALSE
  1881. }
  1882. return hr;
  1883. }
  1884. // Handle binding to the inner namespace, or the regitem accordingly given its pidl.
  1885. HRESULT CRegFolder::_BindToItem(LPCIDLREGITEM pidlr, LPBC pbc, REFIID riid, void **ppv, BOOL bOneLevel)
  1886. {
  1887. LPITEMIDLIST pidlAlloc;
  1888. *ppv = NULL;
  1889. LPCITEMIDLIST pidlNext = _ILNext((LPCITEMIDLIST)pidlr);
  1890. if (ILIsEmpty(pidlNext))
  1891. {
  1892. pidlAlloc = NULL;
  1893. bOneLevel = TRUE; // we know for sure it is one level
  1894. }
  1895. else
  1896. {
  1897. pidlAlloc = ILCloneFirst((LPCITEMIDLIST)pidlr);
  1898. if (!pidlAlloc)
  1899. return E_OUTOFMEMORY;
  1900. pidlr = (LPCIDLREGITEM)pidlAlloc; // a single item IDLIST
  1901. }
  1902. HRESULT hr;
  1903. if (bOneLevel)
  1904. {
  1905. hr = _CreateAndInit(pidlr, pbc, riid, ppv); // create on riid to avoid loads on interfaces not supported
  1906. }
  1907. else
  1908. {
  1909. IShellFolder *psfNext;
  1910. hr = _CreateAndInit(pidlr, pbc, IID_PPV_ARG(IShellFolder, &psfNext));
  1911. if (SUCCEEDED(hr))
  1912. {
  1913. hr = psfNext->BindToObject(pidlNext, pbc, riid, ppv);
  1914. psfNext->Release();
  1915. }
  1916. }
  1917. if (pidlAlloc)
  1918. ILFree(pidlAlloc); // we allocated in this case
  1919. return hr;
  1920. }
  1921. HRESULT CRegFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  1922. {
  1923. HRESULT hr;
  1924. LPCIDLREGITEM pidlr = _IsReg(pidl);
  1925. if (pidlr)
  1926. hr = _BindToItem(pidlr, pbc, riid, ppv, FALSE);
  1927. else
  1928. hr = _psfOuter->BindToObject(pidl, pbc, riid, ppv);
  1929. return hr;
  1930. }
  1931. HRESULT CRegFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  1932. {
  1933. return BindToObject(pidl, pbc, riid, ppv);
  1934. }
  1935. // I can't believe there is no "^^"
  1936. #define LOGICALXOR(a, b) (((a) && !(b)) || (!(a) && (b)))
  1937. BOOL CRegFolder::_IsFolder(LPCITEMIDLIST pidl)
  1938. {
  1939. BOOL fRet = FALSE;
  1940. if (pidl)
  1941. {
  1942. ULONG uAttrib = SFGAO_FOLDER;
  1943. if (SUCCEEDED(GetAttributesOf(1, &pidl, &uAttrib)) && (SFGAO_FOLDER & uAttrib))
  1944. fRet = TRUE;
  1945. }
  1946. return fRet;
  1947. }
  1948. int CRegFolder::_CompareIDsOriginal(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1949. {
  1950. LPCIDLREGITEM pidlr1 = _IsReg(pidl1);
  1951. LPCIDLREGITEM pidlr2 = _IsReg(pidl2);
  1952. int iRes = 0;
  1953. if (pidlr1 && pidlr2)
  1954. {
  1955. iRes = memcmp(&(_GetPIDLRCLSID(pidlr1)), &(_GetPIDLRCLSID(pidlr2)), sizeof(CLSID));
  1956. if (0 == iRes)
  1957. {
  1958. // if they are the same clsid
  1959. // and delegates then we need to query
  1960. // the delegate for the compare
  1961. PDELEGATEITEMID pidld1 = _IsDelegate(pidlr1);
  1962. PDELEGATEITEMID pidld2 = _IsDelegate(pidlr2);
  1963. if (pidld1 && pidld2)
  1964. {
  1965. // these are both the same delegate
  1966. IShellFolder *psf;
  1967. if (SUCCEEDED(_GetDelegateFolder(pidld1, IID_PPV_ARG(IShellFolder, &psf))))
  1968. {
  1969. HRESULT hr = psf->CompareIDs(lParam, pidl1, pidl2);
  1970. psf->Release();
  1971. iRes = HRESULT_CODE(hr);
  1972. }
  1973. }
  1974. else
  1975. {
  1976. ASSERT(!pidld1 && !pidld2);
  1977. }
  1978. }
  1979. else if (!(SHCIDS_CANONICALONLY & lParam))
  1980. {
  1981. // sort by defined order
  1982. BYTE bOrder1 = _GetOrder(pidlr1);
  1983. BYTE bOrder2 = _GetOrder(pidlr2);
  1984. int iUI = bOrder1 - bOrder2;
  1985. if (0 == iUI)
  1986. {
  1987. // All of the required items come first, in reverse
  1988. // order (to make this simpler)
  1989. int iItem1 = _ReqItemIndex(pidlr1);
  1990. int iItem2 = _ReqItemIndex(pidlr2);
  1991. if (iItem1 == -1 && iItem2 == -1)
  1992. {
  1993. TCHAR szName1[MAX_PATH], szName2[MAX_PATH];
  1994. _GetDisplayName(pidlr1, SHGDN_NORMAL, szName1, ARRAYSIZE(szName1));
  1995. _GetDisplayName(pidlr2, SHGDN_NORMAL, szName2, ARRAYSIZE(szName2));
  1996. iUI = StrCmpLogicalRestricted(szName1, szName2);
  1997. }
  1998. else
  1999. {
  2000. iUI = iItem2 - iItem1;
  2001. }
  2002. }
  2003. if (iUI)
  2004. iRes = iUI;
  2005. }
  2006. }
  2007. return iRes;
  2008. }
  2009. // Alphabetical (doesn't care about folder, regitems, ...)
  2010. int CRegFolder::_CompareIDsAlphabetical(UINT iColumn, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  2011. {
  2012. int iRes = 0;
  2013. // Do we have only one ptr?
  2014. if (!LOGICALXOR(pidl1, pidl2))
  2015. {
  2016. // No, either we have two or none.
  2017. if (pidl1 && pidl2)
  2018. {
  2019. iRes = CompareIDsAlphabetical(SAFECAST(this, IShellFolder2*), iColumn, pidl1, pidl2);
  2020. }
  2021. // else iRes already = 0
  2022. }
  2023. else
  2024. {
  2025. // Yes, the one which is non-NULL is first
  2026. iRes = (pidl1 ? -1 : 1);
  2027. }
  2028. return iRes;
  2029. }
  2030. // Folders comes first, and are ordered in alphabetical order among themselves,
  2031. // then comes all the non-folders again sorted among thmeselves
  2032. int CRegFolder::_CompareIDsFolderFirst(UINT iColumn, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  2033. {
  2034. int iRes = 0;
  2035. BOOL fIsFolder1 = _IsFolder(pidl1);
  2036. BOOL fIsFolder2 = _IsFolder(pidl2);
  2037. // Is there one folder and one non-folder?
  2038. if (LOGICALXOR(fIsFolder1, fIsFolder2))
  2039. {
  2040. // Yes, the folder will be first
  2041. iRes = fIsFolder1 ? -1 : 1;
  2042. }
  2043. else
  2044. {
  2045. // No, either both are folders or both are not. One way or the other, go
  2046. // alphabetically
  2047. iRes = _CompareIDsAlphabetical(iColumn, pidl1, pidl2);
  2048. }
  2049. return iRes;
  2050. }
  2051. int CRegFolder::_GetOrderType(LPCITEMIDLIST pidl)
  2052. {
  2053. if (_IsReg(pidl))
  2054. {
  2055. if (_IsDelegate((LPCIDLREGITEM)pidl))
  2056. return REGORDERTYPE_DELEGATE;
  2057. else if (-1 == _ReqItemIndex((LPCIDLREGITEM)pidl))
  2058. return REGORDERTYPE_REGITEM;
  2059. else
  2060. return REGORDERTYPE_REQITEM;
  2061. }
  2062. return _iTypeOuter;
  2063. }
  2064. HRESULT CRegFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  2065. {
  2066. int iType1 = _GetOrderType(pidl1);
  2067. int iType2 = _GetOrderType(pidl2);
  2068. int iTypeCompare = iType1 - iType2;
  2069. int iRes = 0;
  2070. UINT iColumn = (UINT) (SHCIDS_COLUMNMASK & lParam);
  2071. // first we compare the first level only
  2072. switch (_dwSortAttrib)
  2073. {
  2074. case RIISA_ORIGINAL:
  2075. if (0 == iTypeCompare && iType1 == _iTypeOuter)
  2076. {
  2077. // neither are regitems
  2078. return _psfOuter->CompareIDs(lParam, pidl1, pidl2);
  2079. }
  2080. else
  2081. {
  2082. ASSERT(iRes == 0); // this handled by by CompareIDsOriginal() below
  2083. }
  2084. break;
  2085. case RIISA_FOLDERFIRST:
  2086. iRes = _CompareIDsFolderFirst(iColumn, pidl1, pidl2);
  2087. break;
  2088. case RIISA_ALPHABETICAL:
  2089. iRes = _CompareIDsAlphabetical(iColumn, pidl1, pidl2);
  2090. break;
  2091. }
  2092. // all our foofy compares and it still looks the same to us
  2093. // time to get medieval.
  2094. if (0 == iRes)
  2095. {
  2096. iRes = _CompareIDsOriginal(lParam, pidl1, pidl2);
  2097. if (0 == iRes)
  2098. iRes = iTypeCompare;
  2099. if (0 == iRes)
  2100. {
  2101. // If the class ID's really are the same,
  2102. // we'd better check the next level(s)
  2103. return ILCompareRelIDs(SAFECAST(this, IShellFolder *), pidl1, pidl2, lParam);
  2104. }
  2105. }
  2106. return ResultFromShort(iRes);
  2107. }
  2108. HRESULT CRegFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
  2109. {
  2110. return _psfOuter->CreateViewObject(hwnd, riid, ppv);
  2111. }
  2112. HRESULT CRegFolder::_SetAttributes(LPCIDLREGITEM pidlr, BOOL bPerUser, DWORD dwMask, DWORD dwNewBits)
  2113. {
  2114. HKEY hk;
  2115. HRESULT hr = SHRegGetCLSIDKey(_GetPIDLRCLSID(pidlr), TEXT("ShellFolder"), bPerUser, TRUE, &hk);
  2116. if (SUCCEEDED(hr))
  2117. {
  2118. DWORD err, dwValue = 0, cbSize = sizeof(dwValue);
  2119. SHQueryValueEx(hk, TEXT("Attributes"), NULL, NULL, (BYTE *)&dwValue, &cbSize);
  2120. dwValue = (dwValue & ~dwMask) | (dwNewBits & dwMask);
  2121. err = RegSetValueEx(hk, TEXT("Attributes"), 0, REG_DWORD, (BYTE *)&dwValue, sizeof(dwValue));
  2122. hr = HRESULT_FROM_WIN32(err);
  2123. RegCloseKey(hk);
  2124. }
  2125. EnterCriticalSection(&_cs);
  2126. _clsidAttributesCache = CLSID_NULL;
  2127. LeaveCriticalSection(&_cs);
  2128. return hr;
  2129. }
  2130. LONG CRegFolder::_RegOpenCLSIDUSKey(CLSID clsid, PHUSKEY phk)
  2131. {
  2132. WCHAR wszCLSID[39];
  2133. LONG iRetVal = ERROR_INVALID_PARAMETER;
  2134. if (StringFromGUID2(clsid, wszCLSID, ARRAYSIZE(wszCLSID)))
  2135. {
  2136. TCHAR szKey[MAXIMUM_SUB_KEY_LENGTH];
  2137. HRESULT hr;
  2138. hr = StringCchPrintf(szKey, ARRAYSIZE(szKey), TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\%s\\ShellFolder"), wszCLSID);
  2139. if (SUCCEEDED(hr))
  2140. {
  2141. iRetVal = SHRegOpenUSKey(szKey, KEY_QUERY_VALUE, NULL, phk, FALSE);
  2142. }
  2143. }
  2144. return iRetVal;
  2145. }
  2146. ULONG CRegFolder::_GetPerUserAttributes(LPCIDLREGITEM pidlr)
  2147. {
  2148. DWORD dwAttribute = 0;
  2149. HUSKEY hk;
  2150. if (ERROR_SUCCESS == _RegOpenCLSIDUSKey(_GetPIDLRCLSID(pidlr), &hk))
  2151. {
  2152. DWORD cb = sizeof(dwAttribute);
  2153. DWORD dwType = REG_DWORD;
  2154. SHRegQueryUSValue(hk, TEXT("Attributes"), &dwType, &dwAttribute, &cb, FALSE, 0, sizeof(DWORD));
  2155. SHRegCloseUSKey(hk);
  2156. }
  2157. // we only allow these bits to change
  2158. return dwAttribute & (SFGAO_NONENUMERATED | SFGAO_CANDELETE | SFGAO_CANMOVE);
  2159. }
  2160. #define SFGAO_REQ_MASK (SFGAO_NONENUMERATED | SFGAO_CANDELETE | SFGAO_CANMOVE)
  2161. HRESULT CRegFolder::_AttributesOf(LPCIDLREGITEM pidlr, DWORD dwAttributesNeeded, DWORD *pdwAttributes)
  2162. {
  2163. HRESULT hr = S_OK;
  2164. *pdwAttributes = 0;
  2165. PDELEGATEITEMID pidld = _IsDelegate(pidlr);
  2166. if (pidld)
  2167. {
  2168. IShellFolder *psf;
  2169. hr = _GetDelegateFolder(pidld, IID_PPV_ARG(IShellFolder, &psf));
  2170. if (SUCCEEDED(hr))
  2171. {
  2172. *pdwAttributes = dwAttributesNeeded;
  2173. hr = psf->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlr, pdwAttributes);
  2174. psf->Release();
  2175. }
  2176. }
  2177. else
  2178. {
  2179. EnterCriticalSection(&_cs);
  2180. CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment
  2181. BOOL bGuidMatch = IsEqualGUID(clsid, _clsidAttributesCache);
  2182. if (bGuidMatch && ((dwAttributesNeeded & _dwAttributesCacheValid) == dwAttributesNeeded))
  2183. {
  2184. *pdwAttributes = _dwAttributesCache;
  2185. }
  2186. else
  2187. {
  2188. int iItem = _ReqItemIndex(pidlr);
  2189. // if the guid didn't match, we need to start from scratch.
  2190. // otherwise, we'll or back in the cahced bits...
  2191. if (!bGuidMatch)
  2192. {
  2193. _dwAttributesCacheValid = 0;
  2194. _dwAttributesCache = 0;
  2195. }
  2196. if (iItem >= 0)
  2197. {
  2198. *pdwAttributes = _aReqItems[iItem].dwAttributes;
  2199. // per machine attributes allow items to be hidden per machine
  2200. *pdwAttributes |= SHGetAttributesFromCLSID2(&clsid, 0, SFGAO_REQ_MASK) & SFGAO_REQ_MASK;
  2201. }
  2202. else
  2203. {
  2204. *pdwAttributes = SHGetAttributesFromCLSID2(&clsid, SFGAO_CANMOVE | SFGAO_CANDELETE, dwAttributesNeeded & ~_dwAttributesCacheValid);
  2205. }
  2206. *pdwAttributes |= _GetPerUserAttributes(pidlr); // hidden per user
  2207. *pdwAttributes |= _dwDefAttributes; // per folder defaults
  2208. *pdwAttributes |= _dwAttributesCache;
  2209. _clsidAttributesCache = clsid;
  2210. _dwAttributesCache = *pdwAttributes;
  2211. _dwAttributesCacheValid |= dwAttributesNeeded | *pdwAttributes; // if they gave us more than we asked for, cache them
  2212. }
  2213. LeaveCriticalSection(&_cs);
  2214. }
  2215. return hr;
  2216. }
  2217. HRESULT CRegFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *prgfInOut)
  2218. {
  2219. HRESULT hr;
  2220. if (!cidl)
  2221. {
  2222. // special case for the folder as a whole, so I know nothing about it.
  2223. hr = _psfOuter->GetAttributesOf(cidl, apidl, prgfInOut);
  2224. }
  2225. else
  2226. {
  2227. hr = S_OK;
  2228. UINT rgfOut = *prgfInOut;
  2229. LPCITEMIDLIST *ppidl = (LPCITEMIDLIST*)LocalAlloc(LPTR, cidl * sizeof(*ppidl));
  2230. if (ppidl)
  2231. {
  2232. LPCITEMIDLIST *ppidlEnd = ppidl + cidl;
  2233. for (int i = cidl - 1; SUCCEEDED(hr) && (i >= 0); --i)
  2234. {
  2235. LPCIDLREGITEM pidlr = _IsReg(apidl[i]);
  2236. if (pidlr)
  2237. {
  2238. if ((*prgfInOut & SFGAO_VALIDATE) && !_IsDelegate(pidlr))
  2239. {
  2240. if (!_IsInNameSpace(pidlr))
  2241. {
  2242. // validate by binding
  2243. IUnknown *punk;
  2244. hr = _BindToItem(pidlr, NULL, IID_PPV_ARG(IUnknown, &punk), FALSE);
  2245. if (SUCCEEDED(hr))
  2246. punk->Release();
  2247. }
  2248. }
  2249. DWORD dwAttributes;
  2250. hr = _AttributesOf(pidlr, *prgfInOut, &dwAttributes);
  2251. if (SUCCEEDED(hr))
  2252. rgfOut &= dwAttributes;
  2253. cidl--; // remove this from the list used below...
  2254. }
  2255. else
  2256. {
  2257. --ppidlEnd;
  2258. *ppidlEnd = apidl[i];
  2259. }
  2260. }
  2261. if (SUCCEEDED(hr) && cidl) // any non reg items left?
  2262. {
  2263. ULONG rgfThis = rgfOut;
  2264. hr = _psfOuter->GetAttributesOf(cidl, ppidlEnd, &rgfThis);
  2265. rgfOut &= rgfThis;
  2266. }
  2267. LocalFree((HLOCAL)ppidl);
  2268. *prgfInOut = rgfOut;
  2269. }
  2270. else
  2271. hr = E_OUTOFMEMORY;
  2272. }
  2273. return hr;
  2274. }
  2275. HRESULT CRegFolder::_CreateDefExtIconKey(HKEY hkey,
  2276. UINT cidl, LPCITEMIDLIST *apidl, int iItem,
  2277. REFIID riid, void** ppvOut)
  2278. {
  2279. // See if this guy has an icon handler
  2280. TCHAR szHandler[GUIDSTR_MAX];
  2281. HRESULT hr;
  2282. if (hkey &&
  2283. SUCCEEDED(AssocQueryStringByKey(NULL, ASSOCSTR_SHELLEXTENSION, hkey,
  2284. TEXT("IconHandler"), szHandler, IntToPtr_(LPDWORD, ARRAYSIZE(szHandler)))) &&
  2285. SUCCEEDED(SHExtCoCreateInstance(szHandler, NULL, NULL, riid, ppvOut)))
  2286. {
  2287. IShellExtInit *psei;
  2288. if (SUCCEEDED(((IUnknown*)*ppvOut)->QueryInterface(IID_PPV_ARG(IShellExtInit, &psei))))
  2289. {
  2290. IDataObject *pdto;
  2291. hr = GetUIObjectOf(NULL, cidl, apidl, IID_PPV_ARG_NULL(IDataObject, &pdto));
  2292. if (SUCCEEDED(hr))
  2293. {
  2294. hr = psei->Initialize(_GetFolderIDList(), pdto, hkey);
  2295. pdto->Release();
  2296. }
  2297. psei->Release();
  2298. }
  2299. else
  2300. { // Object doesn't need to be initialized, no problemo
  2301. hr = S_OK;
  2302. }
  2303. if (SUCCEEDED(hr))
  2304. {
  2305. return S_OK;
  2306. }
  2307. ((IUnknown *)*ppvOut)->Release(); // Lose this bad guy
  2308. }
  2309. // No icon handler (or icon handler punted); look for DefaultIcon key.
  2310. LPCTSTR pszIconFile;
  2311. int iDefIcon;
  2312. if (iItem >= 0)
  2313. {
  2314. pszIconFile = _aReqItems[iItem].pszIconFile;
  2315. iDefIcon = _aReqItems[iItem].iDefIcon;
  2316. }
  2317. else
  2318. {
  2319. pszIconFile = NULL;
  2320. iDefIcon = II_FOLDER;
  2321. }
  2322. return SHCreateDefExtIconKey(hkey, pszIconFile, iDefIcon, iDefIcon, -1, -1, GIL_PERCLASS, riid, ppvOut);
  2323. }
  2324. HRESULT CRegFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
  2325. REFIID riid, UINT *prgfInOut, void **ppv)
  2326. {
  2327. HRESULT hr;
  2328. *ppv = NULL;
  2329. LPCIDLREGITEM pidlr = _AnyRegItem(cidl, apidl);
  2330. if (pidlr)
  2331. {
  2332. IShellFolder *psf;
  2333. if (_AllDelegates(cidl, apidl, &psf))
  2334. {
  2335. hr = psf->GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv);
  2336. psf->Release();
  2337. }
  2338. else
  2339. {
  2340. if (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW))
  2341. {
  2342. HKEY hkCLSID;
  2343. int iItem = _ReqItemIndex(pidlr);
  2344. // try first to get a per-user icon
  2345. hr = SHRegGetCLSIDKey(_GetPIDLRCLSID(pidlr), NULL, TRUE, FALSE, &hkCLSID);
  2346. if (SUCCEEDED(hr))
  2347. {
  2348. hr = _CreateDefExtIconKey(hkCLSID, cidl, apidl, iItem, riid, ppv);
  2349. if (hr == S_FALSE)
  2350. {
  2351. ((IUnknown *)*ppv)->Release(); // Lose this bad guy
  2352. *ppv = NULL;
  2353. }
  2354. RegCloseKey(hkCLSID);
  2355. }
  2356. //
  2357. // fall back to a per-class icon
  2358. //
  2359. if (*ppv == NULL)
  2360. {
  2361. hr = SHRegGetCLSIDKey(_GetPIDLRCLSID(pidlr), NULL, FALSE, FALSE, &hkCLSID);
  2362. if (SUCCEEDED(hr))
  2363. {
  2364. hr = _CreateDefExtIconKey(hkCLSID, cidl, apidl, iItem, riid, ppv);
  2365. RegCloseKey(hkCLSID);
  2366. }
  2367. }
  2368. }
  2369. else if (IsEqualIID(riid, IID_IQueryInfo))
  2370. {
  2371. hr = _GetInfoTip(pidlr, ppv);
  2372. }
  2373. else if (IsEqualIID(riid, IID_IQueryAssociations))
  2374. {
  2375. hr = _AssocCreate(pidlr, riid, ppv);
  2376. }
  2377. else if (IsEqualIID(riid, IID_IDataObject))
  2378. {
  2379. hr = CIDLData_CreateFromIDArray(_GetFolderIDList(), cidl, apidl, (IDataObject **)ppv);
  2380. }
  2381. else if (IsEqualIID(riid, IID_IContextMenu))
  2382. {
  2383. hr = _psfOuter->QueryInterface(IID_PPV_ARG(IShellFolder, &psf));
  2384. if (SUCCEEDED(hr))
  2385. {
  2386. HKEY keys[2];
  2387. _GetClassKeys(pidlr, &keys[0], &keys[1]);
  2388. hr = CDefFolderMenu_Create2Ex(_GetFolderIDList(), hwnd,
  2389. cidl, apidl,
  2390. psf, this,
  2391. ARRAYSIZE(keys), keys,
  2392. (IContextMenu **)ppv);
  2393. SHRegCloseKeys(keys, ARRAYSIZE(keys));
  2394. psf->Release();
  2395. }
  2396. }
  2397. else if (cidl == 1)
  2398. {
  2399. // blindly delegate unknown riid (IDropTarget, IShellLink, etc) through
  2400. // APP COMPAT! GetUIObjectOf does not support multilevel pidls, but
  2401. // Symantec Internet Fast Find does a
  2402. //
  2403. // psfDesktop->GetUIObjectOf(1, &pidlComplex, IID_IDropTarget, ...)
  2404. //
  2405. // on a multilevel pidl and expects it to work. I guess it worked by
  2406. // lucky accident once upon a time, so now it must continue to work,
  2407. // but only on the desktop.
  2408. //
  2409. hr = _CreateViewObjectFor(pidlr, hwnd, riid, ppv, !_IsDesktop());
  2410. }
  2411. else
  2412. {
  2413. hr = E_NOINTERFACE;
  2414. }
  2415. }
  2416. }
  2417. else
  2418. {
  2419. hr = _psfOuter->GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv);
  2420. }
  2421. return hr;
  2422. }
  2423. HRESULT CRegFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, STRRET *pStrRet)
  2424. {
  2425. HRESULT hr;
  2426. LPCIDLREGITEM pidlr = _IsReg(pidl);
  2427. if (pidlr)
  2428. {
  2429. LPCITEMIDLIST pidlNext = _ILNext(pidl);
  2430. if (ILIsEmpty(pidlNext))
  2431. {
  2432. TCHAR szName[MAX_PATH];
  2433. hr = _GetDisplayName(pidlr, dwFlags, szName, ARRAYSIZE(szName));
  2434. if (SUCCEEDED(hr))
  2435. hr = StringToStrRet(szName, pStrRet);
  2436. }
  2437. else
  2438. {
  2439. IShellFolder *psfNext;
  2440. hr = _BindToItem(pidlr, NULL, IID_PPV_ARG(IShellFolder, &psfNext), TRUE);
  2441. if (SUCCEEDED(hr))
  2442. {
  2443. hr = psfNext->GetDisplayNameOf(pidlNext, dwFlags, pStrRet);
  2444. // If it returns an offset to the pidlNext, we should
  2445. // change the offset relative to pidl.
  2446. if (SUCCEEDED(hr) && pStrRet->uType == STRRET_OFFSET)
  2447. pStrRet->uOffset += (DWORD)((BYTE *)pidlNext - (BYTE *)pidl);
  2448. psfNext->Release();
  2449. }
  2450. }
  2451. }
  2452. else
  2453. hr = _psfOuter->GetDisplayNameOf(pidl, dwFlags, pStrRet);
  2454. return hr;
  2455. }
  2456. HRESULT CRegFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl,
  2457. LPCOLESTR pszName, DWORD dwFlags, LPITEMIDLIST *ppidlOut)
  2458. {
  2459. LPCIDLREGITEM pidlr = _IsReg(pidl);
  2460. if (pidlr)
  2461. {
  2462. HRESULT hr = E_INVALIDARG;
  2463. if (ppidlOut)
  2464. *ppidlOut = NULL;
  2465. PDELEGATEITEMID pidld = _IsDelegate(pidlr);
  2466. if (pidld)
  2467. {
  2468. IShellFolder *psf;
  2469. hr = _GetDelegateFolder(pidld, IID_PPV_ARG(IShellFolder, &psf));
  2470. if (SUCCEEDED(hr))
  2471. {
  2472. hr = psf->SetNameOf(hwnd, pidl, pszName, dwFlags, ppidlOut);
  2473. psf->Release();
  2474. }
  2475. }
  2476. else
  2477. {
  2478. HKEY hkCLSID;
  2479. _ClearNameFromCache();
  2480. // See if per-user entry exists...
  2481. hr = SHRegGetCLSIDKey(_GetPIDLRCLSID(pidlr), NULL, TRUE, TRUE, &hkCLSID);
  2482. // If no per-user, then use per-machine...
  2483. if (FAILED(hr))
  2484. {
  2485. _GetClassKeys(pidlr, &hkCLSID, NULL);
  2486. if (hkCLSID)
  2487. {
  2488. hr = S_OK;
  2489. }
  2490. else
  2491. {
  2492. hr = E_FAIL;
  2493. }
  2494. }
  2495. if (SUCCEEDED(hr))
  2496. {
  2497. TCHAR szName[MAX_PATH];
  2498. SHUnicodeToTChar(pszName, szName, ARRAYSIZE(szName));
  2499. if (RegSetValue(hkCLSID, NULL, REG_SZ, szName, (lstrlen(szName) + 1) * sizeof(szName[0])) == ERROR_SUCCESS)
  2500. {
  2501. LPITEMIDLIST pidlAbs = ILCombine(_GetFolderIDList(), pidl);
  2502. if (pidlAbs)
  2503. {
  2504. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_IDLIST, pidlAbs, NULL);
  2505. ILFree(pidlAbs);
  2506. }
  2507. if (ppidlOut)
  2508. *ppidlOut = ILClone(pidl); // name is not in the PIDL so old == new
  2509. hr = S_OK;
  2510. }
  2511. else
  2512. hr = E_FAIL;
  2513. RegCloseKey(hkCLSID);
  2514. }
  2515. }
  2516. return hr;
  2517. }
  2518. return _psfOuter->SetNameOf(hwnd, pidl, pszName, dwFlags, ppidlOut);
  2519. }
  2520. HRESULT CRegFolder::GetDefaultSearchGUID(LPGUID lpGuid)
  2521. {
  2522. return _psfOuter->GetDefaultSearchGUID(lpGuid);
  2523. }
  2524. HRESULT CRegFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum)
  2525. {
  2526. return _psfOuter->EnumSearches(ppenum);
  2527. }
  2528. HRESULT CRegFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
  2529. {
  2530. return _psfOuter->GetDefaultColumn(dwRes, pSort, pDisplay);
  2531. }
  2532. HRESULT CRegFolder::GetDefaultColumnState(UINT iColumn, DWORD *pbState)
  2533. {
  2534. return _psfOuter->GetDefaultColumnState(iColumn, pbState);
  2535. }
  2536. HRESULT CRegFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
  2537. {
  2538. HRESULT hr = E_NOTIMPL;
  2539. LPCIDLREGITEM pidlr = _IsReg(pidl);
  2540. if (pidlr)
  2541. {
  2542. PDELEGATEITEMID pidld = _IsDelegate(pidlr);
  2543. if (pidld)
  2544. {
  2545. IShellFolder2 *psf2;
  2546. hr = _GetDelegateFolder(pidld, IID_PPV_ARG(IShellFolder2, &psf2));
  2547. if (SUCCEEDED(hr))
  2548. {
  2549. hr = psf2->GetDetailsEx(pidl, pscid, pv);
  2550. psf2->Release();
  2551. }
  2552. }
  2553. else
  2554. {
  2555. TCHAR szTemp[INFOTIPSIZE];
  2556. szTemp[0] = 0;
  2557. if (IsEqualSCID(*pscid, SCID_DESCRIPTIONID))
  2558. {
  2559. SHDESCRIPTIONID did;
  2560. did.dwDescriptionId = SHDID_ROOT_REGITEM;
  2561. did.clsid = _GetPIDLRCLSID(pidlr);
  2562. hr = InitVariantFromBuffer(pv, &did, sizeof(did));
  2563. }
  2564. else if (IsEqualSCID(*pscid, SCID_NAME))
  2565. {
  2566. _GetDisplayName(pidlr, SHGDN_NORMAL, szTemp, ARRAYSIZE(szTemp));
  2567. hr = InitVariantFromStr(pv, szTemp);
  2568. }
  2569. else if (IsEqualSCID(*pscid, SCID_TYPE))
  2570. {
  2571. LoadString(HINST_THISDLL, IDS_DRIVES_REGITEM, szTemp, ARRAYSIZE(szTemp));
  2572. hr = InitVariantFromStr(pv, szTemp);
  2573. }
  2574. else if (IsEqualSCID(*pscid, SCID_Comment))
  2575. {
  2576. _GetRegItemColumnFromRegistry(pidlr, TEXT("InfoTip"), szTemp, ARRAYSIZE(szTemp));
  2577. hr = InitVariantFromStr(pv, szTemp);
  2578. }
  2579. else if (IsEqualSCID(*pscid, SCID_FolderIntroText))
  2580. {
  2581. _GetRegItemColumnFromRegistry(pidlr, TEXT("IntroText"), szTemp, ARRAYSIZE(szTemp));
  2582. hr = InitVariantFromStr(pv, szTemp);
  2583. }
  2584. else
  2585. {
  2586. TCHAR ach[SCIDSTR_MAX];
  2587. StringFromSCID(pscid, ach, ARRAYSIZE(ach));
  2588. hr = _GetRegItemVariantFromRegistry(pidlr, ach, pv);
  2589. }
  2590. }
  2591. }
  2592. else
  2593. {
  2594. hr = _psfOuter->GetDetailsEx(pidl, pscid, pv);
  2595. }
  2596. return hr;
  2597. }
  2598. HRESULT CRegFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetail)
  2599. {
  2600. HRESULT hr = E_FAIL;
  2601. LPCIDLREGITEM pidlr = _IsReg(pidl);
  2602. if (pidlr)
  2603. {
  2604. pDetail->str.uType = STRRET_CSTR;
  2605. pDetail->str.cStr[0] = 0;
  2606. SHCOLUMNID scid;
  2607. hr = _psfOuter->MapColumnToSCID(iColumn, &scid);
  2608. if (SUCCEEDED(hr))
  2609. {
  2610. VARIANT var = {0};
  2611. hr = GetDetailsEx(pidl, &scid, &var);
  2612. if (SUCCEEDED(hr))
  2613. {
  2614. // we need to use SHFormatForDisplay (or we could have use IPropertyUI)
  2615. // to format arbitrary properties into the right display type
  2616. TCHAR szText[MAX_PATH];
  2617. hr = SHFormatForDisplay(scid.fmtid, scid.pid, (PROPVARIANT *)&var,
  2618. PUIFFDF_DEFAULT, szText, ARRAYSIZE(szText));
  2619. if (SUCCEEDED(hr))
  2620. {
  2621. hr = StringToStrRet(szText, &pDetail->str);
  2622. }
  2623. VariantClear(&var);
  2624. }
  2625. }
  2626. }
  2627. else
  2628. {
  2629. hr = _psfOuter->GetDetailsOf(pidl, iColumn, pDetail);
  2630. }
  2631. return hr;
  2632. }
  2633. HRESULT CRegFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
  2634. {
  2635. return _psfOuter->MapColumnToSCID(iColumn, pscid);
  2636. }
  2637. HRESULT CRegFolder::_GetOverlayInfo(LPCIDLREGITEM pidlr, int *pIndex, BOOL fIconIndex)
  2638. {
  2639. HRESULT hr = E_FAIL;
  2640. const CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment
  2641. if (SHQueryShellFolderValue(&clsid, TEXT("QueryForOverlay")))
  2642. {
  2643. IShellIconOverlay* psio;
  2644. hr = _BindToItem(pidlr, NULL, IID_PPV_ARG(IShellIconOverlay, &psio), TRUE);
  2645. if (SUCCEEDED(hr))
  2646. {
  2647. // NULL pidl means "I want to know about YOU, folder, not one of your kids",
  2648. // we only pass that though when its is not a deligate.
  2649. LPITEMIDLIST pidlToPass = (LPITEMIDLIST)_IsDelegate(pidlr);
  2650. if (fIconIndex)
  2651. hr = psio->GetOverlayIconIndex(pidlToPass, pIndex);
  2652. else
  2653. hr = psio->GetOverlayIndex(pidlToPass, pIndex);
  2654. psio->Release();
  2655. }
  2656. }
  2657. return hr;
  2658. }
  2659. // IShellIconOverlay
  2660. HRESULT CRegFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex)
  2661. {
  2662. HRESULT hr = E_FAIL;
  2663. LPCIDLREGITEM pidlr = _IsReg(pidl);
  2664. if (pidlr)
  2665. {
  2666. hr = _GetOverlayInfo(pidlr, pIndex, FALSE);
  2667. }
  2668. else if (_psioOuter)
  2669. {
  2670. hr = _psioOuter->GetOverlayIndex(pidl, pIndex);
  2671. }
  2672. return hr;
  2673. }
  2674. HRESULT CRegFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIconIndex)
  2675. {
  2676. HRESULT hr = E_FAIL;
  2677. LPCIDLREGITEM pidlr = _IsReg(pidl);
  2678. if (pidlr)
  2679. {
  2680. hr = _GetOverlayInfo(pidlr, pIconIndex, TRUE);
  2681. }
  2682. else if (_psioOuter)
  2683. {
  2684. hr = _psioOuter->GetOverlayIconIndex(pidl, pIconIndex);
  2685. }
  2686. return hr;
  2687. }
  2688. // CContextMenuCB
  2689. DWORD CALLBACK _RegFolderPropThreadProc(void *pv)
  2690. {
  2691. PROPSTUFF *pdps = (PROPSTUFF *)pv;
  2692. CRegFolder *prf = (CRegFolder *)pdps->psf;
  2693. STGMEDIUM medium;
  2694. ULONG_PTR dwCookie = 0;
  2695. ActivateActCtx(NULL, &dwCookie);
  2696. LPIDA pida = DataObj_GetHIDA(pdps->pdtobj, &medium);
  2697. if (pida)
  2698. {
  2699. LPCIDLREGITEM pidlr = prf->_IsReg(IDA_GetIDListPtr(pida, 0));
  2700. if (pidlr)
  2701. {
  2702. int iItem = prf->_ReqItemIndex(pidlr);
  2703. if (iItem >= 0 && prf->_aReqItems[iItem].pszCPL)
  2704. SHRunControlPanel(prf->_aReqItems[iItem].pszCPL, NULL);
  2705. else
  2706. {
  2707. TCHAR szName[MAX_PATH];
  2708. if (SUCCEEDED(prf->_GetDisplayName(pidlr, SHGDN_NORMAL, szName, ARRAYSIZE(szName))))
  2709. {
  2710. HKEY hk;
  2711. prf->_GetClassKeys(pidlr, &hk, NULL);
  2712. if (hk)
  2713. {
  2714. SHOpenPropSheet(szName, &hk, 1, NULL, pdps->pdtobj, NULL, (LPCTSTR)pdps->pStartPage);
  2715. RegCloseKey(hk);
  2716. }
  2717. }
  2718. }
  2719. }
  2720. HIDA_ReleaseStgMedium(pida, &medium);
  2721. }
  2722. return 0;
  2723. }
  2724. DWORD DisconnectDialogOnThread(void *pv)
  2725. {
  2726. WNetDisconnectDialog(NULL, RESOURCETYPE_DISK);
  2727. SHChangeNotifyHandleEvents(); // flush any drive notifications
  2728. return 0;
  2729. }
  2730. HRESULT CRegFolder::CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj,
  2731. UINT uMsg, WPARAM wParam, LPARAM lParam)
  2732. {
  2733. HRESULT hr = S_OK;
  2734. switch (uMsg)
  2735. {
  2736. case DFM_MERGECONTEXTMENU:
  2737. {
  2738. STGMEDIUM medium;
  2739. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  2740. if (pida)
  2741. {
  2742. // some ugly specal cases...
  2743. if (HIDA_GetCount(medium.hGlobal) == 1)
  2744. {
  2745. LPCIDLREGITEM pidlr = _IsReg(IDA_GetIDListPtr(pida, 0));
  2746. if (pidlr && !_IsDelegate(pidlr))
  2747. {
  2748. const CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment
  2749. if ((IsEqualGUID(clsid, CLSID_MyComputer) ||
  2750. IsEqualGUID(clsid, CLSID_NetworkPlaces)) &&
  2751. (GetSystemMetrics(SM_NETWORK) & RNC_NETWORKS) &&
  2752. !SHRestricted(REST_NONETCONNECTDISCONNECT))
  2753. {
  2754. CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_DESKTOP_ITEM, 0, (LPQCMINFO)lParam);
  2755. }
  2756. }
  2757. }
  2758. HIDA_ReleaseStgMedium(pida, &medium);
  2759. }
  2760. }
  2761. break;
  2762. case DFM_GETHELPTEXT:
  2763. LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));;
  2764. break;
  2765. case DFM_GETHELPTEXTW:
  2766. LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));;
  2767. break;
  2768. case DFM_INVOKECOMMANDEX:
  2769. {
  2770. DFMICS *pdfmics = (DFMICS *)lParam;
  2771. switch (wParam)
  2772. {
  2773. case FSIDM_CONNECT:
  2774. SHStartNetConnectionDialog(NULL, NULL, RESOURCETYPE_DISK);
  2775. break;
  2776. case FSIDM_DISCONNECT:
  2777. SHCreateThread(DisconnectDialogOnThread, NULL, CTF_COINIT, NULL);
  2778. break;
  2779. case DFM_CMD_PROPERTIES:
  2780. hr = SHLaunchPropSheet(_RegFolderPropThreadProc, pdtobj, (LPCTSTR)pdfmics->lParam, this, NULL);
  2781. break;
  2782. case DFM_CMD_DELETE:
  2783. _Delete(hwnd, pdfmics->fMask, pdtobj);
  2784. break;
  2785. default:
  2786. // This is one of view menu items, use the default code.
  2787. hr = S_FALSE;
  2788. break;
  2789. }
  2790. }
  2791. break;
  2792. default:
  2793. hr = E_NOTIMPL;
  2794. break;
  2795. }
  2796. return hr;
  2797. }
  2798. // IRegItemsFolder
  2799. TCHAR const c_szRegExplorerBackslash[] = REGSTR_PATH_EXPLORER TEXT("\\");
  2800. HRESULT CRegFolder::Initialize(REGITEMSINFO *pri)
  2801. {
  2802. ASSERT(pri != NULL);
  2803. HRESULT hr = E_INVALIDARG;
  2804. if (!_fcs)
  2805. {
  2806. if (!InitializeCriticalSectionAndSpinCount(&_cs, 0))
  2807. {
  2808. return E_FAIL;
  2809. }
  2810. }
  2811. _fcs = TRUE;
  2812. _pszRegKey = pri->pszRegKey;
  2813. _pPolicy = pri->pPolicy;
  2814. _chRegItem = pri->cRegItem;
  2815. _bFlags = pri->bFlags;
  2816. _iTypeOuter = pri->iCmp > 0 ? REGORDERTYPE_OUTERAFTER : REGORDERTYPE_OUTERBEFORE ;
  2817. _dwDefAttributes = pri->rgfRegItems;
  2818. _dwSortAttrib = pri->dwSortAttrib;
  2819. _cbPadding = pri->cbPadding;
  2820. _bFlagsLegacy = pri->bFlagsLegacy;
  2821. // If the registry key lives under HKEY_PATH_EXPLORER, then
  2822. // we will also support per-session regitems.
  2823. //
  2824. int cchPrefix = ARRAYSIZE(c_szRegExplorerBackslash) - 1;
  2825. if (StrCmpNI(_pszRegKey, c_szRegExplorerBackslash, cchPrefix) == 0)
  2826. {
  2827. _pszSesKey = _pszRegKey + cchPrefix;
  2828. }
  2829. else
  2830. {
  2831. _pszSesKey = NULL;
  2832. }
  2833. if ((RIISA_ORIGINAL == _dwSortAttrib) ||
  2834. (RIISA_FOLDERFIRST == _dwSortAttrib) ||
  2835. (RIISA_ALPHABETICAL == _dwSortAttrib))
  2836. {
  2837. Str_SetPtr(&_pszMachine, pri->pszMachine); // save a copy of this
  2838. _aReqItems = (REQREGITEM *)LocalAlloc(LPTR, sizeof(*_aReqItems) * pri->iReqItems);
  2839. if (!_aReqItems)
  2840. return E_OUTOFMEMORY;
  2841. memcpy(_aReqItems, pri->pReqItems, sizeof(*_aReqItems) * pri->iReqItems);
  2842. _nRequiredItems = pri->iReqItems;
  2843. // If we are aggregated, cache the _psioOuter and _psfOuter
  2844. _QueryOuterInterface(IID_PPV_ARG(IShellIconOverlay, &_psioOuter));
  2845. hr = _QueryOuterInterface(IID_PPV_ARG(IShellFolder2, &_psfOuter));
  2846. }
  2847. return hr;
  2848. }
  2849. //
  2850. // instance creation of the RegItems object
  2851. //
  2852. STDAPI CRegFolder_CreateInstance(REGITEMSINFO *pri, IUnknown *punkOuter, REFIID riid, void **ppv)
  2853. {
  2854. HRESULT hr;
  2855. // we only suport being created as an agregate
  2856. if (!punkOuter || !IsEqualIID(riid, IID_IUnknown))
  2857. {
  2858. ASSERT(0);
  2859. return E_FAIL;
  2860. }
  2861. CRegFolder *prif = new CRegFolder(punkOuter);
  2862. if (prif)
  2863. {
  2864. hr = prif->Initialize(pri); // initialize the regfolder
  2865. if (SUCCEEDED(hr))
  2866. hr = prif->_GetInner()->QueryInterface(riid, ppv);
  2867. //
  2868. // If the Initalize and QueryInterface succeeded, this will drop
  2869. // the refcount from 2 to 1. If they failed, then this will drop
  2870. // the refcount from 1 to 0 and free the object.
  2871. //
  2872. ULONG cRef = prif->_GetInner()->Release();
  2873. //
  2874. // On success, the object should have a refcout of exactly 1.
  2875. // On failure, the object should have a refcout of exactly 0.
  2876. //
  2877. ASSERT(SUCCEEDED(hr) == (BOOL)cRef);
  2878. }
  2879. else
  2880. hr = E_OUTOFMEMORY;
  2881. return hr;
  2882. }
  2883. CRegFolderEnum::CRegFolderEnum(CRegFolder* prf, DWORD grfFlags, IEnumIDList* peidl,
  2884. HDCA hdca, HDCA hdcaDel,
  2885. REGITEMSPOLICY* pPolicy) :
  2886. _cRef(1),
  2887. _grfFlags(grfFlags),
  2888. _prf(prf),
  2889. _peidl(peidl),
  2890. _hdca(hdca),
  2891. _pPolicy(pPolicy),
  2892. _hdcaDel(hdcaDel)
  2893. {
  2894. ASSERT(_iCur == 0);
  2895. ASSERT(_iCurDel == 0);
  2896. ASSERT(_peidlDel == NULL);
  2897. _prf->AddRef();
  2898. if (_peidl)
  2899. _peidl->AddRef();
  2900. DllAddRef();
  2901. }
  2902. CRegFolderEnum::~CRegFolderEnum()
  2903. {
  2904. if (_hdca)
  2905. DCA_Destroy(_hdca);
  2906. if (_hdcaDel)
  2907. DCA_Destroy(_hdcaDel);
  2908. ATOMICRELEASE(_prf);
  2909. ATOMICRELEASE(_peidl);
  2910. ATOMICRELEASE(_peidlDel);
  2911. DllRelease();
  2912. }
  2913. //
  2914. // IUnknown
  2915. //
  2916. STDMETHODIMP_(ULONG) CRegFolderEnum::AddRef()
  2917. {
  2918. return InterlockedIncrement(&_cRef);
  2919. }
  2920. STDMETHODIMP_(ULONG) CRegFolderEnum::Release()
  2921. {
  2922. ASSERT( 0 != _cRef );
  2923. ULONG cRef = InterlockedDecrement(&_cRef);
  2924. if ( 0 == cRef )
  2925. {
  2926. delete this;
  2927. }
  2928. return cRef;
  2929. }
  2930. HRESULT CRegFolderEnum::QueryInterface(REFIID riid, void **ppv)
  2931. {
  2932. static const QITAB qit[] = {
  2933. QITABENT(CRegFolderEnum, IEnumIDList), // IID_IEnumIDList
  2934. QITABENT(CRegFolderEnum, IObjectWithSite), // IID_IObjectWithSite
  2935. { 0 },
  2936. };
  2937. return QISearch(this, qit, riid, ppv);
  2938. }
  2939. //
  2940. // IEnumIDList
  2941. //
  2942. BOOL CRegFolderEnum::_TestFolderness(DWORD dwAttribItem)
  2943. {
  2944. if ((_grfFlags & (SHCONTF_FOLDERS | SHCONTF_NONFOLDERS)) != (SHCONTF_FOLDERS | SHCONTF_NONFOLDERS))
  2945. {
  2946. if (dwAttribItem & SFGAO_FOLDER)
  2947. {
  2948. if (!(_grfFlags & SHCONTF_FOLDERS))
  2949. return FALSE;
  2950. }
  2951. else
  2952. {
  2953. if (!(_grfFlags & SHCONTF_NONFOLDERS))
  2954. return FALSE;
  2955. }
  2956. }
  2957. return TRUE;
  2958. }
  2959. BOOL CRegFolderEnum::_TestHidden(LPCIDLREGITEM pidlRegItem)
  2960. {
  2961. CLSID clsidRegItem = _prf->_GetPIDLRCLSID(pidlRegItem);
  2962. return _TestHiddenInWebView(&clsidRegItem) || _TestHiddenInDomain(&clsidRegItem);
  2963. }
  2964. BOOL CRegFolderEnum::_TestHiddenInWebView(LPCLSID clsidRegItem)
  2965. {
  2966. BOOL fRetVal = FALSE;
  2967. if (S_FALSE == SHShouldShowWizards(_punkSite))
  2968. {
  2969. fRetVal = SHQueryShellFolderValue(clsidRegItem, TEXT("HideInWebView"));
  2970. }
  2971. return fRetVal;
  2972. }
  2973. BOOL CRegFolderEnum::_TestHiddenInDomain(LPCLSID clsidRegItem)
  2974. {
  2975. return IsOS(OS_DOMAINMEMBER) && SHQueryShellFolderValue(clsidRegItem, TEXT("HideInDomain"));
  2976. }
  2977. // Check policy restrictions
  2978. BOOL CRegFolderEnum::_IsRestricted()
  2979. {
  2980. BOOL bIsRestricted = FALSE;
  2981. if (_pPolicy)
  2982. {
  2983. TCHAR szName[256];
  2984. szName[0] = 0;
  2985. HKEY hkRoot;
  2986. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, _T("CLSID"), NULL, KEY_QUERY_VALUE, &hkRoot))
  2987. {
  2988. TCHAR szGUID[64];
  2989. SHStringFromGUID(*DCA_GetItem(_hdca, _iCur - 1), szGUID, ARRAYSIZE(szGUID));
  2990. SHLoadLegacyRegUIString(hkRoot, szGUID, szName, ARRAYSIZE(szName));
  2991. RegCloseKey(hkRoot);
  2992. }
  2993. if (szName[0])
  2994. {
  2995. if (SHRestricted(_pPolicy->restAllow) && !IsNameListedUnderKey(szName, _pPolicy->pszAllow))
  2996. bIsRestricted = TRUE;
  2997. if (SHRestricted(_pPolicy->restDisallow) && IsNameListedUnderKey(szName, _pPolicy->pszDisallow))
  2998. bIsRestricted = TRUE;
  2999. }
  3000. }
  3001. return bIsRestricted;
  3002. }
  3003. BOOL CRegFolderEnum::_WrongMachine()
  3004. {
  3005. BOOL bWrongMachine = FALSE;
  3006. // We're filling the regitem with class id clsid. If this is a
  3007. // remote item, first invoke the class to see if it really wants
  3008. // to be enumerated for this remote computer.
  3009. if (_prf->_pszMachine)
  3010. {
  3011. IUnknown* punk;
  3012. // Don't need DCA_ExtCreateInstance since these keys come from
  3013. // HKLM which is already trusted and HKCU which is the user's
  3014. // own fault.
  3015. HRESULT hr = DCA_CreateInstance(_hdca, _iCur - 1, IID_PPV_ARG(IUnknown, &punk));
  3016. if (SUCCEEDED(hr))
  3017. {
  3018. hr = _prf->_InitFromMachine(punk, TRUE);
  3019. punk->Release();
  3020. }
  3021. bWrongMachine = FAILED(hr);
  3022. }
  3023. return bWrongMachine;
  3024. }
  3025. HRESULT CRegFolderEnum::Next(ULONG celt, LPITEMIDLIST *ppidlOut, ULONG *pceltFetched)
  3026. {
  3027. // enumerate from the DCA containing the regitems objects
  3028. if (_hdca)
  3029. {
  3030. if (0 == (SHCONTF_NETPRINTERSRCH & _grfFlags)) //don't enumerate phantom folders for printer search dialog
  3031. {
  3032. while (_iCur < DCA_GetItemCount(_hdca))
  3033. {
  3034. _iCur++;
  3035. if (_WrongMachine())
  3036. continue;
  3037. if (_IsRestricted())
  3038. continue;
  3039. // Ok, actually enumerate the item
  3040. HRESULT hr;
  3041. IDLREGITEM* pidlRegItem = _prf->_CreateAndFillIDLREGITEM(DCA_GetItem(_hdca, _iCur-1));
  3042. if (pidlRegItem)
  3043. {
  3044. DWORD dwAttribItem;
  3045. _prf->_AttributesOf(pidlRegItem, SFGAO_NONENUMERATED | SFGAO_FOLDER, &dwAttribItem);
  3046. if (!(dwAttribItem & SFGAO_NONENUMERATED) &&
  3047. _TestFolderness(dwAttribItem) &&
  3048. !_TestHidden(pidlRegItem))
  3049. {
  3050. *ppidlOut = (LPITEMIDLIST)pidlRegItem;
  3051. hr = S_OK;
  3052. }
  3053. else
  3054. {
  3055. SHFree(pidlRegItem);
  3056. continue;
  3057. }
  3058. }
  3059. else
  3060. {
  3061. hr = E_OUTOFMEMORY;
  3062. }
  3063. if (SUCCEEDED(hr) && pceltFetched)
  3064. *pceltFetched = 1;
  3065. return hr;
  3066. }
  3067. }
  3068. }
  3069. // enumerate from the DCA containing the delegate shell folders
  3070. while (_peidlDel || (_hdcaDel && (_iCurDel < DCA_GetItemCount(_hdcaDel))))
  3071. {
  3072. // we have an enumerator object, so lets call it and see
  3073. // what it generates, if it runs out of items then we either
  3074. // give up (saying were done) or allow us to be called again.
  3075. if (_peidlDel)
  3076. {
  3077. if (S_OK == _peidlDel->Next(celt, ppidlOut, pceltFetched))
  3078. return S_OK;
  3079. ATOMICRELEASE(_peidlDel);
  3080. }
  3081. else
  3082. {
  3083. // we didn't have an enumerator to call, so lets try and
  3084. // create an new IDelegateFolderObject, if that worked
  3085. // then we can set its item allocator, then get an
  3086. // enumerator back from it.
  3087. IShellFolder *psfDelegate;
  3088. if (SUCCEEDED(_prf->_CreateDelegateFolder(DCA_GetItem(_hdcaDel, _iCurDel++), IID_PPV_ARG(IShellFolder, &psfDelegate))))
  3089. {
  3090. psfDelegate->EnumObjects(NULL, _grfFlags, &_peidlDel);
  3091. psfDelegate->Release();
  3092. }
  3093. }
  3094. }
  3095. // now DKA, or we are just about done so lets pass to the inner ISF
  3096. // and see what they return.
  3097. if (_peidl)
  3098. return _peidl->Next(celt, ppidlOut, pceltFetched);
  3099. *ppidlOut = NULL;
  3100. if (pceltFetched)
  3101. pceltFetched = 0;
  3102. return S_FALSE;
  3103. }
  3104. STDMETHODIMP CRegFolderEnum::Reset()
  3105. {
  3106. // Adaptec Easy CD Creator (versions 3.0, 3.01, 3.5) enumerates the
  3107. // items in an IShellFolder like this:
  3108. //
  3109. // psf->EnumObjects(&penum);
  3110. // UINT cObjects = 0;
  3111. // while (SUCCEEDED(penum->Next(...)) {
  3112. // [code]
  3113. // penum->Reset();
  3114. // penum->Skip(++cObjects);
  3115. // }
  3116. //
  3117. // So they took an O(n) algorithm and turned it into an O(n^2)
  3118. // algorithm. They got away with it because in the old days,
  3119. // regfldr implemented neither IEnumIDList::Reset nor
  3120. // IEnumIDList::Skip, so the two calls were just NOPs.
  3121. //
  3122. // Now we implement IEnumIDList::Reset, so without this hack,
  3123. // they end up enumerating the same object over and over again.
  3124. if (SHGetAppCompatFlags(ACF_IGNOREENUMRESET) & ACF_IGNOREENUMRESET)
  3125. return E_NOTIMPL;
  3126. _iCurDel = _iCur = 0;
  3127. ATOMICRELEASE(_peidlDel);
  3128. if (_peidl)
  3129. return _peidl->Reset();
  3130. return S_OK;
  3131. }
  3132. STDMETHODIMP CRegFolderEnum::SetSite(IUnknown *punkSite)
  3133. {
  3134. IUnknown_SetSite(_peidl, punkSite);
  3135. return CObjectWithSite::SetSite(punkSite);
  3136. }
  3137. //
  3138. // Delegate Malloc functions
  3139. //
  3140. #undef new // Hack!! Need to remove this (daviddv)
  3141. class CDelagateMalloc : public IMalloc
  3142. {
  3143. public:
  3144. // IUnknown
  3145. STDMETHODIMP QueryInterface(REFIID,void **);
  3146. STDMETHODIMP_(ULONG) AddRef(void);
  3147. STDMETHODIMP_(ULONG) Release(void);
  3148. // IMalloc
  3149. STDMETHODIMP_(void *) Alloc(SIZE_T cb);
  3150. STDMETHODIMP_(void *) Realloc(void *pv, SIZE_T cb);
  3151. STDMETHODIMP_(void) Free(void *pv);
  3152. STDMETHODIMP_(SIZE_T) GetSize(void *pv);
  3153. STDMETHODIMP_(int) DidAlloc(void *pv);
  3154. STDMETHODIMP_(void) HeapMinimize();
  3155. private:
  3156. CDelagateMalloc(void *pv, SIZE_T cbSize, WORD wOuter);
  3157. ~CDelagateMalloc() {}
  3158. void* operator new(size_t cbClass, SIZE_T cbSize)
  3159. {
  3160. return ::operator new(cbClass + cbSize);
  3161. }
  3162. friend HRESULT CDelegateMalloc_Create(void *pv, SIZE_T cbSize, WORD wOuter, IMalloc **ppmalloc);
  3163. protected:
  3164. LONG _cRef;
  3165. WORD _wOuter; // delegate item outer signature
  3166. WORD _wUnused; // to allign
  3167. #ifdef DEBUG
  3168. UINT _cAllocs;
  3169. #endif
  3170. SIZE_T _cb;
  3171. BYTE _data[EMPTY_SIZE];
  3172. };
  3173. CDelagateMalloc::CDelagateMalloc(void *pv, SIZE_T cbSize, WORD wOuter)
  3174. {
  3175. _cRef = 1;
  3176. _wOuter = wOuter;
  3177. _cb = cbSize;
  3178. memcpy(_data, pv, _cb);
  3179. }
  3180. HRESULT CDelagateMalloc::QueryInterface(REFIID riid, void **ppvObj)
  3181. {
  3182. static const QITAB qit[] = {
  3183. QITABENT(CDelagateMalloc, IMalloc),
  3184. { 0 },
  3185. };
  3186. return QISearch(this, qit, riid, ppvObj);
  3187. }
  3188. ULONG CDelagateMalloc::AddRef()
  3189. {
  3190. return InterlockedIncrement(&_cRef);
  3191. }
  3192. ULONG CDelagateMalloc::Release()
  3193. {
  3194. ASSERT( 0 != _cRef );
  3195. ULONG cRef = InterlockedDecrement(&_cRef);
  3196. if ( 0 == cRef )
  3197. {
  3198. delete this;
  3199. }
  3200. return cRef;
  3201. }
  3202. // the cbInner is the size of the data needed by the delegate. we need to compute
  3203. // the full size of the pidl for the allocation and init that we the outer folder data
  3204. void *CDelagateMalloc::Alloc(SIZE_T cbInner)
  3205. {
  3206. DELEGATEITEMID *pidl;
  3207. SIZE_T cbAlloc =
  3208. sizeof(DELEGATEITEMID) - sizeof(pidl->rgb[0]) + // header
  3209. cbInner + // inner
  3210. _cb + // outer data
  3211. sizeof(WORD); // trailing null (pidl terminator)
  3212. pidl = (DELEGATEITEMID *)SHAlloc(cbAlloc);
  3213. if (pidl)
  3214. {
  3215. ZeroMemory(pidl, cbAlloc); // make it all empty
  3216. pidl->cbSize = (WORD)cbAlloc - sizeof(WORD);
  3217. pidl->wOuter = _wOuter;
  3218. pidl->cbInner = (WORD)cbInner;
  3219. memcpy(&pidl->rgb[cbInner], _data, _cb);
  3220. #ifdef DEBUG
  3221. _cAllocs++;
  3222. #endif
  3223. }
  3224. return pidl;
  3225. }
  3226. void *CDelagateMalloc::Realloc(void *pv, SIZE_T cb)
  3227. {
  3228. return NULL;
  3229. }
  3230. void CDelagateMalloc::Free(void *pv)
  3231. {
  3232. SHFree(pv);
  3233. }
  3234. SIZE_T CDelagateMalloc::GetSize(void *pv)
  3235. {
  3236. return (SIZE_T)-1;
  3237. }
  3238. int CDelagateMalloc::DidAlloc(void *pv)
  3239. {
  3240. return -1;
  3241. }
  3242. void CDelagateMalloc::HeapMinimize()
  3243. {
  3244. }
  3245. STDAPI CDelegateMalloc_Create(void *pv, SIZE_T cbSize, WORD wOuter, IMalloc **ppmalloc)
  3246. {
  3247. HRESULT hr;
  3248. CDelagateMalloc *pdm = new(cbSize) CDelagateMalloc(pv, cbSize, wOuter);
  3249. if (pdm)
  3250. {
  3251. hr = pdm->QueryInterface(IID_PPV_ARG(IMalloc, ppmalloc));
  3252. pdm->Release();
  3253. }
  3254. else
  3255. hr = E_OUTOFMEMORY;
  3256. return hr;
  3257. }