#include "shellprv.h" #include "ids.h" #include "infotip.h" #include "datautil.h" #include #include "pidl.h" #include "fstreex.h" #include "views.h" #include "shitemid.h" #include "ole2dup.h" #include "deskfldr.h" #include "prop.h" #include "util.h" // GetVariantFromRegistryValue #include "defcm.h" #include "cowsite.h" #pragma hdrstop // // HACKHACK: GUIDs for _IsInNameSpace hack // // {D6277990-4C6A-11CF-8D87-00AA0060F5BF} const GUID CLSID_ScheduledTasks = { 0xD6277990, 0x4C6A, 0x11CF, { 0x8D, 0x87, 0x00, 0xAA, 0x00, 0x60, 0xF5, 0xBF } }; // {7007ACC7-3202-11D1-AAD2-00805FC1270E} const GUID CLSID_NetworkConnections = { 0x7007ACC7, 0x3202, 0x11D1, { 0xAA, 0xD2, 0x00, 0x80, 0x5F, 0xC1, 0x27, 0x0E } }; // // delegate regitems are a special type of IDLIST. they share the same type as a regular REGITEM however // they have a different IDLIST format. we are unable to change the format of REGITEM IDLISTs so to facilitate // delegate objects we store items using the following format: // // // // DELEGATEITEMID // is a shell structure which contains information about the folder specific information, // and a regular ITEMIDLIST header. // // // this is specific to the IShellFolder that is being merged into the namespace. // // // this contains a signature so we can determine if the IDLIST is a special delegate, // and the CLSID of the IShellFolder which owns this data. // // all delegate regitems are allocated using the IDelegateMalloc // {5e591a74-df96-48d3-8d67-1733bcee28ba} const GUID CLSID_DelegateItemSig = { 0x5e591a74, 0xdf96, 0x48d3, {0x8d, 0x67, 0x17, 0x33, 0xbc, 0xee, 0x28, 0xba} }; typedef struct { CLSID clsidSignature; // == CLSID_DelegateItemSig (indicating this is a delegate object) CLSID clsidShellFolder; // == CLSID of IShellFolder implementing this delegateitem } DELEGATEITEMDATA; typedef UNALIGNED DELEGATEITEMDATA *PDELEGATEITEMDATA; typedef const UNALIGNED DELEGATEITEMDATA *PCDELEGATEITEMDATA; // IDREGITEM as implemented in NT5 Beta 3, breaks the ctrlfldr IShellFolder of downlevel // platforms. The clsid is interpreted as the IDCONTROL's oName and oInfo and these offsets // are way to big for the following buffer (cBuf). On downlevel platform, when we are lucky, // the offset is still in memory readable by our process, and we just do random stuff. When // unlucky we try to read memory which we do not have access to and crash. The IDREGITEMEX // struct solves this by putting padding between bOrder and the CLSID and filling these bytes // with 0's. When persisted, downlevel platform will interpret these 0's as oName, oInfo and // as L'\0' at the beggining of cBuf. A _bFlagsLegacy was also added to handle the NT5 Beta3 // persisted pidls. (stephstm, 7/15/99) // Note: in the case where CRegFldr::_cbPadding == 0, IDREGITEMEX.rgbPadding[0] is at // same location as the IDREGITEM.clsid #pragma pack(1) typedef struct { WORD cb; BYTE bFlags; BYTE bOrder; BYTE rgbPadding[16]; // at least 16 to store the clsid } IDREGITEMEX; typedef UNALIGNED IDREGITEMEX *LPIDREGITEMEX; typedef const UNALIGNED IDREGITEMEX *LPCIDREGITEMEX; #pragma pack() STDAPI_(BOOL) IsNameListedUnderKey(LPCTSTR pszFileName, LPCTSTR pszKey); C_ASSERT(sizeof(IDREGITEMEX) == sizeof(IDREGITEM)); EXTERN_C const IDLREGITEM c_idlNet = { {sizeof(IDREGITEM), SHID_ROOT_REGITEM, SORT_ORDER_NETWORK, { 0x208D2C60, 0x3AEA, 0x1069, 0xA2,0xD7,0x08,0x00,0x2B,0x30,0x30,0x9D, },}, // CLSID_NetworkPlaces 0, } ; EXTERN_C const IDLREGITEM c_idlDrives = { {sizeof(IDREGITEM), SHID_ROOT_REGITEM, SORT_ORDER_DRIVES, { 0x20D04FE0, 0x3AEA, 0x1069, 0xA2,0xD8,0x08,0x00,0x2B,0x30,0x30,0x9D, },}, // CLSID_MyComputer 0, } ; EXTERN_C const IDLREGITEM c_idlInetRoot = { {sizeof(IDREGITEM), SHID_ROOT_REGITEM, SORT_ORDER_INETROOT, { 0x871C5380, 0x42A0, 0x1069, 0xA2,0xEA,0x08,0x00,0x2B,0x30,0x30,0x9D, },}, // CSIDL_Internet 0, } ; EXTERN_C const IDREGITEM c_aidlConnections[] = { {sizeof(IDREGITEM), SHID_ROOT_REGITEM, SORT_ORDER_DRIVES, { 0x20D04FE0, 0x3AEA, 0x1069, 0xA2,0xD8,0x08,0x00,0x2B,0x30,0x30,0x9D, },}, // CLSID_MyComputer {sizeof(IDREGITEM), SHID_COMPUTER_REGITEM, 0, { 0x21EC2020, 0x3AEA, 0x1069, 0xA2,0xDD,0x08,0x00,0x2B,0x30,0x30,0x9D, },}, // CLSID_ControlPanel {sizeof(IDREGITEM), SHID_CONTROLPANEL_REGITEM, 0, { 0x7007ACC7, 0x3202, 0x11D1, 0xAA,0xD2,0x00,0x80,0x5F,0xC1,0x27,0x0E, },}, // CLSID_NetworkConnections { 0 }, }; enum { REGORDERTYPE_OUTERBEFORE = -1, REGORDERTYPE_REQITEM = 0, REGORDERTYPE_REGITEM, REGORDERTYPE_DELEGATE, REGORDERTYPE_OUTERAFTER }; // // class that implements the regitems folder // // CLSID_RegFolder {0997898B-0713-11d2-A4AA-00C04F8EEB3E} const GUID CLSID_RegFolder = { 0x997898b, 0x713, 0x11d2, { 0xa4, 0xaa, 0x0, 0xc0, 0x4f, 0x8e, 0xeb, 0x3e } }; class CRegFolderEnum; // forward class CRegFolder : public CAggregatedUnknown, public IShellFolder2, public IContextMenuCB, public IShellIconOverlay { public: // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj) { return CAggregatedUnknown::QueryInterface(riid, ppvObj); }; STDMETHODIMP_(ULONG) AddRef(void) { return CAggregatedUnknown::AddRef(); }; // // PowerDesk98 passes a CFSFolder to CRegFolder::Release(), so validate // the pointer before proceeding down the path to iniquity. // STDMETHODIMP_(ULONG) Release(void) { return _dwSignature == c_dwSignature ? CAggregatedUnknown::Release() : 0; }; // IShellFolder STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes); STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList); STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut); STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj); STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); STDMETHODIMP CreateViewObject (HWND hwndOwner, REFIID riid, void **ppvOut); STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG *rgfInOut); STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppvOut); STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName); STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST * ppidlOut); // IShellFolder2 STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid); STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum); STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay); STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState); STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv); STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails); STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid); // IShellIconOverlay STDMETHODIMP GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex); STDMETHODIMP GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIconIndex); // IContextMenuCB STDMETHODIMP CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam); // IRegItemFolder STDMETHODIMP Initialize(REGITEMSINFO *pri); protected: CRegFolder(IUnknown *punkOuter); ~CRegFolder(); // used by the CAggregatedUnknown stuff HRESULT v_InternalQueryInterface(REFIID riid,void **ppvObj); HRESULT _GetOverlayInfo(LPCIDLREGITEM pidlr, int *pIndex, BOOL fIconIndex); LPCITEMIDLIST _GetFolderIDList(); LPCIDLREGITEM _AnyRegItem(UINT cidl, LPCITEMIDLIST apidl[]); BOOL _AllDelegates(UINT cidl, LPCITEMIDLIST *apidl, IShellFolder **ppsf); int _ReqItemIndex(LPCIDLREGITEM pidlr); BYTE _GetOrderPure(LPCIDLREGITEM pidlr); BYTE _GetOrder(LPCIDLREGITEM pidlr); int _GetOrderType(LPCITEMIDLIST pidl); void _GetNameSpaceKey(LPCIDLREGITEM pidlr, LPTSTR pszKeyName, UINT cchKeyName); LPCIDLREGITEM _IsReg(LPCITEMIDLIST pidl); PDELEGATEITEMID _IsDelegate(LPCIDLREGITEM pidlr); HRESULT _CreateDelegateFolder(const CLSID* pclsid, REFIID riid, void **ppv); HRESULT _GetDelegateFolder(PDELEGATEITEMID pidld, REFIID riid, void **ppv); BOOL _IsInNameSpace(LPCIDLREGITEM pidlr); HDCA _ItemArray(); HDCA _DelItemArray(); HRESULT _InitFromMachine(IUnknown *punk, BOOL bEnum); HRESULT _CreateAndInit(LPCIDLREGITEM pidlr, LPBC pbc, REFIID riid, void **ppv); HRESULT _BindToItem(LPCIDLREGITEM pidlr, LPBC pbc, REFIID riid, void **ppv, BOOL bOneLevel); HRESULT _GetInfoTip(LPCIDLREGITEM pidlr, void **ppv); HRESULT _GetRegItemColumnFromRegistry(LPCIDLREGITEM pidlr, LPCTSTR pszColumnName, LPTSTR pszColumnData, int cchColumnData); HRESULT _GetRegItemVariantFromRegistry(LPCIDLREGITEM pidlr, LPCTSTR pszColumnName, VARIANT *pv); void _GetClassKeys(LPCIDLREGITEM pidlr, HKEY *phkCLSID, HKEY *phkBase); HRESULT _GetDisplayNameFromSelf(LPCIDLREGITEM pidlr, DWORD dwFlags, LPTSTR pszName, UINT cchName); HRESULT _GetDisplayName(LPCIDLREGITEM pidlr, DWORD dwFlags, LPTSTR pszName, UINT cchName); HRESULT _DeleteRegItem(LPCIDLREGITEM pidlr); BOOL _GetDeleteMessage(LPCIDLREGITEM pidlr, LPTSTR pszMsg, int cchMax); HRESULT _ParseNextLevel(HWND hwnd, LPBC pbc, LPCIDLREGITEM pidlr, LPOLESTR pwzRest, LPITEMIDLIST *ppidlOut, ULONG *pdwAttributes); HRESULT _ParseGUIDName(HWND hwnd, LPBC pbc, LPOLESTR pwzDisplayName, LPITEMIDLIST *ppidlOut, ULONG *pdwAttributes); HRESULT _ParseThroughItem(LPCIDLREGITEM pidlr, HWND hwnd, LPBC pbc, LPOLESTR pszName, ULONG *pchEaten, LPITEMIDLIST *ppidlOut, ULONG *pdwAttributes); HRESULT _SetAttributes(LPCIDLREGITEM pidlr, BOOL bPerUser, DWORD dwMask, DWORD dwNewBits); LONG _RegOpenCLSIDUSKey(CLSID clsid, PHUSKEY phk); ULONG _GetPerUserAttributes(LPCIDLREGITEM pidlr); HRESULT _AttributesOf(LPCIDLREGITEM pidlr, DWORD dwAttributesNeeded, DWORD *pdwAttributes); HRESULT _CreateDefExtIconKey(HKEY hkey, UINT cidl, LPCITEMIDLIST *apidl, int iItem, REFIID riid, void** ppvOut); BOOL _CanDelete(LPCIDLREGITEM pidlr); void _Delete(HWND hwnd, UINT uFlags, IDataObject *pdtobj); HRESULT _AssocCreate(LPCIDLREGITEM pidl, REFIID riid, void **ppv); // // inline // // Will probably not be expanded inline as _GetOrderPure is a behemoth of a fct void _FillIDList(const CLSID *pclsid, IDLREGITEM *pidlr) { pidlr->idri.cb = sizeof(pidlr->idri) + (WORD)_cbPadding; pidlr->idri.bFlags = _bFlags; _SetPIDLRCLSID(pidlr, pclsid); pidlr->idri.bOrder = _GetOrderPure((LPCIDLREGITEM)pidlr); }; BOOL _IsDesktop() { return ILIsEmpty(_GetFolderIDList()); } int _MapToOuterColNo(int iCol); // CompareIDs Helpers int _CompareIDsOriginal(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); int _CompareIDsFolderFirst(UINT iColumn, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); int _CompareIDsAlphabetical(UINT iColumn, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); BOOL _IsFolder(LPCITEMIDLIST pidl); HRESULT _CreateViewObjectFor(LPCIDLREGITEM pidlr, HWND hwnd, REFIID riid, void **ppv, BOOL bOneLevel); private: inline UNALIGNED const CLSID& _GetPIDLRCLSID(LPCIDLREGITEM pidlr); inline void _SetPIDLRCLSID(LPIDLREGITEM pidlr, const CLSID *pclsid); inline IDLREGITEM* _CreateAndFillIDLREGITEM(const CLSID *pclsid); BOOL _GetNameFromCache(REFCLSID rclsid, DWORD dwFlags, LPTSTR pszName, UINT cchName); inline void _ClearNameFromCache(); void _SaveNameInCache(REFCLSID rclsid, DWORD dwFlags, LPTSTR pszName); private: enum { c_dwSignature = 0x38394450 }; // "PD98" - PowerDesk 98 hack DWORD _dwSignature; LPTSTR _pszMachine; LPITEMIDLIST _pidl; IShellFolder2 *_psfOuter; IShellIconOverlay *_psioOuter; IPersistFreeThreadedObject *_pftoReg; // cached pointer of last free threaded bind int _iTypeOuter; // default sort order for outer items LPCTSTR _pszRegKey; LPCTSTR _pszSesKey; REGITEMSPOLICY* _pPolicy; TCHAR _chRegItem; // parsing prefix, must be TEXT(':') BYTE _bFlags; // flags field for PIDL construction DWORD _dwDefAttributes; // default attributes for items int _nRequiredItems; // # of required items DWORD _dwSortAttrib; // sorting attributes DWORD _cbPadding; // see comment in views.h BYTE _bFlagsLegacy; // see comment in views.h CLSID _clsidAttributesCache; ULONG _dwAttributesCache; ULONG _dwAttributesCacheValid; LONG _lNameCacheInterlock; DWORD _dwNameCacheTime; CLSID _clsidNameCache; DWORD _dwFlagsNameCache; TCHAR _szNameCache[64]; REQREGITEM *_aReqItems; IPersistFreeThreadedObject *_pftoDelegate; CRITICAL_SECTION _cs; BOOL _fcs; friend DWORD CALLBACK _RegFolderPropThreadProc(void *pv); friend HRESULT CRegFolder_CreateInstance(REGITEMSINFO *pri, IUnknown *punkOuter, REFIID riid, void **ppv); friend CRegFolderEnum; }; class CRegFolderEnum : public CObjectWithSite, IEnumIDList { public: // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // IEnumIDList STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched); STDMETHODIMP Skip(ULONG celt) { return E_NOTIMPL; }; STDMETHODIMP Reset(); STDMETHODIMP Clone(IEnumIDList **ppenum) { return E_NOTIMPL; }; // IObjectWithSite STDMETHODIMP SetSite(IUnknown* punkSite); // we need to override this protected: CRegFolderEnum(CRegFolder* prf, DWORD grfFlags, IEnumIDList* pesf, HDCA dcaItems, HDCA dcaDel, REGITEMSPOLICY* pPolicy); ~CRegFolderEnum(); BOOL _IsRestricted(); BOOL _WrongMachine(); BOOL _TestFolderness(DWORD dwAttribItem); BOOL _TestHidden(LPCIDLREGITEM pidlRegItem); BOOL _TestHiddenInWebView(LPCLSID clsidRegItem); BOOL _TestHiddenInDomain(LPCLSID clsidRegItem); private: LONG _cRef; CRegFolder* _prf; // reg item folder IEnumIDList* _peidl; DWORD _grfFlags; // guy we are wrapping REGITEMSPOLICY* _pPolicy; // controls what items are visible HDCA _hdca; // DCA of regitem objects INT _iCur; HDCA _hdcaDel; // delegate shell folder; INT _iCurDel; // index into the delegate folder DCA. IEnumIDList *_peidlDel; // delegate folder enumerator friend CRegFolder; }; STDAPI CDelegateMalloc_Create(void *pv, SIZE_T cbSize, WORD wOuter, IMalloc **ppmalloc); HRESULT ShowHideIconOnlyOnDesktop(const CLSID *pclsid, int StartIndex, int EndIndex, BOOL fHide); // // Construction / Destruction and aggregation // CRegFolder::CRegFolder(IUnknown *punkOuter) : _dwSignature(c_dwSignature), CAggregatedUnknown(punkOuter), _pidl(NULL), _pszMachine(NULL), _psfOuter(NULL), _lNameCacheInterlock(-1) { DllAddRef(); } CRegFolder::~CRegFolder() { IUnknown *punkCached = (IUnknown *)InterlockedExchangePointer((void**)&_pftoReg, NULL); if (punkCached) punkCached->Release(); punkCached = (IUnknown *)InterlockedExchangePointer((void**)&_pftoDelegate, NULL); if (punkCached) punkCached->Release(); ILFree(_pidl); Str_SetPtr(&_pszMachine, NULL); LocalFree(_aReqItems); SHReleaseOuterInterface(_GetOuter(), (IUnknown **)&_psfOuter); // release _psfOuter SHReleaseOuterInterface(_GetOuter(), (IUnknown **)&_psioOuter); if (_fcs) { DeleteCriticalSection(&_cs); } DllRelease(); } HRESULT CRegFolder::v_InternalQueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENTMULTI(CRegFolder, IShellFolder, IShellFolder2), // IID_IShellFolder QITABENT(CRegFolder, IShellFolder2), // IID_IShellFolder2 QITABENT(CRegFolder, IShellIconOverlay), // IID_IShellIconOverlay { 0 }, }; HRESULT hr = QISearch(this, qit, riid, ppv); if (FAILED(hr) && IsEqualIID(CLSID_RegFolder, riid)) { *ppv = this; // not ref counted hr = S_OK; } return hr; } // // get the pidl used to initialize this namespace // LPCITEMIDLIST CRegFolder::_GetFolderIDList() { if (!_pidl) SHGetIDListFromUnk(_psfOuter, &_pidl); return _pidl; } // // check to see if this pidl is a regitem // LPCIDLREGITEM CRegFolder::_IsReg(LPCITEMIDLIST pidl) { if (pidl && !ILIsEmpty(pidl)) { LPCIDLREGITEM pidlr = (LPCIDLREGITEM)pidl; if ((pidlr->idri.bFlags == _bFlags) && ((pidl->mkid.cb >= (sizeof(pidlr->idri) + (WORD)_cbPadding)) || _IsDelegate(pidlr))) { return pidlr; } else if (_cbPadding && _bFlagsLegacy && (pidlr->idri.bFlags == _bFlagsLegacy)) { // We needed to add padding to the Control Panel regitems. There was CP // regitems out there without the padding. If there is padding and we fail // the above case then maybe we are dealing with one of these. (stephstm) return pidlr; } } return NULL; } PDELEGATEITEMID CRegFolder::_IsDelegate(LPCIDLREGITEM pidlr) { PDELEGATEITEMID pidld = (PDELEGATEITEMID)pidlr; // save casting below if ((pidld->cbSize > sizeof(*pidld)) && /* note, (int) casts needed to force signed evaluation as we need the < 0 case */ (((int)pidld->cbSize - (int)pidld->cbInner) >= (int)sizeof(DELEGATEITEMDATA))) { PDELEGATEITEMDATA pdeidl = (PDELEGATEITEMDATA)&pidld->rgb[pidld->cbInner]; const CLSID clsid = pdeidl->clsidSignature; // alignment if (IsEqualGUID(clsid, CLSID_DelegateItemSig)) { return pidld; } } return NULL; } BOOL CRegFolder::_AllDelegates(UINT cidl, LPCITEMIDLIST *apidl, IShellFolder **ppsf) { *ppsf = NULL; PDELEGATEITEMID pidld = NULL; CLSID clsid, clsidFirst; for (UINT i = 0; i < cidl; i++) { LPCIDLREGITEM pidlr = _IsReg(apidl[i]); if (pidlr) { pidld = _IsDelegate(pidlr); if (pidld) { if (i == 0) { // get the clsid of the first guy clsidFirst = _GetPIDLRCLSID(pidlr); } else if (clsid = _GetPIDLRCLSID(pidlr), clsidFirst != clsid) { pidld = NULL; // not from the same delegate break; } } else { break; } } else { pidld = NULL; break; } } return pidld && SUCCEEDED(_GetDelegateFolder(pidld, IID_PPV_ARG(IShellFolder, ppsf))); } __inline IPersistFreeThreadedObject *ExchangeFTO(IPersistFreeThreadedObject **ppfto, IPersistFreeThreadedObject *pfto) { return (IPersistFreeThreadedObject *)InterlockedExchangePointer((void**)ppfto, pfto); } HRESULT CRegFolder::_CreateDelegateFolder(const CLSID* pclsid, REFIID riid, void **ppv) { HRESULT hr; *ppv = NULL; // try using the cached delegate (if it exists) IPersistFreeThreadedObject *pfto = ExchangeFTO(&_pftoDelegate, NULL); if (pfto) { CLSID clsidT; if (SUCCEEDED(pfto->GetClassID(&clsidT)) && IsEqualGUID(clsidT, *pclsid)) { // if this fails, ppv will still be NULL // so we will create a new cache item... hr = pfto->QueryInterface(riid, ppv); } } if (NULL == *ppv) { IDelegateFolder *pdel; hr = SHExtCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IDelegateFolder, &pdel)); if (SUCCEEDED(hr)) { DELEGATEITEMDATA delid = { 0 }; delid.clsidSignature = CLSID_DelegateItemSig; delid.clsidShellFolder = *pclsid; IMalloc *pm; hr = CDelegateMalloc_Create(&delid, sizeof(delid), _bFlags, &pm); if (SUCCEEDED(hr)) { hr = pdel->SetItemAlloc(pm); if (SUCCEEDED(hr)) { IPersistFolder *ppf; if (SUCCEEDED(pdel->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf)))) { hr = ppf->Initialize(_GetFolderIDList()); ppf->Release(); } if (SUCCEEDED(hr)) hr = pdel->QueryInterface(riid, ppv); } pm->Release(); } // now, we might be able to cache this guy if he is "free threaded" if (SUCCEEDED(hr)) { if (pfto) { pfto->Release(); pfto = NULL; } if (SUCCEEDED(pdel->QueryInterface(IID_PPV_ARG(IPersistFreeThreadedObject, &pfto)))) { SHPinDllOfCLSID(pclsid); } } pdel->Release(); } } if (pfto) { pfto = ExchangeFTO(&_pftoDelegate, pfto); if (pfto) pfto->Release(); // protect against race condition or re-entrancy } return hr; } HRESULT CRegFolder::_GetDelegateFolder(PDELEGATEITEMID pidld, REFIID riid, void **ppv) { PDELEGATEITEMDATA pdeidl = (PDELEGATEITEMDATA)&pidld->rgb[pidld->cbInner]; CLSID clsid = pdeidl->clsidShellFolder; // alignment return _CreateDelegateFolder(&clsid, riid, ppv); } // // returns a ~REFERENCE~ to the CLSID in the pidlr. HintHint: the ref has // the same scope as the pidlr. This is to replace the pidlr->idri.clsid // usage. (stephstm) // UNALIGNED const CLSID& CRegFolder::_GetPIDLRCLSID(LPCIDLREGITEM pidlr) { #ifdef DEBUG if (_cbPadding && (_bFlagsLegacy != pidlr->idri.bFlags)) { LPIDREGITEMEX pidriex = (LPIDREGITEMEX)&(pidlr->idri); for (DWORD i = 0; i < _cbPadding; ++i) { ASSERT(0 == pidriex->rgbPadding[i]); } } #endif PDELEGATEITEMID pidld = _IsDelegate(pidlr); if (pidld) { PDELEGATEITEMDATA pdeidl = (PDELEGATEITEMDATA)&pidld->rgb[pidld->cbInner]; return pdeidl->clsidShellFolder; } return (pidlr->idri.bFlags != _bFlagsLegacy) ? // return the new padded clsid ((UNALIGNED CLSID&)((LPIDREGITEMEX)&(pidlr->idri))->rgbPadding[_cbPadding]) : // return the old non-padded clsid (pidlr->idri.clsid); } // This fct is called only for IDREGITEMs created within this file. It is not // called for existing PIDL, so we do not need to check if it is a legacy pidl. void CRegFolder::_SetPIDLRCLSID(LPIDLREGITEM pidlr, const CLSID *pclsid) { LPIDREGITEMEX pidriex = (LPIDREGITEMEX)&(pidlr->idri); ((UNALIGNED CLSID&)pidriex->rgbPadding[_cbPadding]) = *pclsid; ZeroMemory(pidriex->rgbPadding, _cbPadding); } IDLREGITEM* CRegFolder::_CreateAndFillIDLREGITEM(const CLSID *pclsid) { IDLREGITEM* pidlRegItem = (IDLREGITEM*)_ILCreate(sizeof(IDLREGITEM) + _cbPadding); if (pidlRegItem) { _FillIDList(pclsid, pidlRegItem); } return pidlRegItem; } // // Returns: ptr to the first reg item if there are any // LPCIDLREGITEM CRegFolder::_AnyRegItem(UINT cidl, LPCITEMIDLIST apidl[]) { for (UINT i = 0; i < cidl; i++) { LPCIDLREGITEM pidlr = _IsReg(apidl[i]); if (pidlr) return pidlr; } return NULL; } int CRegFolder::_ReqItemIndex(LPCIDLREGITEM pidlr) { const CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment for (int i = _nRequiredItems - 1; i >= 0; i--) { if (IsEqualGUID(clsid, *_aReqItems[i].pclsid)) { break; } } return i; } // // a sort order of 0 means there is not specified sort order for this item // BYTE CRegFolder::_GetOrderPure(LPCIDLREGITEM pidlr) { BYTE bRet; int i = _ReqItemIndex(pidlr); if (i != -1) { bRet = _aReqItems[i].bOrder; } else { HKEY hkey; TCHAR szKey[MAX_PATH], szCLSID[GUIDSTR_MAX]; HRESULT hr; SHStringFromGUID(_GetPIDLRCLSID(pidlr), szCLSID, ARRAYSIZE(szCLSID)); bRet = 128; // default for items that do not register a SortOrderIndex hr = StringCchPrintf(szKey, ARRAYSIZE(szKey), TEXT("CLSID\\%s"), szCLSID); if (SUCCEEDED(hr)) { if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, szKey, NULL, KEY_QUERY_VALUE, &hkey)) { DWORD dwOrder, cbSize = sizeof(dwOrder); if (SHQueryValueEx(hkey, TEXT("SortOrderIndex"), NULL, NULL, (BYTE *)&dwOrder, &cbSize) == ERROR_SUCCESS) { // B#221890 - PowerDesk assumes that it can do this: // Desktop -> First child -> Third child // and it will get the C: drive. This means that // My Computer must be the first regitem. So any items // in front of My Computer are put immediately behind. if ((SHGetAppCompatFlags(ACF_MYCOMPUTERFIRST) & ACF_MYCOMPUTERFIRST) && dwOrder <= SORT_ORDER_DRIVES) dwOrder = SORT_ORDER_DRIVES + 1; bRet = (BYTE)dwOrder; } RegCloseKey(hkey); } } } return bRet; } BYTE CRegFolder::_GetOrder(LPCIDLREGITEM pidlr) { if (!_IsDelegate(pidlr)) { // If the bOrder values are less than 0x40, then they are the old values // Therefore compute the new bOrder values for these cases. if (pidlr->idri.bOrder <= 0x40) return _GetOrderPure(pidlr); else return pidlr->idri.bOrder; } else return 128; } // // validate that this item exists in this name space (look in the registry) // BOOL CRegFolder::_IsInNameSpace(LPCIDLREGITEM pidlr) { TCHAR szKeyName[MAX_PATH]; if (_IsDelegate(pidlr)) return FALSE; // its a delegate, therefore by default its transient if (_ReqItemIndex(pidlr) >= 0) return TRUE; // HACKHACK: we will return TRUE for Printers, N/W connections and Scheduled tasks // since they've been moved from My Computer to Control Panel and they // don't really care where they live const CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment if (IsEqualGUID(CLSID_Printers, clsid) || IsEqualGUID(CLSID_NetworkConnections, clsid) || IsEqualGUID(CLSID_ScheduledTasks, clsid)) { return TRUE; } _GetNameSpaceKey(pidlr, szKeyName, ARRAYSIZE(szKeyName)); // Note that we do not look in the session key, // since it by definition is transient return SHRegSubKeyExists(HKEY_LOCAL_MACHINE, szKeyName) || SHRegSubKeyExists(HKEY_CURRENT_USER, szKeyName); } // // The "Session key" is a volatile registry key unique to this session. // A session is a single continuous logon. If Explorer crashes and is // auto-restarted, the two Explorers share the same session. But if you // log off and back on, that new Explorer is a new session. // // // The s_SessionKeyName is the name of the session key relative to // REGSTR_PATH_EXPLORER\SessionInfo. On NT, this is normally the // Authentication ID, but we pre-initialize it to something safe so // we don't fault if for some reason we can't get to it. Since // Win95 supports only one session at a time, it just stays at the // default value. // // Sometimes we want to talk about the full path (SessionInfo\BlahBlah) // and sometimes just the partial path (BlahBlah) so we wrap it inside // this goofy structure. // union SESSIONKEYNAME { TCHAR szPath[12+16+1]; struct { TCHAR szSessionInfo[12]; // strlen("SessionInfo\\") TCHAR szName[16+1]; // 16 = two DWORDs converted to hex }; } s_SessionKeyName = { { TEXT("SessionInfo\\.Default") } }; BOOL g_fHaveSessionKeyName = FALSE; // // samDesired = a registry security access mask, or the special value // 0xFFFFFFFF to delete the session key. // phk = receives the session key on success // // NOTE! Only Explorer should delete the session key (when the user // logs off). // STDAPI SHCreateSessionKey(REGSAM samDesired, HKEY *phk) { LONG lRes; *phk = NULL; if (!g_fHaveSessionKeyName) { ENTERCRITICAL; // // Build the name of the session key. We use the authentication ID // which is guaranteed to be unique forever. We can't use the // Hydra session ID since that can be recycled. // // Note: Do not use OpenThreadToken since it will fail if the // thread is not impersonating. People who do impersonation // cannot use SHCreateSessionKey anyway since we cache the // session key on the assumption that there is no impersonation // going on. (And besides, HKCU is wrong when impersonating.) // HANDLE hToken; if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { TOKEN_STATISTICS stats; DWORD cbOut; if (GetTokenInformation(hToken, TokenStatistics, &stats, sizeof(stats), &cbOut)) { StringCchPrintf(s_SessionKeyName.szName, ARRAYSIZE(s_SessionKeyName.szName), // ok to truncate TEXT("%08x%08x"), stats.AuthenticationId.HighPart, stats.AuthenticationId.LowPart); // should never truncate g_fHaveSessionKeyName = TRUE; } CloseHandle(hToken); } LEAVECRITICAL; } HKEY hkExplorer = SHGetShellKey(SHELLKEY_HKCU_EXPLORER, NULL, TRUE); if (hkExplorer) { if (samDesired != 0xFFFFFFFF) { DWORD dwDisposition; lRes = RegCreateKeyEx(hkExplorer, s_SessionKeyName.szPath, 0, NULL, REG_OPTION_VOLATILE, samDesired, NULL, phk, &dwDisposition); } else { lRes = SHDeleteKey(hkExplorer, s_SessionKeyName.szPath); } RegCloseKey(hkExplorer); } else { lRes = ERROR_ACCESS_DENIED; } return HRESULT_FROM_WIN32(lRes); } // // We use a HDCA to store the CLSIDs for the regitems in this folder, this call returns // that HDCA> // HDCA CRegFolder::_ItemArray() { HDCA hdca = DCA_Create(); if (hdca) { for (int i = 0; i < _nRequiredItems; i++) { DCA_AddItem(hdca, *_aReqItems[i].pclsid); } DCA_AddItemsFromKey(hdca, HKEY_LOCAL_MACHINE, _pszRegKey); DCA_AddItemsFromKey(hdca, HKEY_CURRENT_USER, _pszRegKey); if (_pszSesKey) { HKEY hkSession; if (SUCCEEDED(SHCreateSessionKey(KEY_QUERY_VALUE, &hkSession))) { DCA_AddItemsFromKey(hdca, hkSession, _pszSesKey); RegCloseKey(hkSession); } } } return hdca; } HDCA CRegFolder::_DelItemArray() { HDCA hdca = DCA_Create(); if (hdca) { TCHAR szKey[MAX_PATH*2]; HRESULT hr = StringCchPrintf(szKey, ARRAYSIZE(szKey), TEXT("%s\\DelegateFolders"), _pszRegKey); if (SUCCEEDED(hr)) { DCA_AddItemsFromKey(hdca, HKEY_LOCAL_MACHINE, szKey); DCA_AddItemsFromKey(hdca, HKEY_CURRENT_USER, szKey); } if (_pszSesKey) { HKEY hkSession; if (SUCCEEDED(SHCreateSessionKey(KEY_QUERY_VALUE, &hkSession))) { hr = StringCchPrintf(szKey, ARRAYSIZE(szKey), TEXT("%s\\DelegateFolders"), _pszSesKey); if (SUCCEEDED(hr)) { DCA_AddItemsFromKey(hdca, hkSession, szKey); } RegCloseKey(hkSession); } } } return hdca; } // // Given our cached machine name, attempt get the object on that machine. // HRESULT CRegFolder::_InitFromMachine(IUnknown *punk, BOOL bEnum) { HRESULT hr = S_OK; if (_pszMachine) { // prior to Win2K there was IRemoteComputerA/W, we removed IRemoteComputerA and // made IRemoteComputer map to the W version of the API, therefore we // thunk the string to its wide version before calling the Initialize method. (daviddv 102099) IRemoteComputer * premc; hr = punk->QueryInterface(IID_PPV_ARG(IRemoteComputer, &premc)); if (SUCCEEDED(hr)) { WCHAR wszName[MAX_PATH]; SHTCharToUnicode(_pszMachine, wszName, ARRAYSIZE(wszName)); hr = premc->Initialize(wszName, bEnum); premc->Release(); } } return hr; } // // Given a pidl, lets get an instance of the namespace that provides it. // - handles caching accordingly // HRESULT CRegFolder::_CreateAndInit(LPCIDLREGITEM pidlr, LPBC pbc, REFIID riid, void **ppv) { *ppv = NULL; HRESULT hr = E_FAIL; PDELEGATEITEMID pidld = _IsDelegate(pidlr); if (pidld) { IShellFolder *psf; hr = _GetDelegateFolder(pidld, IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { hr = psf->BindToObject((LPCITEMIDLIST)pidlr, pbc, riid, ppv); psf->Release(); } } else { CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment // try using the cached pointer IPersistFreeThreadedObject *pfto = ExchangeFTO(&_pftoReg, NULL); if (pfto) { CLSID clsidT; if (SUCCEEDED(pfto->GetClassID(&clsidT)) && IsEqualGUID(clsidT, clsid)) { // if this fails, ppv will still be NULL // so we will create a new cache item... hr = pfto->QueryInterface(riid, ppv); } } // cache failed, cocreate it ourself if (NULL == *ppv) { OBJCOMPATFLAGS ocf = SHGetObjectCompatFlags(NULL, &clsid); if (!(OBJCOMPATF_UNBINDABLE & ocf)) { // // HACKHACK - some regitems can only be CoCreated with IID_IShellFolder // specifically the hummingbird shellext will DebugBreak() bringing // down the shell... but we can CoCreate() and then QI after... // hr = SHExtCoCreateInstance(NULL, &clsid, NULL, (OBJCOMPATF_COCREATESHELLFOLDERONLY & ocf) ? IID_IShellFolder : riid , ppv); if (SUCCEEDED(hr)) { IUnknown *punk = (IUnknown *)*ppv; // avoid casts below if ((OBJCOMPATF_COCREATESHELLFOLDERONLY & ocf)) { hr = punk->QueryInterface(riid, ppv); punk->Release(); punk = (IUnknown *)*ppv; // avoid casts below } if (SUCCEEDED(hr)) { hr = _InitFromMachine(punk, FALSE); if (SUCCEEDED(hr)) { IPersistFolder *ppf; if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf)))) { LPITEMIDLIST pidlAbs = ILCombine(_GetFolderIDList(), (LPCITEMIDLIST)pidlr); if (pidlAbs) { hr = ppf->Initialize(pidlAbs); ILFree(pidlAbs); } else { hr = E_OUTOFMEMORY; } ppf->Release(); } if (SUCCEEDED(hr)) { if (pfto) { pfto->Release(); // we are going to replace the cache pfto = NULL; } if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IPersistFreeThreadedObject, &pfto)))) { SHPinDllOfCLSID(&clsid); } } } if (FAILED(hr)) { // we're going to return failure -- don't leak the object we created punk->Release(); *ppv = NULL; } } } } } // recache the pfto if (pfto) { pfto = ExchangeFTO(&_pftoReg, pfto); if (pfto) pfto->Release(); // protect against race condition or re-entrancy } } return hr; } // // lets the reg item itself pick off the GetDisplayNameOf() impl for itself. this lets // MyDocs on the desktop return c:\win\profile\name\My Documents as it's parsing name // // returns: // S_FALSE do normal parsing, reg item did not handel // // HRESULT CRegFolder::_GetDisplayNameFromSelf(LPCIDLREGITEM pidlr, DWORD dwFlags, LPTSTR pszName, UINT cchName) { HRESULT hr = S_FALSE; // normal case const CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment BOOL bGetFromSelf = FALSE; const BOOL bForParsingOnly = ((dwFlags & (SHGDN_FORADDRESSBAR | SHGDN_INFOLDER | SHGDN_FORPARSING)) == SHGDN_FORPARSING); if (bForParsingOnly) { if (SHQueryShellFolderValue(&clsid, TEXT("WantsFORPARSING"))) { bGetFromSelf = TRUE; } } else { const BOOL bForParsing = (0 != (dwFlags & SHGDN_FORPARSING)); const BOOL bForAddressBar = (0 != (dwFlags & SHGDN_FORADDRESSBAR)); if (!bForParsing || bForAddressBar) { if (SHQueryShellFolderValue(&clsid, TEXT("WantsFORDISPLAY"))) { bGetFromSelf = TRUE; } } } if (bGetFromSelf) { IShellFolder *psf; if (SUCCEEDED(_BindToItem(pidlr, NULL, IID_PPV_ARG(IShellFolder, &psf), TRUE))) { // // Pass NULL pidl (c_idlDesktop) to get display name of the folder itself. // Note that we can't use DisplayNameOf() because that function // eats the S_FALSE that can be returned from the // IShellFolder::GetDisplayNameOf implementation to indicate // "do the default thing". // STRRET sr; hr = psf->GetDisplayNameOf(&c_idlDesktop, dwFlags, &sr); if (S_OK == hr) { hr = StrRetToBuf(&sr, &c_idlDesktop, pszName, cchName); } psf->Release(); } } return hr; } // // Managing the name cache is tricky because it is hit frequently on // multiple threads, and collisions are frequent. E.g., one thread // does a SetNameOf+SHChangeNotify, and multiple other threads try // to pull the name out simultaneously. If you're not careful, these // threads step on each other and some poor schmuck gets invalid data. // // _lNameCacheInterlock = -1 if nobody is using the name cache, else >= 0 // // Therefore, if InterlockedIncrement(&_lNameCacheInterlock) == 0, then // you are the sole owner of the cache. Otherwise, the cache is busy // and you should get out. // // Furthermore, we don't use the name cache if it is more than 500ms stale. // BOOL CRegFolder::_GetNameFromCache(REFCLSID rclsid, DWORD dwFlags, LPTSTR pszName, UINT cchName) { BOOL fSuccess = FALSE; // Quick check to avoid entering the interlock unnecessarily if (rclsid.Data1 == _clsidNameCache.Data1 && GetTickCount() - _dwNameCacheTime < 500) { if (InterlockedIncrement(&_lNameCacheInterlock) == 0) { if (IsEqualGUID(rclsid, _clsidNameCache) && (_dwFlagsNameCache == dwFlags)) { StringCchCopy(pszName, cchName, _szNameCache); // ok to truncate fSuccess = TRUE; } } InterlockedDecrement(&_lNameCacheInterlock); } return fSuccess; } void CRegFolder::_ClearNameFromCache() { _clsidNameCache = CLSID_NULL; } void CRegFolder::_SaveNameInCache(REFCLSID rclsid, DWORD dwFlags, LPTSTR pszName) { if (lstrlen(pszName) < ARRAYSIZE(_szNameCache)) { if (InterlockedIncrement(&_lNameCacheInterlock) == 0) { StringCchCopy(_szNameCache, ARRAYSIZE(_szNameCache), pszName); // ok to truncate _dwFlagsNameCache = dwFlags; _clsidNameCache = rclsid; _dwNameCacheTime = GetTickCount(); } InterlockedDecrement(&_lNameCacheInterlock); } } // // Given a pidl in the regitms folder, get the friendly name for this (trying the user // store ones, then the global one). // #define GUIDSIZE 50 HRESULT CRegFolder::_GetDisplayName(LPCIDLREGITEM pidlr, DWORD dwFlags, LPTSTR pszName, UINT cchName) { *pszName = 0; PDELEGATEITEMID pidld = _IsDelegate(pidlr); if (pidld) { IShellFolder *psf; HRESULT hr = _GetDelegateFolder(pidld, IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { hr = DisplayNameOf(psf, (LPCITEMIDLIST)pidlr, dwFlags, pszName, cchName); psf->Release(); } return hr; } else { HKEY hkCLSID; CLSID clsid = _GetPIDLRCLSID(pidlr); if (_GetNameFromCache(clsid, dwFlags, pszName, cchName)) { // Satisfied from cache; all done! } else { HRESULT hr = _GetDisplayNameFromSelf(pidlr, dwFlags, pszName, cchName); if (hr != S_FALSE) return hr; if (dwFlags & SHGDN_FORPARSING) { if (!(dwFlags & SHGDN_FORADDRESSBAR)) { // Get the parent folder name TCHAR szParentName[MAX_PATH]; szParentName[0] = 0; if (!(dwFlags & SHGDN_INFOLDER) && !ILIsEmpty(_GetFolderIDList())) { SHGetNameAndFlags(_GetFolderIDList(), SHGDN_FORPARSING, szParentName, SIZECHARS(szParentName), NULL); hr = StringCchCat(szParentName, ARRAYSIZE(szParentName), TEXT("\\")); if (FAILED(hr)) { return hr; } } // Win95 didn't support SHGDN_FORPARSING on regitems; it always // returned the display name. Norton Unerase relies on this, // because it assumes that if the second character of the FORPARSING // name is a colon, then it's a drive. Therefore, we can't return // ::{guid} or Norton will fault. (I guess they didn't believe in // SFGAO_FILESYSTEM.) So if we are Norton Unerase, then ignore // the FORPARSING flag; always get the name for display. // And good luck to any user who sets his computer name to something // with a colon as the second character... if (SHGetAppCompatFlags(ACF_OLDREGITEMGDN) & ACF_OLDREGITEMGDN) { // In Application compatibility mode turn SHGDN_FORPARSING // off and fall thru to the remainder of the function which // avoids the ::{GUID} when required. dwFlags &= ~SHGDN_FORPARSING; } else { // Get this reg folder name TCHAR szFolderName[GUIDSIZE + 2]; szFolderName[0] = szFolderName[1] = _chRegItem; SHStringFromGUID(clsid, szFolderName + 2, cchName - 2); // Copy the full path into szParentName. hr = StringCchCat(szParentName, ARRAYSIZE(szParentName), szFolderName); if (FAILED(hr)) { return hr; } // Copy the full path into the output buffer. return StringCchCopy(pszName, cchName, szParentName); } } } // Check per-user settings first... if ((*pszName == 0) && SUCCEEDED(SHRegGetCLSIDKey(clsid, NULL, TRUE, FALSE, &hkCLSID))) { DWORD cbName = cchName * sizeof(TCHAR); if (ERROR_SUCCESS != SHRegGetValue(hkCLSID, NULL, NULL, SRRF_RT_REG_SZ, NULL, pszName, &cbName)) *pszName = 0; RegCloseKey(hkCLSID); } // If we have to, use per-machine settings... if (*pszName == 0) { _GetClassKeys(pidlr, &hkCLSID, NULL); if (hkCLSID) { SHLoadLegacyRegUIString(hkCLSID, NULL, pszName, cchName); RegCloseKey(hkCLSID); } } // try the required item names, they might not be in the registry if (*pszName == 0) { int iItem = _ReqItemIndex(pidlr); if (iItem >= 0) LoadString(HINST_THISDLL, _aReqItems[iItem].uNameID, pszName, cchName); } if (*pszName) { if (_pszMachine && !(dwFlags & SHGDN_INFOLDER)) { // szName now holds the item name, and _pszMachine holds the machine. LPTSTR pszRet = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(IDS_DSPTEMPLATE_WITH_ON), SkipServerSlashes(_pszMachine), pszName); if (pszRet) { StringCchCopy(pszName, cchName, pszRet); // ok to truncate LocalFree(pszRet); } } _SaveNameInCache(clsid, dwFlags, pszName); } } } return *pszName ? S_OK : E_FAIL; } // // get the HKEYs that map to the regitm. // // NOTE: this function returns a void so that the caller explicitly must check the keys // to see if they are non-null before using them. // void CRegFolder::_GetClassKeys(LPCIDLREGITEM pidlr, HKEY* phkCLSID, HKEY* phkBase) { HRESULT hr; IQueryAssociations *pqa; if (phkCLSID) *phkCLSID = NULL; if (phkBase) *phkBase = NULL; hr = _AssocCreate(pidlr, IID_PPV_ARG(IQueryAssociations, &pqa)); if (SUCCEEDED(hr)) { if (phkCLSID) { hr = pqa->GetKey(0, ASSOCKEY_CLASS, NULL, phkCLSID); ASSERT((SUCCEEDED(hr) && *phkCLSID) || (FAILED(hr) && (*phkCLSID == NULL))); } if (phkBase) { hr = pqa->GetKey(0, ASSOCKEY_BASECLASS, NULL, phkBase); ASSERT((SUCCEEDED(hr) && *phkBase) || (FAILED(hr) && (*phkBase == NULL))); } pqa->Release(); } } // {9EAC43C0-53EC-11CE-8230-CA8A32CF5494} static const GUID GUID_WINAMP = { 0x9eac43c0, 0x53ec, 0x11ce, { 0x82, 0x30, 0xca, 0x8a, 0x32, 0xcf, 0x54, 0x94 } }; #define SZ_BROKEN_WINAMP_VERB TEXT("OpenFileOrPlayList") // IQA - Move this to Legacy Mapper. void _MaybeDoWinAmpHack(UNALIGNED REFGUID rguid) { if (IsEqualGUID(rguid, GUID_WINAMP)) { // WinAmp writes in "OpenFileOrPlayList" as default value under shell, but they // don't write a corresponding "OpenFileorPlayList" verb key. So we need to whack // the registry into shape for them. Otherwise, they won't get the default verb // they want (due to an NT5 change in CDefExt_QueryContextMenu's behavior). TCHAR szCLSID[GUIDSTR_MAX]; SHStringFromGUID(rguid, szCLSID, ARRAYSIZE(szCLSID)); TCHAR szRegKey[GUIDSTR_MAX + 40]; HRESULT hr; hr = StringCchPrintf(szRegKey, ARRAYSIZE(szRegKey), TEXT("CLSID\\%s\\shell"), szCLSID); if (SUCCEEDED(hr)) { TCHAR szValue[ARRAYSIZE(SZ_BROKEN_WINAMP_VERB)+2]; DWORD dwType; DWORD dwSize = sizeof(szValue); if (SHGetValue(HKEY_CLASSES_ROOT, szRegKey, NULL, &dwType, szValue, &dwSize) == 0) { if (dwType == REG_SZ && lstrcmp(szValue, SZ_BROKEN_WINAMP_VERB) == 0) { // Make "open" the default verb SHSetValue(HKEY_CLASSES_ROOT, szRegKey, NULL, REG_SZ, TEXT("open"), sizeof(TEXT("open"))); } } } } } HRESULT CRegFolder::_AssocCreate(LPCIDLREGITEM pidlr, REFIID riid, void **ppv) { *ppv = NULL; IQueryAssociations *pqa; HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa)); if (SUCCEEDED(hr)) { WCHAR szCLSID[GUIDSTR_MAX]; const CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment ASSOCF flags = ASSOCF_INIT_NOREMAPCLSID; DWORD dwAttributes; if ((SUCCEEDED(_AttributesOf(pidlr, SFGAO_FOLDER, &dwAttributes)) && (dwAttributes & SFGAO_FOLDER)) && !SHQueryShellFolderValue(&clsid, TEXT("HideFolderVerbs"))) flags |= ASSOCF_INIT_DEFAULTTOFOLDER; SHStringFromGUIDW(clsid, szCLSID, ARRAYSIZE(szCLSID)); _MaybeDoWinAmpHack(clsid); hr = pqa->Init(flags, szCLSID, NULL, NULL); if (SUCCEEDED(hr)) hr = pqa->QueryInterface(riid, ppv); pqa->Release(); } return hr; } // // get the namespace key for this objec. // void CRegFolder::_GetNameSpaceKey(LPCIDLREGITEM pidlr, LPTSTR pszKeyName, UINT cchKeyName) { TCHAR szClass[GUIDSTR_MAX]; SHStringFromGUID(_GetPIDLRCLSID(pidlr), szClass, ARRAYSIZE(szClass)); StringCchPrintf(pszKeyName, cchKeyName, TEXT("%s\\%s"), _pszRegKey, szClass); // ok to truncate } BOOL CRegFolder::_CanDelete(LPCIDLREGITEM pidlr) { DWORD dwAttributes; return pidlr && SUCCEEDED(_AttributesOf(pidlr, SFGAO_CANDELETE, &dwAttributes)) && (dwAttributes & SFGAO_CANDELETE); } // // the user is trying to delete an object from a regitem folder, therefore // lets look in the IDataObject to see if that includes any regitems, if // so then handle their deletion, before passing to the outer guy to // handle the other objects. // #define MAX_REGITEM_WARNTEXT 1024 void CRegFolder::_Delete(HWND hwnd, UINT uFlags, IDataObject *pdtobj) { STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(pdtobj, &medium); if (pida) { TCHAR szItemWarning[MAX_REGITEM_WARNTEXT]; UINT nregfirst = (UINT)-1; UINT creg = 0; UINT cwarn = 0; UINT countfs = 0; LPCITEMIDLIST *ppidlFS = NULL; // calc number of regitems and index of first for (UINT i = 0; i < pida->cidl; i++) { LPCITEMIDLIST pidl = IDA_GetIDListPtr(pida, i); LPCIDLREGITEM pidlr = _IsReg(pidl); if (_CanDelete(pidlr)) { TCHAR szTemp[MAX_REGITEM_WARNTEXT]; creg++; if (nregfirst == (UINT)-1) nregfirst = i; // use a temporary here because _GetDeleteMessage clobbers the buffer // when it doesn't get a delete message --ccooney if ((cwarn < 2) && _GetDeleteMessage(pidlr, szTemp, ARRAYSIZE(szTemp))) { StringCchCopy(szItemWarning, ARRAYSIZE(szItemWarning), szTemp); // ok to truncate cwarn++; } } else if (!pidlr) //only do this for non reg items { // alloc an alternate array for FS pidls // for simplicitu over alloc in the case where there are reg items if (ppidlFS == NULL) ppidlFS = (LPCITEMIDLIST *)LocalAlloc(LPTR, pida->cidl * sizeof(LPCITEMIDLIST)); if (ppidlFS) { ppidlFS[countfs++] = pidl; } } } // // compose the confirmation message / ask the user / fry the items... // if (creg) { SHELLSTATE ss = {0}; SHGetSetSettings(&ss, SSF_NOCONFIRMRECYCLE, FALSE); if ((uFlags & CMIC_MASK_FLAG_NO_UI) || ss.fNoConfirmRecycle) { for (i = 0; i < pida->cidl; i++) { LPCIDLREGITEM pidlr = _IsReg(IDA_GetIDListPtr(pida, i)); if (_CanDelete(pidlr)) _DeleteRegItem(pidlr); } } else { TCHAR szItemName[MAX_PATH]; TCHAR szWarnText[1024 + MAX_REGITEM_WARNTEXT]; TCHAR szWarnCaption[128]; TCHAR szTemp[256]; MSGBOXPARAMS mbp = {sizeof(mbp), hwnd, HINST_THISDLL, szWarnText, szWarnCaption, MB_YESNO | MB_USERICON, MAKEINTRESOURCE(IDI_NUKEFILE), 0, NULL, 0}; // // so we can tell if we got these later // *szItemName = 0; *szWarnText = 0; // // if there is only one, retrieve its name // if (creg == 1) { LPCIDLREGITEM pidlr = _IsReg(IDA_GetIDListPtr(pida, nregfirst)); _GetDisplayName(pidlr, SHGDN_NORMAL, szItemName, ARRAYSIZE(szItemName)); } // // ask the question "are you sure..." // if ((pida->cidl == 1) && *szItemName) { TCHAR szTemp2[256]; LoadString(HINST_THISDLL, _IsDesktop() ? IDS_CONFIRMDELETEDESKTOPREGITEM : IDS_CONFIRMDELETEREGITEM, szTemp2, ARRAYSIZE(szTemp2)); StringCchPrintf(szTemp, ARRAYSIZE(szTemp), szTemp2, szItemName); // ok to truncate } else { LoadString(HINST_THISDLL, _IsDesktop() ? IDS_CONFIRMDELETEDESKTOPREGITEMS : IDS_CONFIRMDELETEREGITEMS, szTemp, ARRAYSIZE(szTemp)); } StringCchCat(szWarnText, ARRAYSIZE(szWarnText), szTemp); // ok to truncate // // if there is exactly one special warning message and one item total, add it in // if (creg == 1 && cwarn == 1 && *szItemWarning) { StringCchCat(szWarnText, ARRAYSIZE(szWarnText), TEXT("\r\n\n")); // ok to truncate StringCchCat(szWarnText, ARRAYSIZE(szWarnText), szItemWarning); // ok to truncate } else { if (creg == 1) { TCHAR szTemp2[256]; TCHAR szControlPanel[256]; LPCIDLREGITEM pidlr = _IsReg(IDA_GetIDListPtr(pida, nregfirst)); CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment int idString = (1 == pida->cidl) ? IDS_CANTRECYCLEREGITEMS_NAME : IDS_CANTRECYCLEREGITEMS_INCL_NAME; LoadString(HINST_THISDLL, idString, szTemp, ARRAYSIZE(szTemp)); if ((IsEqualCLSID(CLSID_NetworkPlaces, clsid)) || (IsEqualCLSID(CLSID_Internet, clsid)) || (IsEqualCLSID(CLSID_MyComputer, clsid)) || (IsEqualCLSID(CLSID_MyDocuments, clsid))) { LoadString(HINST_THISDLL, IDS_CANTRECYLE_FOLDER, szControlPanel, ARRAYSIZE(szControlPanel)); } else { LoadString(HINST_THISDLL, IDS_CANTRECYLE_GENERAL, szControlPanel, ARRAYSIZE(szControlPanel)); } StringCchCat(szWarnText, ARRAYSIZE(szWarnText), TEXT("\r\n\n")); // ok to truncate StringCchPrintf(szTemp2, ARRAYSIZE(szTemp2), szTemp, szControlPanel); // ok to truncate StringCchCat(szWarnText, ARRAYSIZE(szWarnText), szTemp2); // ok to truncate } // // otherwise, say "these items..." or "some of these items..." // else { TCHAR szTemp2[256]; TCHAR szControlPanel[256]; int idString = (creg == pida->cidl) ? IDS_CANTRECYCLEREGITEMS_ALL : IDS_CANTRECYCLEREGITEMS_SOME; LoadString(HINST_THISDLL, idString, szTemp, ARRAYSIZE(szTemp)); LoadString(HINST_THISDLL, IDS_CANTRECYLE_GENERAL, szControlPanel, ARRAYSIZE(szControlPanel)); StringCchCat(szWarnText, ARRAYSIZE(szWarnText), TEXT("\r\n\n")); // ok to truncate StringCchPrintf(szTemp2, ARRAYSIZE(szTemp2), szTemp, szControlPanel); // ok to truncate StringCchCat(szWarnText, ARRAYSIZE(szWarnText), szTemp2); // ok to truncate // // we just loaded a very vague message // don't confuse the user any more by adding random text // if these is a special warning, force it to show separately // if (cwarn == 1) cwarn++; } } // // finally, the message box caption (also needed in loop below) // LoadString(HINST_THISDLL, IDS_CONFIRMDELETE_CAPTION, szWarnCaption, ARRAYSIZE(szWarnCaption)); // make sure the user is cool with it if (MessageBoxIndirect(&mbp) == IDYES) { // go ahead and delete the reg items for (i = 0; i < pida->cidl; i++) { LPCIDLREGITEM pidlr = _IsReg(IDA_GetIDListPtr(pida, i)); if (_CanDelete(pidlr)) { if ((cwarn > 1) && _GetDeleteMessage(pidlr, szItemWarning, ARRAYSIZE(szItemWarning))) { if (FAILED(_GetDisplayName(pidlr, SHGDN_NORMAL, szItemName, ARRAYSIZE(szItemName)))) { StringCchCopy(szItemName, ARRAYSIZE(szItemName), szWarnCaption); // ok to truncate } MessageBox(hwnd, szItemWarning, szItemName, MB_OK | MB_ICONINFORMATION); } _DeleteRegItem(pidlr); } } } } } // now delete the fs objects if (ppidlFS) { SHInvokeCommandOnPidlArray(hwnd, NULL, (IShellFolder *)this, ppidlFS, countfs, uFlags, "delete"); LocalFree((HANDLE)ppidlFS); } HIDA_ReleaseStgMedium(pida, &medium); } } // // Delete a regitem given its pidl. // HRESULT CRegFolder::_DeleteRegItem(LPCIDLREGITEM pidlr) { if (_IsDelegate(pidlr)) return E_INVALIDARG; HRESULT hr = E_ACCESSDENIED; if (_CanDelete(pidlr)) { const CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment if (SHQueryShellFolderValue(&clsid, TEXT("HideOnDesktopPerUser"))) { // hide this icon only on desktop for StartPanel on (0) and off (1) hr = ShowHideIconOnlyOnDesktop(&clsid, 0, 1, TRUE); } else if (SHQueryShellFolderValue(&clsid, TEXT("HideAsDeletePerUser"))) { // clear the non enuerated bit to hide this item hr = _SetAttributes(pidlr, TRUE, SFGAO_NONENUMERATED, SFGAO_NONENUMERATED); } else if (SHQueryShellFolderValue(&clsid, TEXT("HideAsDelete"))) { // clear the non enuerated bit to hide this item hr = _SetAttributes(pidlr, FALSE, SFGAO_NONENUMERATED, SFGAO_NONENUMERATED); } else { // remove from the key to delete it TCHAR szKeyName[MAX_PATH]; _GetNameSpaceKey(pidlr, szKeyName, ARRAYSIZE(szKeyName)); if ((RegDeleteKey(HKEY_CURRENT_USER, szKeyName) == ERROR_SUCCESS) || (RegDeleteKey(HKEY_LOCAL_MACHINE, szKeyName) == ERROR_SUCCESS)) { hr = S_OK; } } if (SUCCEEDED(hr)) { // tell the world LPITEMIDLIST pidlAbs = ILCombine(_GetFolderIDList(), (LPCITEMIDLIST)pidlr); if (pidlAbs) { SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, pidlAbs, NULL); ILFree(pidlAbs); } } } return hr; } // // Get the prompt to be displayed if the user tries to delete the regitem, // this is stored both globally (HKLM) and as s user configured preference. // BOOL CRegFolder::_GetDeleteMessage(LPCIDLREGITEM pidlr, LPTSTR pszMsg, int cchMax) { HKEY hk; TCHAR szKeyName[MAX_PATH]; ASSERT(!_IsDelegate(pidlr)); *pszMsg = 0; _GetNameSpaceKey(pidlr, szKeyName, ARRAYSIZE(szKeyName)); if ((RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, NULL, KEY_QUERY_VALUE, &hk) == ERROR_SUCCESS) || (RegOpenKeyEx(HKEY_CURRENT_USER, szKeyName, NULL, KEY_QUERY_VALUE, &hk) == ERROR_SUCCESS)) { SHLoadRegUIString(hk, REGSTR_VAL_REGITEMDELETEMESSAGE, pszMsg, cchMax); RegCloseKey(hk); } return *pszMsg != 0; } HRESULT CRegFolder::_GetRegItemColumnFromRegistry(LPCIDLREGITEM pidlr, LPCTSTR pszColumnName, LPTSTR pszColumnData, int cchColumnData) { HKEY hkCLSID; HRESULT hr = E_FAIL; _GetClassKeys(pidlr, &hkCLSID, NULL); *pszColumnData = 0; // Default string if (hkCLSID) { // Use SHLoadRegUIString to allow the string to be localized if (SUCCEEDED(SHLoadRegUIString(hkCLSID, pszColumnName, pszColumnData, cchColumnData))) { hr = S_OK; } // FIXED kenwic 052699 #342955 RegCloseKey(hkCLSID); } return hr; } // // A more generic version of _GetRegItemColumnFromRegistry which takes a pidlr and a string // and finds the corresponding variant value from the registry. // // pidlr: pidl of the regitem, we open the registry key corresponding to its CLSID // pszColumnName: name of the value to took for under the opened key // pv: variant to return the value // HRESULT CRegFolder::_GetRegItemVariantFromRegistry(LPCIDLREGITEM pidlr, LPCTSTR pszColumnName, VARIANT *pv) { HKEY hkCLSID; HRESULT hr = E_FAIL; _GetClassKeys(pidlr, &hkCLSID, NULL); if (hkCLSID) { hr = GetVariantFromRegistryValue(hkCLSID, pszColumnName, pv); RegCloseKey(hkCLSID); } return hr; } // // App compat: McAfee Nuts & Bolts Quick Copy has the wrong function // signature for CreateViewObject. They implemented it as // // STDAPI CreateViewObject(HWND hwnd) { return S_OK; } // // so we must manually reset the stack after the call. // #ifdef _X86_ STDAPI SHAppCompatCreateViewObject(IShellFolder *psf, HWND hwnd, REFIID riid, void * *ppv) { HRESULT hr; _asm mov edi, esp hr = psf->CreateViewObject(hwnd, riid, ppv); _asm mov esp, edi // AppCompat - Undelete 2.0 returns S_OK for interfaces that it doesnt support // but they do correctly NULL the ppv out param so we check for that as well if (SUCCEEDED(hr) && !*ppv) hr = E_NOINTERFACE; return hr; } #else #define SHAppCompatCreateViewObject(psf, hwnd, riid, ppv) \ psf->CreateViewObject(hwnd, riid, ppv) #endif HRESULT CRegFolder::_CreateViewObjectFor(LPCIDLREGITEM pidlr, HWND hwnd, REFIID riid, void **ppv, BOOL bOneLevel) { IShellFolder *psf; HRESULT hr = _BindToItem(pidlr, NULL, IID_PPV_ARG(IShellFolder, &psf), bOneLevel); if (SUCCEEDED(hr)) { hr = SHAppCompatCreateViewObject(psf, hwnd, riid, ppv); psf->Release(); } else *ppv = NULL; return hr; } // Geta an infotip object for the namespace HRESULT CRegFolder::_GetInfoTip(LPCIDLREGITEM pidlr, void **ppv) { HKEY hkCLSID; HRESULT hr = E_FAIL; _GetClassKeys(pidlr, &hkCLSID, NULL); if (hkCLSID) { DWORD dwQuery, lLen = sizeof(dwQuery); // let the regitem code compute the info tip if it wants to... if (SHQueryValueEx(hkCLSID, TEXT("QueryForInfoTip"), NULL, NULL, (BYTE *)&dwQuery, &lLen) == ERROR_SUCCESS) { hr = _CreateViewObjectFor(pidlr, NULL, IID_IQueryInfo, ppv, TRUE); } else { hr = E_FAIL; } // fall back to reading it from the registry if (FAILED(hr)) { TCHAR szText[INFOTIPSIZE]; // Use SHLoadRegUIString to allow the info tip to be localized if (SUCCEEDED(SHLoadRegUIString(hkCLSID, TEXT("InfoTip"), szText, ARRAYSIZE(szText))) && szText[0]) { hr = CreateInfoTipFromText(szText, IID_IQueryInfo, ppv); //The InfoTip COM object } } RegCloseKey(hkCLSID); } return hr; } // there are 2 forms for parsing syntax we support // // to parse reg items in this folder // ::{clsid reg item} [\ optional extra stuff to parse] // // to parse items that might live in a delegate folder // ::{clsid delegate folder}, [\ optional extra stuff to parse] // // in both cases the optional remander stuff gets passed through to complete // the parse in that name space HRESULT CRegFolder::_ParseGUIDName(HWND hwnd, LPBC pbc, LPOLESTR pwzDisplayName, LPITEMIDLIST *ppidlOut, ULONG *pdwAttributes) { LPOLESTR pwzNext; LPOLESTR pwzDelegateInfo = NULL; // Note that we add 2 to skip the RegItem identifier characters pwzDisplayName += 2; // Skip to a '\\' for (pwzNext = pwzDisplayName; *pwzNext && *pwzNext != TEXT('\\'); pwzNext++) { // if we hit a ',' then eg, ::{GUID},stuff then we assume the stuff is for a delegate if ((*pwzNext == TEXT(',')) && !pwzDelegateInfo) { pwzDelegateInfo = pwzNext + 1; // skip comma } } CLSID clsid; HRESULT hr = SHCLSIDFromString(pwzDisplayName, &clsid); if (SUCCEEDED(hr)) { if (pwzDelegateInfo) { IShellFolder *psf; if (SUCCEEDED(_CreateDelegateFolder(&clsid, IID_PPV_ARG(IShellFolder, &psf)))) { ULONG chEaten; hr = psf->ParseDisplayName(hwnd, pbc, pwzDelegateInfo, &chEaten, ppidlOut, pdwAttributes); psf->Release(); } } else { IDLREGITEM* pidlRegItem = _CreateAndFillIDLREGITEM(&clsid); if (pidlRegItem) { if (_IsInNameSpace(pidlRegItem) || (BindCtx_GetMode(pbc, 0) & STGM_CREATE)) { hr = _ParseNextLevel(hwnd, pbc, pidlRegItem, pwzNext, ppidlOut, pdwAttributes); } else hr = E_INVALIDARG; ILFree((LPITEMIDLIST)pidlRegItem); } else { hr = E_OUTOFMEMORY; } } } return hr; } // // ask a (known) regitem to parse a displayname // HRESULT CRegFolder::_ParseThroughItem(LPCIDLREGITEM pidlr, HWND hwnd, LPBC pbc, LPOLESTR pszName, ULONG *pchEaten, LPITEMIDLIST *ppidlOut, ULONG *pdwAttributes) { IShellFolder *psfItem; HRESULT hr = _BindToItem(pidlr, pbc, IID_PPV_ARG(IShellFolder, &psfItem), FALSE); if (SUCCEEDED(hr)) { LPITEMIDLIST pidlRight; hr = psfItem->ParseDisplayName(hwnd, pbc, pszName, pchEaten, &pidlRight, pdwAttributes); if (SUCCEEDED(hr)) { hr = SHILCombine((LPCITEMIDLIST)pidlr, pidlRight, ppidlOut); ILFree(pidlRight); } psfItem->Release(); } return hr; } // // Parse through the GUID to the namespace below // HRESULT CRegFolder::_ParseNextLevel(HWND hwnd, LPBC pbc, LPCIDLREGITEM pidlr, LPOLESTR pwzRest, LPITEMIDLIST *ppidlOut, ULONG *pdwAttributes) { if (!*pwzRest) { // base case for recursive calls // pidlNext should be a simple pidl. ASSERT(!ILIsEmpty((LPCITEMIDLIST)pidlr) && ILIsEmpty(_ILNext((LPCITEMIDLIST)pidlr))); if (pdwAttributes && *pdwAttributes) _AttributesOf(pidlr, *pdwAttributes, pdwAttributes); return SHILClone((LPCITEMIDLIST)pidlr, ppidlOut); } ASSERT(*pwzRest == TEXT('\\')); ++pwzRest; IShellFolder *psfNext; HRESULT hr = _BindToItem(pidlr, pbc, IID_PPV_ARG(IShellFolder, &psfNext), FALSE); if (SUCCEEDED(hr)) { ULONG chEaten; LPITEMIDLIST pidlRest; hr = psfNext->ParseDisplayName(hwnd, pbc, pwzRest, &chEaten, &pidlRest, pdwAttributes); if (SUCCEEDED(hr)) { hr = SHILCombine((LPCITEMIDLIST)pidlr, pidlRest, ppidlOut); SHFree(pidlRest); } psfNext->Release(); } return hr; } BOOL _FailForceReturn(HRESULT hr) { switch (hr) { case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND): case HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND): case HRESULT_FROM_WIN32(ERROR_BAD_NET_NAME): case HRESULT_FROM_WIN32(ERROR_BAD_NETPATH): case HRESULT_FROM_WIN32(ERROR_CANCELLED): return TRUE; } return FALSE; } HRESULT CRegFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszName, ULONG *pchEaten, LPITEMIDLIST *ppidlOut, ULONG *pdwAttributes) { HRESULT hr = E_INVALIDARG; if (ppidlOut) *ppidlOut = NULL; if (ppidlOut && pszName) { // ::{guid} lets you get the pidl for a reg item if (*pszName && (pszName[0] == _chRegItem) && (pszName[1] == _chRegItem)) { hr = _ParseGUIDName(hwnd, pbc, pszName, ppidlOut, pdwAttributes); } else { // inner folder gets a chance to parse hr = _psfOuter->ParseDisplayName(hwnd, pbc, pszName, pchEaten, ppidlOut, pdwAttributes); if (FAILED(hr) && !_FailForceReturn(hr) && !SHSkipJunctionBinding(pbc, NULL)) { // loop over all of the items HDCA hdca = _ItemArray(); if (hdca) { HRESULT hrTemp = E_FAIL; for (int i = 0; FAILED(hrTemp) && (i < DCA_GetItemCount(hdca)); i++) { const CLSID clsid = *DCA_GetItem(hdca, i); if (!SHSkipJunction(pbc, &clsid) && SHQueryShellFolderValue(&clsid, L"WantsParseDisplayName")) { IDLREGITEM* pidlRegItem = _CreateAndFillIDLREGITEM(DCA_GetItem(hdca, i)); if (pidlRegItem) { hrTemp = _ParseThroughItem(pidlRegItem, hwnd, pbc, pszName, pchEaten, ppidlOut, pdwAttributes); } ILFree((LPITEMIDLIST)pidlRegItem); } } DCA_Destroy(hdca); if (SUCCEEDED(hrTemp) || _FailForceReturn(hrTemp)) hr = hrTemp; else hr = E_INVALIDARG; // no one could handle it } else { hr = E_OUTOFMEMORY; } } } ASSERT(SUCCEEDED(hr) ? *ppidlOut != NULL : *ppidlOut == NULL); } if (FAILED(hr)) TraceMsg(TF_WARNING, "CRegFolder::ParseDisplayName(), hr:%x %hs", hr, pszName); return hr; } HRESULT CRegFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenum) { *ppenum = NULL; IEnumIDList *penumOuter; HRESULT hr = _psfOuter->EnumObjects(hwnd, grfFlags, &penumOuter); if (SUCCEEDED(hr)) { // SUCCEEDED(hr) may be S_FALSE with penumOuter == NULL // CRegFolderEnum deals with this just fine CRegFolderEnum *preidl = new CRegFolderEnum(this, grfFlags, penumOuter, _ItemArray(), _DelItemArray(), _pPolicy); if (preidl) { *ppenum = SAFECAST(preidl, IEnumIDList*); hr = S_OK; } else hr = E_OUTOFMEMORY; if (penumOuter) penumOuter->Release(); // _psfOuter returned S_FALSE } return hr; } // Handle binding to the inner namespace, or the regitem accordingly given its pidl. HRESULT CRegFolder::_BindToItem(LPCIDLREGITEM pidlr, LPBC pbc, REFIID riid, void **ppv, BOOL bOneLevel) { LPITEMIDLIST pidlAlloc; *ppv = NULL; LPCITEMIDLIST pidlNext = _ILNext((LPCITEMIDLIST)pidlr); if (ILIsEmpty(pidlNext)) { pidlAlloc = NULL; bOneLevel = TRUE; // we know for sure it is one level } else { pidlAlloc = ILCloneFirst((LPCITEMIDLIST)pidlr); if (!pidlAlloc) return E_OUTOFMEMORY; pidlr = (LPCIDLREGITEM)pidlAlloc; // a single item IDLIST } HRESULT hr; if (bOneLevel) { hr = _CreateAndInit(pidlr, pbc, riid, ppv); // create on riid to avoid loads on interfaces not supported } else { IShellFolder *psfNext; hr = _CreateAndInit(pidlr, pbc, IID_PPV_ARG(IShellFolder, &psfNext)); if (SUCCEEDED(hr)) { hr = psfNext->BindToObject(pidlNext, pbc, riid, ppv); psfNext->Release(); } } if (pidlAlloc) ILFree(pidlAlloc); // we allocated in this case return hr; } HRESULT CRegFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv) { HRESULT hr; LPCIDLREGITEM pidlr = _IsReg(pidl); if (pidlr) hr = _BindToItem(pidlr, pbc, riid, ppv, FALSE); else hr = _psfOuter->BindToObject(pidl, pbc, riid, ppv); return hr; } HRESULT CRegFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv) { return BindToObject(pidl, pbc, riid, ppv); } // I can't believe there is no "^^" #define LOGICALXOR(a, b) (((a) && !(b)) || (!(a) && (b))) BOOL CRegFolder::_IsFolder(LPCITEMIDLIST pidl) { BOOL fRet = FALSE; if (pidl) { ULONG uAttrib = SFGAO_FOLDER; if (SUCCEEDED(GetAttributesOf(1, &pidl, &uAttrib)) && (SFGAO_FOLDER & uAttrib)) fRet = TRUE; } return fRet; } int CRegFolder::_CompareIDsOriginal(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { LPCIDLREGITEM pidlr1 = _IsReg(pidl1); LPCIDLREGITEM pidlr2 = _IsReg(pidl2); int iRes = 0; if (pidlr1 && pidlr2) { iRes = memcmp(&(_GetPIDLRCLSID(pidlr1)), &(_GetPIDLRCLSID(pidlr2)), sizeof(CLSID)); if (0 == iRes) { // if they are the same clsid // and delegates then we need to query // the delegate for the compare PDELEGATEITEMID pidld1 = _IsDelegate(pidlr1); PDELEGATEITEMID pidld2 = _IsDelegate(pidlr2); if (pidld1 && pidld2) { // these are both the same delegate IShellFolder *psf; if (SUCCEEDED(_GetDelegateFolder(pidld1, IID_PPV_ARG(IShellFolder, &psf)))) { HRESULT hr = psf->CompareIDs(lParam, pidl1, pidl2); psf->Release(); iRes = HRESULT_CODE(hr); } } else { ASSERT(!pidld1 && !pidld2); } } else if (!(SHCIDS_CANONICALONLY & lParam)) { // sort by defined order BYTE bOrder1 = _GetOrder(pidlr1); BYTE bOrder2 = _GetOrder(pidlr2); int iUI = bOrder1 - bOrder2; if (0 == iUI) { // All of the required items come first, in reverse // order (to make this simpler) int iItem1 = _ReqItemIndex(pidlr1); int iItem2 = _ReqItemIndex(pidlr2); if (iItem1 == -1 && iItem2 == -1) { TCHAR szName1[MAX_PATH], szName2[MAX_PATH]; _GetDisplayName(pidlr1, SHGDN_NORMAL, szName1, ARRAYSIZE(szName1)); _GetDisplayName(pidlr2, SHGDN_NORMAL, szName2, ARRAYSIZE(szName2)); iUI = StrCmpLogicalRestricted(szName1, szName2); } else { iUI = iItem2 - iItem1; } } if (iUI) iRes = iUI; } } return iRes; } // Alphabetical (doesn't care about folder, regitems, ...) int CRegFolder::_CompareIDsAlphabetical(UINT iColumn, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { int iRes = 0; // Do we have only one ptr? if (!LOGICALXOR(pidl1, pidl2)) { // No, either we have two or none. if (pidl1 && pidl2) { iRes = CompareIDsAlphabetical(SAFECAST(this, IShellFolder2*), iColumn, pidl1, pidl2); } // else iRes already = 0 } else { // Yes, the one which is non-NULL is first iRes = (pidl1 ? -1 : 1); } return iRes; } // Folders comes first, and are ordered in alphabetical order among themselves, // then comes all the non-folders again sorted among thmeselves int CRegFolder::_CompareIDsFolderFirst(UINT iColumn, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { int iRes = 0; BOOL fIsFolder1 = _IsFolder(pidl1); BOOL fIsFolder2 = _IsFolder(pidl2); // Is there one folder and one non-folder? if (LOGICALXOR(fIsFolder1, fIsFolder2)) { // Yes, the folder will be first iRes = fIsFolder1 ? -1 : 1; } else { // No, either both are folders or both are not. One way or the other, go // alphabetically iRes = _CompareIDsAlphabetical(iColumn, pidl1, pidl2); } return iRes; } int CRegFolder::_GetOrderType(LPCITEMIDLIST pidl) { if (_IsReg(pidl)) { if (_IsDelegate((LPCIDLREGITEM)pidl)) return REGORDERTYPE_DELEGATE; else if (-1 == _ReqItemIndex((LPCIDLREGITEM)pidl)) return REGORDERTYPE_REGITEM; else return REGORDERTYPE_REQITEM; } return _iTypeOuter; } HRESULT CRegFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { int iType1 = _GetOrderType(pidl1); int iType2 = _GetOrderType(pidl2); int iTypeCompare = iType1 - iType2; int iRes = 0; UINT iColumn = (UINT) (SHCIDS_COLUMNMASK & lParam); // first we compare the first level only switch (_dwSortAttrib) { case RIISA_ORIGINAL: if (0 == iTypeCompare && iType1 == _iTypeOuter) { // neither are regitems return _psfOuter->CompareIDs(lParam, pidl1, pidl2); } else { ASSERT(iRes == 0); // this handled by by CompareIDsOriginal() below } break; case RIISA_FOLDERFIRST: iRes = _CompareIDsFolderFirst(iColumn, pidl1, pidl2); break; case RIISA_ALPHABETICAL: iRes = _CompareIDsAlphabetical(iColumn, pidl1, pidl2); break; } // all our foofy compares and it still looks the same to us // time to get medieval. if (0 == iRes) { iRes = _CompareIDsOriginal(lParam, pidl1, pidl2); if (0 == iRes) iRes = iTypeCompare; if (0 == iRes) { // If the class ID's really are the same, // we'd better check the next level(s) return ILCompareRelIDs(SAFECAST(this, IShellFolder *), pidl1, pidl2, lParam); } } return ResultFromShort(iRes); } HRESULT CRegFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv) { return _psfOuter->CreateViewObject(hwnd, riid, ppv); } HRESULT CRegFolder::_SetAttributes(LPCIDLREGITEM pidlr, BOOL bPerUser, DWORD dwMask, DWORD dwNewBits) { HKEY hk; HRESULT hr = SHRegGetCLSIDKey(_GetPIDLRCLSID(pidlr), TEXT("ShellFolder"), bPerUser, TRUE, &hk); if (SUCCEEDED(hr)) { DWORD err, dwValue = 0, cbSize = sizeof(dwValue); SHQueryValueEx(hk, TEXT("Attributes"), NULL, NULL, (BYTE *)&dwValue, &cbSize); dwValue = (dwValue & ~dwMask) | (dwNewBits & dwMask); err = RegSetValueEx(hk, TEXT("Attributes"), 0, REG_DWORD, (BYTE *)&dwValue, sizeof(dwValue)); hr = HRESULT_FROM_WIN32(err); RegCloseKey(hk); } EnterCriticalSection(&_cs); _clsidAttributesCache = CLSID_NULL; LeaveCriticalSection(&_cs); return hr; } LONG CRegFolder::_RegOpenCLSIDUSKey(CLSID clsid, PHUSKEY phk) { WCHAR wszCLSID[39]; LONG iRetVal = ERROR_INVALID_PARAMETER; if (StringFromGUID2(clsid, wszCLSID, ARRAYSIZE(wszCLSID))) { TCHAR szKey[MAXIMUM_SUB_KEY_LENGTH]; HRESULT hr; hr = StringCchPrintf(szKey, ARRAYSIZE(szKey), TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CLSID\\%s\\ShellFolder"), wszCLSID); if (SUCCEEDED(hr)) { iRetVal = SHRegOpenUSKey(szKey, KEY_QUERY_VALUE, NULL, phk, FALSE); } } return iRetVal; } ULONG CRegFolder::_GetPerUserAttributes(LPCIDLREGITEM pidlr) { DWORD dwAttribute = 0; HUSKEY hk; if (ERROR_SUCCESS == _RegOpenCLSIDUSKey(_GetPIDLRCLSID(pidlr), &hk)) { DWORD cb = sizeof(dwAttribute); DWORD dwType = REG_DWORD; SHRegQueryUSValue(hk, TEXT("Attributes"), &dwType, &dwAttribute, &cb, FALSE, 0, sizeof(DWORD)); SHRegCloseUSKey(hk); } // we only allow these bits to change return dwAttribute & (SFGAO_NONENUMERATED | SFGAO_CANDELETE | SFGAO_CANMOVE); } #define SFGAO_REQ_MASK (SFGAO_NONENUMERATED | SFGAO_CANDELETE | SFGAO_CANMOVE) HRESULT CRegFolder::_AttributesOf(LPCIDLREGITEM pidlr, DWORD dwAttributesNeeded, DWORD *pdwAttributes) { HRESULT hr = S_OK; *pdwAttributes = 0; PDELEGATEITEMID pidld = _IsDelegate(pidlr); if (pidld) { IShellFolder *psf; hr = _GetDelegateFolder(pidld, IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { *pdwAttributes = dwAttributesNeeded; hr = psf->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlr, pdwAttributes); psf->Release(); } } else { EnterCriticalSection(&_cs); CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment BOOL bGuidMatch = IsEqualGUID(clsid, _clsidAttributesCache); if (bGuidMatch && ((dwAttributesNeeded & _dwAttributesCacheValid) == dwAttributesNeeded)) { *pdwAttributes = _dwAttributesCache; } else { int iItem = _ReqItemIndex(pidlr); // if the guid didn't match, we need to start from scratch. // otherwise, we'll or back in the cahced bits... if (!bGuidMatch) { _dwAttributesCacheValid = 0; _dwAttributesCache = 0; } if (iItem >= 0) { *pdwAttributes = _aReqItems[iItem].dwAttributes; // per machine attributes allow items to be hidden per machine *pdwAttributes |= SHGetAttributesFromCLSID2(&clsid, 0, SFGAO_REQ_MASK) & SFGAO_REQ_MASK; } else { *pdwAttributes = SHGetAttributesFromCLSID2(&clsid, SFGAO_CANMOVE | SFGAO_CANDELETE, dwAttributesNeeded & ~_dwAttributesCacheValid); } *pdwAttributes |= _GetPerUserAttributes(pidlr); // hidden per user *pdwAttributes |= _dwDefAttributes; // per folder defaults *pdwAttributes |= _dwAttributesCache; _clsidAttributesCache = clsid; _dwAttributesCache = *pdwAttributes; _dwAttributesCacheValid |= dwAttributesNeeded | *pdwAttributes; // if they gave us more than we asked for, cache them } LeaveCriticalSection(&_cs); } return hr; } HRESULT CRegFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *prgfInOut) { HRESULT hr; if (!cidl) { // special case for the folder as a whole, so I know nothing about it. hr = _psfOuter->GetAttributesOf(cidl, apidl, prgfInOut); } else { hr = S_OK; UINT rgfOut = *prgfInOut; LPCITEMIDLIST *ppidl = (LPCITEMIDLIST*)LocalAlloc(LPTR, cidl * sizeof(*ppidl)); if (ppidl) { LPCITEMIDLIST *ppidlEnd = ppidl + cidl; for (int i = cidl - 1; SUCCEEDED(hr) && (i >= 0); --i) { LPCIDLREGITEM pidlr = _IsReg(apidl[i]); if (pidlr) { if ((*prgfInOut & SFGAO_VALIDATE) && !_IsDelegate(pidlr)) { if (!_IsInNameSpace(pidlr)) { // validate by binding IUnknown *punk; hr = _BindToItem(pidlr, NULL, IID_PPV_ARG(IUnknown, &punk), FALSE); if (SUCCEEDED(hr)) punk->Release(); } } DWORD dwAttributes; hr = _AttributesOf(pidlr, *prgfInOut, &dwAttributes); if (SUCCEEDED(hr)) rgfOut &= dwAttributes; cidl--; // remove this from the list used below... } else { --ppidlEnd; *ppidlEnd = apidl[i]; } } if (SUCCEEDED(hr) && cidl) // any non reg items left? { ULONG rgfThis = rgfOut; hr = _psfOuter->GetAttributesOf(cidl, ppidlEnd, &rgfThis); rgfOut &= rgfThis; } LocalFree((HLOCAL)ppidl); *prgfInOut = rgfOut; } else hr = E_OUTOFMEMORY; } return hr; } HRESULT CRegFolder::_CreateDefExtIconKey(HKEY hkey, UINT cidl, LPCITEMIDLIST *apidl, int iItem, REFIID riid, void** ppvOut) { // See if this guy has an icon handler TCHAR szHandler[GUIDSTR_MAX]; HRESULT hr; if (hkey && SUCCEEDED(AssocQueryStringByKey(NULL, ASSOCSTR_SHELLEXTENSION, hkey, TEXT("IconHandler"), szHandler, IntToPtr_(LPDWORD, ARRAYSIZE(szHandler)))) && SUCCEEDED(SHExtCoCreateInstance(szHandler, NULL, NULL, riid, ppvOut))) { IShellExtInit *psei; if (SUCCEEDED(((IUnknown*)*ppvOut)->QueryInterface(IID_PPV_ARG(IShellExtInit, &psei)))) { IDataObject *pdto; hr = GetUIObjectOf(NULL, cidl, apidl, IID_PPV_ARG_NULL(IDataObject, &pdto)); if (SUCCEEDED(hr)) { hr = psei->Initialize(_GetFolderIDList(), pdto, hkey); pdto->Release(); } psei->Release(); } else { // Object doesn't need to be initialized, no problemo hr = S_OK; } if (SUCCEEDED(hr)) { return S_OK; } ((IUnknown *)*ppvOut)->Release(); // Lose this bad guy } // No icon handler (or icon handler punted); look for DefaultIcon key. LPCTSTR pszIconFile; int iDefIcon; if (iItem >= 0) { pszIconFile = _aReqItems[iItem].pszIconFile; iDefIcon = _aReqItems[iItem].iDefIcon; } else { pszIconFile = NULL; iDefIcon = II_FOLDER; } return SHCreateDefExtIconKey(hkey, pszIconFile, iDefIcon, iDefIcon, -1, -1, GIL_PERCLASS, riid, ppvOut); } HRESULT CRegFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl, REFIID riid, UINT *prgfInOut, void **ppv) { HRESULT hr; *ppv = NULL; LPCIDLREGITEM pidlr = _AnyRegItem(cidl, apidl); if (pidlr) { IShellFolder *psf; if (_AllDelegates(cidl, apidl, &psf)) { hr = psf->GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv); psf->Release(); } else { if (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) { HKEY hkCLSID; int iItem = _ReqItemIndex(pidlr); // try first to get a per-user icon hr = SHRegGetCLSIDKey(_GetPIDLRCLSID(pidlr), NULL, TRUE, FALSE, &hkCLSID); if (SUCCEEDED(hr)) { hr = _CreateDefExtIconKey(hkCLSID, cidl, apidl, iItem, riid, ppv); if (hr == S_FALSE) { ((IUnknown *)*ppv)->Release(); // Lose this bad guy *ppv = NULL; } RegCloseKey(hkCLSID); } // // fall back to a per-class icon // if (*ppv == NULL) { hr = SHRegGetCLSIDKey(_GetPIDLRCLSID(pidlr), NULL, FALSE, FALSE, &hkCLSID); if (SUCCEEDED(hr)) { hr = _CreateDefExtIconKey(hkCLSID, cidl, apidl, iItem, riid, ppv); RegCloseKey(hkCLSID); } } } else if (IsEqualIID(riid, IID_IQueryInfo)) { hr = _GetInfoTip(pidlr, ppv); } else if (IsEqualIID(riid, IID_IQueryAssociations)) { hr = _AssocCreate(pidlr, riid, ppv); } else if (IsEqualIID(riid, IID_IDataObject)) { hr = CIDLData_CreateFromIDArray(_GetFolderIDList(), cidl, apidl, (IDataObject **)ppv); } else if (IsEqualIID(riid, IID_IContextMenu)) { hr = _psfOuter->QueryInterface(IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { HKEY keys[2]; _GetClassKeys(pidlr, &keys[0], &keys[1]); hr = CDefFolderMenu_Create2Ex(_GetFolderIDList(), hwnd, cidl, apidl, psf, this, ARRAYSIZE(keys), keys, (IContextMenu **)ppv); SHRegCloseKeys(keys, ARRAYSIZE(keys)); psf->Release(); } } else if (cidl == 1) { // blindly delegate unknown riid (IDropTarget, IShellLink, etc) through // APP COMPAT! GetUIObjectOf does not support multilevel pidls, but // Symantec Internet Fast Find does a // // psfDesktop->GetUIObjectOf(1, &pidlComplex, IID_IDropTarget, ...) // // on a multilevel pidl and expects it to work. I guess it worked by // lucky accident once upon a time, so now it must continue to work, // but only on the desktop. // hr = _CreateViewObjectFor(pidlr, hwnd, riid, ppv, !_IsDesktop()); } else { hr = E_NOINTERFACE; } } } else { hr = _psfOuter->GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv); } return hr; } HRESULT CRegFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, STRRET *pStrRet) { HRESULT hr; LPCIDLREGITEM pidlr = _IsReg(pidl); if (pidlr) { LPCITEMIDLIST pidlNext = _ILNext(pidl); if (ILIsEmpty(pidlNext)) { TCHAR szName[MAX_PATH]; hr = _GetDisplayName(pidlr, dwFlags, szName, ARRAYSIZE(szName)); if (SUCCEEDED(hr)) hr = StringToStrRet(szName, pStrRet); } else { IShellFolder *psfNext; hr = _BindToItem(pidlr, NULL, IID_PPV_ARG(IShellFolder, &psfNext), TRUE); if (SUCCEEDED(hr)) { hr = psfNext->GetDisplayNameOf(pidlNext, dwFlags, pStrRet); // If it returns an offset to the pidlNext, we should // change the offset relative to pidl. if (SUCCEEDED(hr) && pStrRet->uType == STRRET_OFFSET) pStrRet->uOffset += (DWORD)((BYTE *)pidlNext - (BYTE *)pidl); psfNext->Release(); } } } else hr = _psfOuter->GetDisplayNameOf(pidl, dwFlags, pStrRet); return hr; } HRESULT CRegFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD dwFlags, LPITEMIDLIST *ppidlOut) { LPCIDLREGITEM pidlr = _IsReg(pidl); if (pidlr) { HRESULT hr = E_INVALIDARG; if (ppidlOut) *ppidlOut = NULL; PDELEGATEITEMID pidld = _IsDelegate(pidlr); if (pidld) { IShellFolder *psf; hr = _GetDelegateFolder(pidld, IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { hr = psf->SetNameOf(hwnd, pidl, pszName, dwFlags, ppidlOut); psf->Release(); } } else { HKEY hkCLSID; _ClearNameFromCache(); // See if per-user entry exists... hr = SHRegGetCLSIDKey(_GetPIDLRCLSID(pidlr), NULL, TRUE, TRUE, &hkCLSID); // If no per-user, then use per-machine... if (FAILED(hr)) { _GetClassKeys(pidlr, &hkCLSID, NULL); if (hkCLSID) { hr = S_OK; } else { hr = E_FAIL; } } if (SUCCEEDED(hr)) { TCHAR szName[MAX_PATH]; SHUnicodeToTChar(pszName, szName, ARRAYSIZE(szName)); if (RegSetValue(hkCLSID, NULL, REG_SZ, szName, (lstrlen(szName) + 1) * sizeof(szName[0])) == ERROR_SUCCESS) { LPITEMIDLIST pidlAbs = ILCombine(_GetFolderIDList(), pidl); if (pidlAbs) { SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_IDLIST, pidlAbs, NULL); ILFree(pidlAbs); } if (ppidlOut) *ppidlOut = ILClone(pidl); // name is not in the PIDL so old == new hr = S_OK; } else hr = E_FAIL; RegCloseKey(hkCLSID); } } return hr; } return _psfOuter->SetNameOf(hwnd, pidl, pszName, dwFlags, ppidlOut); } HRESULT CRegFolder::GetDefaultSearchGUID(LPGUID lpGuid) { return _psfOuter->GetDefaultSearchGUID(lpGuid); } HRESULT CRegFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum) { return _psfOuter->EnumSearches(ppenum); } HRESULT CRegFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay) { return _psfOuter->GetDefaultColumn(dwRes, pSort, pDisplay); } HRESULT CRegFolder::GetDefaultColumnState(UINT iColumn, DWORD *pbState) { return _psfOuter->GetDefaultColumnState(iColumn, pbState); } HRESULT CRegFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv) { HRESULT hr = E_NOTIMPL; LPCIDLREGITEM pidlr = _IsReg(pidl); if (pidlr) { PDELEGATEITEMID pidld = _IsDelegate(pidlr); if (pidld) { IShellFolder2 *psf2; hr = _GetDelegateFolder(pidld, IID_PPV_ARG(IShellFolder2, &psf2)); if (SUCCEEDED(hr)) { hr = psf2->GetDetailsEx(pidl, pscid, pv); psf2->Release(); } } else { TCHAR szTemp[INFOTIPSIZE]; szTemp[0] = 0; if (IsEqualSCID(*pscid, SCID_DESCRIPTIONID)) { SHDESCRIPTIONID did; did.dwDescriptionId = SHDID_ROOT_REGITEM; did.clsid = _GetPIDLRCLSID(pidlr); hr = InitVariantFromBuffer(pv, &did, sizeof(did)); } else if (IsEqualSCID(*pscid, SCID_NAME)) { _GetDisplayName(pidlr, SHGDN_NORMAL, szTemp, ARRAYSIZE(szTemp)); hr = InitVariantFromStr(pv, szTemp); } else if (IsEqualSCID(*pscid, SCID_TYPE)) { LoadString(HINST_THISDLL, IDS_DRIVES_REGITEM, szTemp, ARRAYSIZE(szTemp)); hr = InitVariantFromStr(pv, szTemp); } else if (IsEqualSCID(*pscid, SCID_Comment)) { _GetRegItemColumnFromRegistry(pidlr, TEXT("InfoTip"), szTemp, ARRAYSIZE(szTemp)); hr = InitVariantFromStr(pv, szTemp); } else if (IsEqualSCID(*pscid, SCID_FolderIntroText)) { _GetRegItemColumnFromRegistry(pidlr, TEXT("IntroText"), szTemp, ARRAYSIZE(szTemp)); hr = InitVariantFromStr(pv, szTemp); } else { TCHAR ach[SCIDSTR_MAX]; StringFromSCID(pscid, ach, ARRAYSIZE(ach)); hr = _GetRegItemVariantFromRegistry(pidlr, ach, pv); } } } else { hr = _psfOuter->GetDetailsEx(pidl, pscid, pv); } return hr; } HRESULT CRegFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetail) { HRESULT hr = E_FAIL; LPCIDLREGITEM pidlr = _IsReg(pidl); if (pidlr) { pDetail->str.uType = STRRET_CSTR; pDetail->str.cStr[0] = 0; SHCOLUMNID scid; hr = _psfOuter->MapColumnToSCID(iColumn, &scid); if (SUCCEEDED(hr)) { VARIANT var = {0}; hr = GetDetailsEx(pidl, &scid, &var); if (SUCCEEDED(hr)) { // we need to use SHFormatForDisplay (or we could have use IPropertyUI) // to format arbitrary properties into the right display type TCHAR szText[MAX_PATH]; hr = SHFormatForDisplay(scid.fmtid, scid.pid, (PROPVARIANT *)&var, PUIFFDF_DEFAULT, szText, ARRAYSIZE(szText)); if (SUCCEEDED(hr)) { hr = StringToStrRet(szText, &pDetail->str); } VariantClear(&var); } } } else { hr = _psfOuter->GetDetailsOf(pidl, iColumn, pDetail); } return hr; } HRESULT CRegFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid) { return _psfOuter->MapColumnToSCID(iColumn, pscid); } HRESULT CRegFolder::_GetOverlayInfo(LPCIDLREGITEM pidlr, int *pIndex, BOOL fIconIndex) { HRESULT hr = E_FAIL; const CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment if (SHQueryShellFolderValue(&clsid, TEXT("QueryForOverlay"))) { IShellIconOverlay* psio; hr = _BindToItem(pidlr, NULL, IID_PPV_ARG(IShellIconOverlay, &psio), TRUE); if (SUCCEEDED(hr)) { // NULL pidl means "I want to know about YOU, folder, not one of your kids", // we only pass that though when its is not a deligate. LPITEMIDLIST pidlToPass = (LPITEMIDLIST)_IsDelegate(pidlr); if (fIconIndex) hr = psio->GetOverlayIconIndex(pidlToPass, pIndex); else hr = psio->GetOverlayIndex(pidlToPass, pIndex); psio->Release(); } } return hr; } // IShellIconOverlay HRESULT CRegFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex) { HRESULT hr = E_FAIL; LPCIDLREGITEM pidlr = _IsReg(pidl); if (pidlr) { hr = _GetOverlayInfo(pidlr, pIndex, FALSE); } else if (_psioOuter) { hr = _psioOuter->GetOverlayIndex(pidl, pIndex); } return hr; } HRESULT CRegFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIconIndex) { HRESULT hr = E_FAIL; LPCIDLREGITEM pidlr = _IsReg(pidl); if (pidlr) { hr = _GetOverlayInfo(pidlr, pIconIndex, TRUE); } else if (_psioOuter) { hr = _psioOuter->GetOverlayIconIndex(pidl, pIconIndex); } return hr; } // CContextMenuCB DWORD CALLBACK _RegFolderPropThreadProc(void *pv) { PROPSTUFF *pdps = (PROPSTUFF *)pv; CRegFolder *prf = (CRegFolder *)pdps->psf; STGMEDIUM medium; ULONG_PTR dwCookie = 0; ActivateActCtx(NULL, &dwCookie); LPIDA pida = DataObj_GetHIDA(pdps->pdtobj, &medium); if (pida) { LPCIDLREGITEM pidlr = prf->_IsReg(IDA_GetIDListPtr(pida, 0)); if (pidlr) { int iItem = prf->_ReqItemIndex(pidlr); if (iItem >= 0 && prf->_aReqItems[iItem].pszCPL) SHRunControlPanel(prf->_aReqItems[iItem].pszCPL, NULL); else { TCHAR szName[MAX_PATH]; if (SUCCEEDED(prf->_GetDisplayName(pidlr, SHGDN_NORMAL, szName, ARRAYSIZE(szName)))) { HKEY hk; prf->_GetClassKeys(pidlr, &hk, NULL); if (hk) { SHOpenPropSheet(szName, &hk, 1, NULL, pdps->pdtobj, NULL, (LPCTSTR)pdps->pStartPage); RegCloseKey(hk); } } } } HIDA_ReleaseStgMedium(pida, &medium); } return 0; } DWORD DisconnectDialogOnThread(void *pv) { WNetDisconnectDialog(NULL, RESOURCETYPE_DISK); SHChangeNotifyHandleEvents(); // flush any drive notifications return 0; } HRESULT CRegFolder::CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) { HRESULT hr = S_OK; switch (uMsg) { case DFM_MERGECONTEXTMENU: { STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(pdtobj, &medium); if (pida) { // some ugly specal cases... if (HIDA_GetCount(medium.hGlobal) == 1) { LPCIDLREGITEM pidlr = _IsReg(IDA_GetIDListPtr(pida, 0)); if (pidlr && !_IsDelegate(pidlr)) { const CLSID clsid = _GetPIDLRCLSID(pidlr); // alignment if ((IsEqualGUID(clsid, CLSID_MyComputer) || IsEqualGUID(clsid, CLSID_NetworkPlaces)) && (GetSystemMetrics(SM_NETWORK) & RNC_NETWORKS) && !SHRestricted(REST_NONETCONNECTDISCONNECT)) { CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_DESKTOP_ITEM, 0, (LPQCMINFO)lParam); } } } HIDA_ReleaseStgMedium(pida, &medium); } } break; case DFM_GETHELPTEXT: LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));; break; case DFM_GETHELPTEXTW: LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));; break; case DFM_INVOKECOMMANDEX: { DFMICS *pdfmics = (DFMICS *)lParam; switch (wParam) { case FSIDM_CONNECT: SHStartNetConnectionDialog(NULL, NULL, RESOURCETYPE_DISK); break; case FSIDM_DISCONNECT: SHCreateThread(DisconnectDialogOnThread, NULL, CTF_COINIT, NULL); break; case DFM_CMD_PROPERTIES: hr = SHLaunchPropSheet(_RegFolderPropThreadProc, pdtobj, (LPCTSTR)pdfmics->lParam, this, NULL); break; case DFM_CMD_DELETE: _Delete(hwnd, pdfmics->fMask, pdtobj); break; default: // This is one of view menu items, use the default code. hr = S_FALSE; break; } } break; default: hr = E_NOTIMPL; break; } return hr; } // IRegItemsFolder TCHAR const c_szRegExplorerBackslash[] = REGSTR_PATH_EXPLORER TEXT("\\"); HRESULT CRegFolder::Initialize(REGITEMSINFO *pri) { ASSERT(pri != NULL); HRESULT hr = E_INVALIDARG; if (!_fcs) { if (!InitializeCriticalSectionAndSpinCount(&_cs, 0)) { return E_FAIL; } } _fcs = TRUE; _pszRegKey = pri->pszRegKey; _pPolicy = pri->pPolicy; _chRegItem = pri->cRegItem; _bFlags = pri->bFlags; _iTypeOuter = pri->iCmp > 0 ? REGORDERTYPE_OUTERAFTER : REGORDERTYPE_OUTERBEFORE ; _dwDefAttributes = pri->rgfRegItems; _dwSortAttrib = pri->dwSortAttrib; _cbPadding = pri->cbPadding; _bFlagsLegacy = pri->bFlagsLegacy; // If the registry key lives under HKEY_PATH_EXPLORER, then // we will also support per-session regitems. // int cchPrefix = ARRAYSIZE(c_szRegExplorerBackslash) - 1; if (StrCmpNI(_pszRegKey, c_szRegExplorerBackslash, cchPrefix) == 0) { _pszSesKey = _pszRegKey + cchPrefix; } else { _pszSesKey = NULL; } if ((RIISA_ORIGINAL == _dwSortAttrib) || (RIISA_FOLDERFIRST == _dwSortAttrib) || (RIISA_ALPHABETICAL == _dwSortAttrib)) { Str_SetPtr(&_pszMachine, pri->pszMachine); // save a copy of this _aReqItems = (REQREGITEM *)LocalAlloc(LPTR, sizeof(*_aReqItems) * pri->iReqItems); if (!_aReqItems) return E_OUTOFMEMORY; memcpy(_aReqItems, pri->pReqItems, sizeof(*_aReqItems) * pri->iReqItems); _nRequiredItems = pri->iReqItems; // If we are aggregated, cache the _psioOuter and _psfOuter _QueryOuterInterface(IID_PPV_ARG(IShellIconOverlay, &_psioOuter)); hr = _QueryOuterInterface(IID_PPV_ARG(IShellFolder2, &_psfOuter)); } return hr; } // // instance creation of the RegItems object // STDAPI CRegFolder_CreateInstance(REGITEMSINFO *pri, IUnknown *punkOuter, REFIID riid, void **ppv) { HRESULT hr; // we only suport being created as an agregate if (!punkOuter || !IsEqualIID(riid, IID_IUnknown)) { ASSERT(0); return E_FAIL; } CRegFolder *prif = new CRegFolder(punkOuter); if (prif) { hr = prif->Initialize(pri); // initialize the regfolder if (SUCCEEDED(hr)) hr = prif->_GetInner()->QueryInterface(riid, ppv); // // If the Initalize and QueryInterface succeeded, this will drop // the refcount from 2 to 1. If they failed, then this will drop // the refcount from 1 to 0 and free the object. // ULONG cRef = prif->_GetInner()->Release(); // // On success, the object should have a refcout of exactly 1. // On failure, the object should have a refcout of exactly 0. // ASSERT(SUCCEEDED(hr) == (BOOL)cRef); } else hr = E_OUTOFMEMORY; return hr; } CRegFolderEnum::CRegFolderEnum(CRegFolder* prf, DWORD grfFlags, IEnumIDList* peidl, HDCA hdca, HDCA hdcaDel, REGITEMSPOLICY* pPolicy) : _cRef(1), _grfFlags(grfFlags), _prf(prf), _peidl(peidl), _hdca(hdca), _pPolicy(pPolicy), _hdcaDel(hdcaDel) { ASSERT(_iCur == 0); ASSERT(_iCurDel == 0); ASSERT(_peidlDel == NULL); _prf->AddRef(); if (_peidl) _peidl->AddRef(); DllAddRef(); } CRegFolderEnum::~CRegFolderEnum() { if (_hdca) DCA_Destroy(_hdca); if (_hdcaDel) DCA_Destroy(_hdcaDel); ATOMICRELEASE(_prf); ATOMICRELEASE(_peidl); ATOMICRELEASE(_peidlDel); DllRelease(); } // // IUnknown // STDMETHODIMP_(ULONG) CRegFolderEnum::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CRegFolderEnum::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } HRESULT CRegFolderEnum::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CRegFolderEnum, IEnumIDList), // IID_IEnumIDList QITABENT(CRegFolderEnum, IObjectWithSite), // IID_IObjectWithSite { 0 }, }; return QISearch(this, qit, riid, ppv); } // // IEnumIDList // BOOL CRegFolderEnum::_TestFolderness(DWORD dwAttribItem) { if ((_grfFlags & (SHCONTF_FOLDERS | SHCONTF_NONFOLDERS)) != (SHCONTF_FOLDERS | SHCONTF_NONFOLDERS)) { if (dwAttribItem & SFGAO_FOLDER) { if (!(_grfFlags & SHCONTF_FOLDERS)) return FALSE; } else { if (!(_grfFlags & SHCONTF_NONFOLDERS)) return FALSE; } } return TRUE; } BOOL CRegFolderEnum::_TestHidden(LPCIDLREGITEM pidlRegItem) { CLSID clsidRegItem = _prf->_GetPIDLRCLSID(pidlRegItem); return _TestHiddenInWebView(&clsidRegItem) || _TestHiddenInDomain(&clsidRegItem); } BOOL CRegFolderEnum::_TestHiddenInWebView(LPCLSID clsidRegItem) { BOOL fRetVal = FALSE; if (S_FALSE == SHShouldShowWizards(_punkSite)) { fRetVal = SHQueryShellFolderValue(clsidRegItem, TEXT("HideInWebView")); } return fRetVal; } BOOL CRegFolderEnum::_TestHiddenInDomain(LPCLSID clsidRegItem) { return IsOS(OS_DOMAINMEMBER) && SHQueryShellFolderValue(clsidRegItem, TEXT("HideInDomain")); } // Check policy restrictions BOOL CRegFolderEnum::_IsRestricted() { BOOL bIsRestricted = FALSE; if (_pPolicy) { TCHAR szName[256]; szName[0] = 0; HKEY hkRoot; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, _T("CLSID"), NULL, KEY_QUERY_VALUE, &hkRoot)) { TCHAR szGUID[64]; SHStringFromGUID(*DCA_GetItem(_hdca, _iCur - 1), szGUID, ARRAYSIZE(szGUID)); SHLoadLegacyRegUIString(hkRoot, szGUID, szName, ARRAYSIZE(szName)); RegCloseKey(hkRoot); } if (szName[0]) { if (SHRestricted(_pPolicy->restAllow) && !IsNameListedUnderKey(szName, _pPolicy->pszAllow)) bIsRestricted = TRUE; if (SHRestricted(_pPolicy->restDisallow) && IsNameListedUnderKey(szName, _pPolicy->pszDisallow)) bIsRestricted = TRUE; } } return bIsRestricted; } BOOL CRegFolderEnum::_WrongMachine() { BOOL bWrongMachine = FALSE; // We're filling the regitem with class id clsid. If this is a // remote item, first invoke the class to see if it really wants // to be enumerated for this remote computer. if (_prf->_pszMachine) { IUnknown* punk; // Don't need DCA_ExtCreateInstance since these keys come from // HKLM which is already trusted and HKCU which is the user's // own fault. HRESULT hr = DCA_CreateInstance(_hdca, _iCur - 1, IID_PPV_ARG(IUnknown, &punk)); if (SUCCEEDED(hr)) { hr = _prf->_InitFromMachine(punk, TRUE); punk->Release(); } bWrongMachine = FAILED(hr); } return bWrongMachine; } HRESULT CRegFolderEnum::Next(ULONG celt, LPITEMIDLIST *ppidlOut, ULONG *pceltFetched) { // enumerate from the DCA containing the regitems objects if (_hdca) { if (0 == (SHCONTF_NETPRINTERSRCH & _grfFlags)) //don't enumerate phantom folders for printer search dialog { while (_iCur < DCA_GetItemCount(_hdca)) { _iCur++; if (_WrongMachine()) continue; if (_IsRestricted()) continue; // Ok, actually enumerate the item HRESULT hr; IDLREGITEM* pidlRegItem = _prf->_CreateAndFillIDLREGITEM(DCA_GetItem(_hdca, _iCur-1)); if (pidlRegItem) { DWORD dwAttribItem; _prf->_AttributesOf(pidlRegItem, SFGAO_NONENUMERATED | SFGAO_FOLDER, &dwAttribItem); if (!(dwAttribItem & SFGAO_NONENUMERATED) && _TestFolderness(dwAttribItem) && !_TestHidden(pidlRegItem)) { *ppidlOut = (LPITEMIDLIST)pidlRegItem; hr = S_OK; } else { SHFree(pidlRegItem); continue; } } else { hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr) && pceltFetched) *pceltFetched = 1; return hr; } } } // enumerate from the DCA containing the delegate shell folders while (_peidlDel || (_hdcaDel && (_iCurDel < DCA_GetItemCount(_hdcaDel)))) { // we have an enumerator object, so lets call it and see // what it generates, if it runs out of items then we either // give up (saying were done) or allow us to be called again. if (_peidlDel) { if (S_OK == _peidlDel->Next(celt, ppidlOut, pceltFetched)) return S_OK; ATOMICRELEASE(_peidlDel); } else { // we didn't have an enumerator to call, so lets try and // create an new IDelegateFolderObject, if that worked // then we can set its item allocator, then get an // enumerator back from it. IShellFolder *psfDelegate; if (SUCCEEDED(_prf->_CreateDelegateFolder(DCA_GetItem(_hdcaDel, _iCurDel++), IID_PPV_ARG(IShellFolder, &psfDelegate)))) { psfDelegate->EnumObjects(NULL, _grfFlags, &_peidlDel); psfDelegate->Release(); } } } // now DKA, or we are just about done so lets pass to the inner ISF // and see what they return. if (_peidl) return _peidl->Next(celt, ppidlOut, pceltFetched); *ppidlOut = NULL; if (pceltFetched) pceltFetched = 0; return S_FALSE; } STDMETHODIMP CRegFolderEnum::Reset() { // Adaptec Easy CD Creator (versions 3.0, 3.01, 3.5) enumerates the // items in an IShellFolder like this: // // psf->EnumObjects(&penum); // UINT cObjects = 0; // while (SUCCEEDED(penum->Next(...)) { // [code] // penum->Reset(); // penum->Skip(++cObjects); // } // // So they took an O(n) algorithm and turned it into an O(n^2) // algorithm. They got away with it because in the old days, // regfldr implemented neither IEnumIDList::Reset nor // IEnumIDList::Skip, so the two calls were just NOPs. // // Now we implement IEnumIDList::Reset, so without this hack, // they end up enumerating the same object over and over again. if (SHGetAppCompatFlags(ACF_IGNOREENUMRESET) & ACF_IGNOREENUMRESET) return E_NOTIMPL; _iCurDel = _iCur = 0; ATOMICRELEASE(_peidlDel); if (_peidl) return _peidl->Reset(); return S_OK; } STDMETHODIMP CRegFolderEnum::SetSite(IUnknown *punkSite) { IUnknown_SetSite(_peidl, punkSite); return CObjectWithSite::SetSite(punkSite); } // // Delegate Malloc functions // #undef new // Hack!! Need to remove this (daviddv) class CDelagateMalloc : public IMalloc { public: // IUnknown STDMETHODIMP QueryInterface(REFIID,void **); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // IMalloc STDMETHODIMP_(void *) Alloc(SIZE_T cb); STDMETHODIMP_(void *) Realloc(void *pv, SIZE_T cb); STDMETHODIMP_(void) Free(void *pv); STDMETHODIMP_(SIZE_T) GetSize(void *pv); STDMETHODIMP_(int) DidAlloc(void *pv); STDMETHODIMP_(void) HeapMinimize(); private: CDelagateMalloc(void *pv, SIZE_T cbSize, WORD wOuter); ~CDelagateMalloc() {} void* operator new(size_t cbClass, SIZE_T cbSize) { return ::operator new(cbClass + cbSize); } friend HRESULT CDelegateMalloc_Create(void *pv, SIZE_T cbSize, WORD wOuter, IMalloc **ppmalloc); protected: LONG _cRef; WORD _wOuter; // delegate item outer signature WORD _wUnused; // to allign #ifdef DEBUG UINT _cAllocs; #endif SIZE_T _cb; BYTE _data[EMPTY_SIZE]; }; CDelagateMalloc::CDelagateMalloc(void *pv, SIZE_T cbSize, WORD wOuter) { _cRef = 1; _wOuter = wOuter; _cb = cbSize; memcpy(_data, pv, _cb); } HRESULT CDelagateMalloc::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CDelagateMalloc, IMalloc), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } ULONG CDelagateMalloc::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CDelagateMalloc::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } // the cbInner is the size of the data needed by the delegate. we need to compute // the full size of the pidl for the allocation and init that we the outer folder data void *CDelagateMalloc::Alloc(SIZE_T cbInner) { DELEGATEITEMID *pidl; SIZE_T cbAlloc = sizeof(DELEGATEITEMID) - sizeof(pidl->rgb[0]) + // header cbInner + // inner _cb + // outer data sizeof(WORD); // trailing null (pidl terminator) pidl = (DELEGATEITEMID *)SHAlloc(cbAlloc); if (pidl) { ZeroMemory(pidl, cbAlloc); // make it all empty pidl->cbSize = (WORD)cbAlloc - sizeof(WORD); pidl->wOuter = _wOuter; pidl->cbInner = (WORD)cbInner; memcpy(&pidl->rgb[cbInner], _data, _cb); #ifdef DEBUG _cAllocs++; #endif } return pidl; } void *CDelagateMalloc::Realloc(void *pv, SIZE_T cb) { return NULL; } void CDelagateMalloc::Free(void *pv) { SHFree(pv); } SIZE_T CDelagateMalloc::GetSize(void *pv) { return (SIZE_T)-1; } int CDelagateMalloc::DidAlloc(void *pv) { return -1; } void CDelagateMalloc::HeapMinimize() { } STDAPI CDelegateMalloc_Create(void *pv, SIZE_T cbSize, WORD wOuter, IMalloc **ppmalloc) { HRESULT hr; CDelagateMalloc *pdm = new(cbSize) CDelagateMalloc(pv, cbSize, wOuter); if (pdm) { hr = pdm->QueryInterface(IID_PPV_ARG(IMalloc, ppmalloc)); pdm->Release(); } else hr = E_OUTOFMEMORY; return hr; }