Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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