#include "shellprv.h" #pragma hdrstop #include // CLSID_MyDocuments, CLSID_ShellFSFolder #include // SHCoCreateInstance #include // IID_IResolveShellLink #include "util.h" #include "ids.h" enum CALLING_APP_TYPE { APP_IS_UNKNOWN = 0, APP_IS_NORMAL, APP_IS_OFFICE }; class CMyDocsFolderLinkResolver : public IResolveShellLink { private: LONG _cRef; public: CMyDocsFolderLinkResolver() : _cRef(1) { DllAddRef(); }; ~CMyDocsFolderLinkResolver() { DllRelease(); }; // IUnknown STDMETHOD(QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)(); // IResolveShellLink STDMETHOD(ResolveShellLink)(IUnknown* punk, HWND hwnd, DWORD fFlags); }; STDMETHODIMP CMyDocsFolderLinkResolver::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CMyDocsFolderLinkResolver, IResolveShellLink), { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_ (ULONG) CMyDocsFolderLinkResolver::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_ (ULONG) CMyDocsFolderLinkResolver::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } STDMETHODIMP CMyDocsFolderLinkResolver::ResolveShellLink(IUnknown* punk, HWND hwnd, DWORD fFlags) { // No action needed to resolve a link to the mydocs folder: return S_OK; } // shell folder implementation for icon on the desktop. the purpouse of this object is // 1) to give access to MyDocs high up in the name space // this makes it easier for end users to get to MyDocs // 2) allow for end user custimization of the real MyDocs folder // through the provided property page on this icon // NOTE: this object agregates the file system folder so we get away with a minimal set of interfaces // on this object. the real file system folder does stuff like IPersistFolder2 for us class CMyDocsFolder : public IPersistFolder, public IShellFolder2, public IShellIconOverlay { public: CMyDocsFolder(); HRESULT Init(); // IUnknown STDMETHOD(QueryInterface)(REFIID riid, void **ppv); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)(); // IShellFolder STDMETHOD(ParseDisplayName)(HWND hwnd, LPBC pbc, LPOLESTR pDisplayName, ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes); STDMETHOD(EnumObjects)(HWND hwnd, DWORD grfFlags, IEnumIDList **ppEnumIDList); STDMETHOD(BindToObject)(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv); STDMETHOD(BindToStorage)(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv); STDMETHOD(CompareIDs)(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); STDMETHOD(CreateViewObject)(HWND hwnd, REFIID riid, void **ppv); STDMETHOD(GetAttributesOf)(UINT cidl, LPCITEMIDLIST * apidl, ULONG * rgfInOut); STDMETHOD(GetUIObjectOf)(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppv); STDMETHOD(GetDisplayNameOf)(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName); STDMETHOD(SetNameOf)(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST* ppidlOut); // IShellFolder2 STDMETHODIMP GetDefaultSearchGUID(LPGUID lpGuid); STDMETHODIMP EnumSearches(LPENUMEXTRASEARCH *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 iCol, SHCOLUMNID *pscid); // IPersist, IPersistFreeThreadedObject STDMETHOD(GetClassID)(CLSID *pClassID); // IPersistFolder STDMETHOD(Initialize)(LPCITEMIDLIST pidl); // IPersistFolder2, IPersistFolder3, etc are all implemented by // the folder we agregate // IShellIconOverlay STDMETHODIMP GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex); STDMETHODIMP GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIconIndex); private: ~CMyDocsFolder(); HRESULT _GetFolder(); HRESULT _GetFolder2(); HRESULT _GetShellIconOverlay(); void _FreeFolder(); HRESULT _PathFromIDList(LPCITEMIDLIST pidl, LPTSTR pszPath); HRESULT _PathToIDList(LPCTSTR pszPath, LPITEMIDLIST *ppidl); HRESULT _GetFolderOverlayInfo(int *pIndex, BOOL fIconIndex); LONG _cRef; IUnknown * _punk; // points to IUnknown for shell folder in use... IShellFolder * _psf; // points to shell folder in use... IShellFolder2 * _psf2; // points to shell folder in use... IShellIconOverlay* _psio; // points to shell folder in use... LPITEMIDLIST _pidl; // copy of pidl passed to us in Initialize() CALLING_APP_TYPE _host; HRESULT RealInitialize(LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlBindTo, LPTSTR pRootPath); CALLING_APP_TYPE _WhoIsCalling(); }; STDAPI CMyDocsFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { HRESULT hr; CMyDocsFolder *pmydocs = new CMyDocsFolder(); if (pmydocs) { hr = pmydocs->Init(); if (SUCCEEDED(hr)) hr = pmydocs->QueryInterface(riid, ppv); pmydocs->Release(); } else { *ppv = NULL; hr = E_OUTOFMEMORY; } return hr; } CMyDocsFolder::CMyDocsFolder() : _cRef(1), _host(APP_IS_UNKNOWN), _psf(NULL), _psf2(NULL), _psio(NULL), _punk(NULL), _pidl(NULL) { DllAddRef(); } CMyDocsFolder::~CMyDocsFolder() { _cRef = 1000; // deal with agregation re-enter _FreeFolder(); ILFree(_pidl); DllRelease(); } HRESULT CMyDocsFolder::Init() { // agregate a file system folder object early so we can // delegate QI() to him that we don't implement HRESULT hr = SHCoCreateInstance(NULL, &CLSID_ShellFSFolder, SAFECAST(this, IShellFolder *), IID_PPV_ARG(IUnknown, &_punk)); if (SUCCEEDED(hr)) { IPersistFolder3 *ppf3; hr = SHQueryInnerInterface(SAFECAST(this, IShellFolder *), _punk, IID_PPV_ARG(IPersistFolder3, &ppf3)); if (SUCCEEDED(hr)) { PERSIST_FOLDER_TARGET_INFO pfti = {0}; pfti.dwAttributes = FILE_ATTRIBUTE_DIRECTORY; pfti.csidl = CSIDL_PERSONAL | CSIDL_FLAG_PFTI_TRACKTARGET; hr = SHGetFolderLocation(NULL, CSIDL_PERSONAL, NULL, 0, &_pidl); if (SUCCEEDED(hr)) { hr = ppf3->InitializeEx(NULL, _pidl, &pfti); } SHReleaseInnerInterface(SAFECAST(this, IShellFolder *), (IUnknown **)&ppf3); } } return hr; } STDMETHODIMP CMyDocsFolder::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENTMULTI(CMyDocsFolder, IShellFolder, IShellFolder2), QITABENT(CMyDocsFolder, IShellFolder2), QITABENTMULTI(CMyDocsFolder, IPersist, IPersistFolder), QITABENT(CMyDocsFolder, IPersistFolder), QITABENT(CMyDocsFolder, IShellIconOverlay), // QITABENTMULTI2(CMyDocsFolder, IID_IPersistFreeThreadedObject, IPersist), // IID_IPersistFreeThreadedObject { 0 }, }; HRESULT hr = QISearch(this, qit, riid, ppv); if (FAILED(hr) && _punk) hr = _punk->QueryInterface(riid, ppv); // agregated guy return hr; } STDMETHODIMP_ (ULONG) CMyDocsFolder::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_ (ULONG) CMyDocsFolder::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } // Determine who is calling us so that we can do app specific // compatibility hacks when needed CALLING_APP_TYPE CMyDocsFolder::_WhoIsCalling() { // Check to see if we have the value already... if (_host == APP_IS_UNKNOWN) { if (SHGetAppCompatFlags (ACF_APPISOFFICE) & ACF_APPISOFFICE) _host = APP_IS_OFFICE; else _host = APP_IS_NORMAL; } return _host; } // IPersist methods STDMETHODIMP CMyDocsFolder::GetClassID(CLSID *pClassID) { *pClassID = CLSID_MyDocuments; return S_OK; } HRESULT _BindToIDListParent(LPCITEMIDLIST pidl, LPBC pbc, IShellFolder **ppsf, LPITEMIDLIST *ppidlLast) { HRESULT hr; LPITEMIDLIST pidlParent = ILCloneParent(pidl); if (pidlParent) { hr = SHBindToObjectEx(NULL, pidlParent, pbc, IID_PPV_ARG(IShellFolder, ppsf)); ILFree(pidlParent); } else hr = E_OUTOFMEMORY; if (ppidlLast) *ppidlLast = ILFindLastID(pidl); return hr; } HRESULT _ConfirmMyDocsPath(HWND hwnd) { TCHAR szPath[MAX_PATH]; HRESULT hr = SHGetFolderPath(hwnd, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, szPath); if (S_OK != hr) { TCHAR szTitle[MAX_PATH]; // above failed, get unverified path SHGetFolderPath(NULL, CSIDL_PERSONAL | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szPath); LPCTSTR pszMsg = PathIsNetworkPath(szPath) ? MAKEINTRESOURCE(IDS_CANT_FIND_MYDOCS_NET) : MAKEINTRESOURCE(IDS_CANT_FIND_MYDOCS); PathCompactPath(NULL, szPath, 400); GetMyDocumentsDisplayName(szTitle, ARRAYSIZE(szTitle)); ShellMessageBox(g_hinst, hwnd, pszMsg, szTitle, MB_OK | MB_ICONSTOP, szPath, szTitle); hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // user saw the message } else if (hr == S_FALSE) hr = E_FAIL; return hr; } // like SHGetPathFromIDList() except this uses the bind context to make sure // we don't get into loops since there can be cases where there are multiple // instances of this folder that can cause binding loops. HRESULT CMyDocsFolder::_PathFromIDList(LPCITEMIDLIST pidl, LPTSTR pszPath) { *pszPath = 0; LPBC pbc; HRESULT hr = CreateBindCtx(NULL, &pbc); if (SUCCEEDED(hr)) { // this bind context skips extension taged with our CLSID hr = pbc->RegisterObjectParam(STR_SKIP_BINDING_CLSID, SAFECAST(this, IShellFolder *)); if (SUCCEEDED(hr)) { LPITEMIDLIST pidlLast; IShellFolder *psf; hr = _BindToIDListParent(pidl, pbc, &psf, &pidlLast); if (SUCCEEDED(hr)) { hr = DisplayNameOf(psf, pidlLast, SHGDN_FORPARSING, pszPath, MAX_PATH); psf->Release(); } } pbc->Release(); } return hr; } HRESULT CMyDocsFolder::_PathToIDList(LPCTSTR pszPath, LPITEMIDLIST *ppidl) { IShellFolder *psfDesktop; HRESULT hr = SHGetDesktopFolder(&psfDesktop); if (SUCCEEDED(hr)) { LPBC pbc; hr = CreateBindCtx( 0, &pbc ); if (SUCCEEDED(hr)) { BIND_OPTS bo = {sizeof(bo), 0}; bo.grfFlags = BIND_JUSTTESTEXISTENCE; // skip all junctions hr = pbc->SetBindOptions( &bo ); if (SUCCEEDED(hr)) { WCHAR szPath[MAX_PATH]; SHTCharToUnicode(pszPath, szPath, ARRAYSIZE(szPath)); hr = psfDesktop->ParseDisplayName(NULL, pbc, szPath, NULL, ppidl, NULL); } pbc->Release(); } psfDesktop->Release(); } return hr; } void CMyDocsFolder::_FreeFolder() { if (_punk) { SHReleaseInnerInterface(SAFECAST(this, IShellFolder *), (IUnknown **)&_psf); SHReleaseInnerInterface(SAFECAST(this, IShellFolder *), (IUnknown **)&_psf2); SHReleaseInnerInterface(SAFECAST(this, IShellFolder *), (IUnknown **)&_psio); _punk->Release(); _punk = NULL; } } // verify that _psf (agregated file system folder) has been inited HRESULT CMyDocsFolder::_GetFolder() { HRESULT hr; if (_psf) { hr = S_OK; } else { hr = SHQueryInnerInterface(SAFECAST(this, IShellFolder *), _punk, IID_PPV_ARG(IShellFolder, &_psf)); } return hr; } HRESULT CMyDocsFolder::_GetFolder2() { HRESULT hr; if (_psf2) hr = S_OK; else { hr = _GetFolder(); if (SUCCEEDED(hr)) hr = SHQueryInnerInterface(SAFECAST(this, IShellFolder *), _punk, IID_PPV_ARG(IShellFolder2, &_psf2)); } return hr; } HRESULT CMyDocsFolder::_GetShellIconOverlay() { HRESULT hr; if (_psio) { hr = S_OK; } else { hr = _GetFolder(); if (SUCCEEDED(hr)) { hr = SHQueryInnerInterface(SAFECAST(this, IShellFolder *), _punk, IID_PPV_ARG(IShellIconOverlay, &_psio)); } } return hr; } // returns: // S_OK -- goodness // S_FALSE freed the pidl, set to empty // E_OUTOFMEMORY HRESULT _SetIDList(LPITEMIDLIST* ppidl, LPCITEMIDLIST pidl) { if (*ppidl) { ILFree(*ppidl); *ppidl = NULL; } return pidl ? SHILClone(pidl, ppidl) : S_FALSE; } BOOL IsMyDocsIDList(LPCITEMIDLIST pidl) { BOOL bIsMyDocs = FALSE; if (pidl && !ILIsEmpty(pidl) && ILIsEmpty(_ILNext(pidl))) { LPITEMIDLIST pidlMyDocs; if (SUCCEEDED(SHGetFolderLocation(NULL, CSIDL_PERSONAL, NULL, 0, &pidlMyDocs))) { bIsMyDocs = ILIsEqual(pidl, pidlMyDocs); ILFree(pidlMyDocs); } } return bIsMyDocs; } // Scans a desktop.ini file for sections to see if all of them are empty... BOOL IsDesktopIniEmpty(LPCTSTR pIniFile) { TCHAR szSections[1024]; // for section names if (GetPrivateProfileSectionNames(szSections, ARRAYSIZE(szSections), pIniFile)) { for (LPTSTR pTmp = szSections; *pTmp; pTmp += lstrlen(pTmp) + 1) { TCHAR szSection[1024]; // for section key names and values GetPrivateProfileSection(pTmp, szSection, ARRAYSIZE(szSection), pIniFile); if (szSection[0]) { return FALSE; } } } return TRUE; } // Remove our entries from the desktop.ini file in this directory, and // then test the desktop.ini to see if it's empty. If it is, delete it // and remove the system/readonly bit from the directory... void MyDocsUnmakeSystemFolder(LPCTSTR pPath) { TCHAR szIniFile[MAX_PATH]; PathCombine(szIniFile, pPath, c_szDesktopIni); // Remove CLSID2 WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("CLSID2"), NULL, szIniFile); // Remove InfoTip WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("InfoTip"), NULL, szIniFile); // Remove Icon WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("IconFile"), NULL, szIniFile); DWORD dwAttrb = GetFileAttributes(szIniFile); if (dwAttrb != 0xFFFFFFFF) { if (IsDesktopIniEmpty(szIniFile)) { dwAttrb &= ~(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN); SetFileAttributes(szIniFile, dwAttrb); DeleteFile(szIniFile); } PathUnmakeSystemFolder(pPath); } } // IPersistFolder HRESULT CMyDocsFolder::Initialize(LPCITEMIDLIST pidl) { HRESULT hr; if (IsMyDocsIDList(pidl)) { hr = _SetIDList(&_pidl, pidl); } else { TCHAR szPathInit[MAX_PATH], szMyDocs[MAX_PATH]; // we are being inited by some folder other than the one on the // desktop (from the old mydocs desktop.ini). if this the current users // MyDocs we will untag it now so we don't get called on this anymore SHGetFolderPath(NULL, CSIDL_PERSONAL | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szMyDocs); if (SUCCEEDED(_PathFromIDList(pidl, szPathInit)) && lstrcmpi(szPathInit, szMyDocs) == 0) { MyDocsUnmakeSystemFolder(szMyDocs); } hr = E_FAIL; // don't init on the file system folder anymore } return hr; } STDMETHODIMP CMyDocsFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pDisplayName, ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG *pdwAttributes) { HRESULT hr = _GetFolder(); if (SUCCEEDED(hr)) hr = _psf->ParseDisplayName(hwnd, pbc, pDisplayName, pchEaten, ppidl, pdwAttributes); return hr; } STDMETHODIMP CMyDocsFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppEnumIdList) { HRESULT hr = _GetFolder(); if (SUCCEEDED(hr)) hr = _psf->EnumObjects(hwnd, grfFlags, ppEnumIdList); return hr; } STDMETHODIMP CMyDocsFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv) { HRESULT hr = _GetFolder(); if (SUCCEEDED(hr)) hr = _psf->BindToObject(pidl, pbc, riid, ppv); return hr; } STDMETHODIMP CMyDocsFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv) { HRESULT hr = _GetFolder(); if (SUCCEEDED(hr)) hr = _psf->BindToStorage(pidl, pbc, riid, ppv); return hr; } STDMETHODIMP CMyDocsFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { HRESULT hr = _GetFolder(); if (SUCCEEDED(hr)) hr = _psf->CompareIDs(lParam, pidl1, pidl2); return hr; } /* void UpdateSendToFile() { IPersistFile *ppf; if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_MyDocsDropTarget, NULL, IID_PPV_ARG(IPersistFile, &ppf)))) { ppf->Load(NULL, 0); // hack, get this guy to update his icon ppf->Release(); } } */ STDMETHODIMP CMyDocsFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv) { *ppv = NULL; HRESULT hr; if (riid == IID_IResolveShellLink) { // No work needed to resolve a link to the mydocs folder, because it is a virtual // folder whose location is always tracked by the shell, so return our implementation // of IResolveShellLink - which does nothing when Resolve() is called CMyDocsFolderLinkResolver* pslr = new CMyDocsFolderLinkResolver; if (pslr) { hr = pslr->QueryInterface(riid, ppv); pslr->Release(); } else { hr = E_OUTOFMEMORY; } } else if (riid == IID_IShellLinkA || riid == IID_IShellLinkW) { LPITEMIDLIST pidl; hr = SHGetFolderLocation(NULL, CSIDL_PERSONAL | CSIDL_FLAG_NO_ALIAS, NULL, 0, &pidl); if (SUCCEEDED(hr)) { IShellLink *psl; hr = SHCoCreateInstance(NULL, &CLSID_ShellLink, NULL, IID_PPV_ARG(IShellLink, &psl)); if (SUCCEEDED(hr)) { hr = psl->SetIDList(pidl); if (SUCCEEDED(hr)) { hr = psl->QueryInterface(riid, ppv); } psl->Release(); } ILFree(pidl); } } else { hr = _GetFolder(); if (SUCCEEDED(hr)) { if (hwnd && (IID_IShellView == riid)) hr = _ConfirmMyDocsPath(hwnd); if (SUCCEEDED(hr)) hr = _psf->CreateViewObject(hwnd, riid, ppv); } } return hr; } DWORD _GetRealMyDocsAttributes(DWORD dwAttributes) { DWORD dwRet = SFGAO_HASPROPSHEET; // default to this in the falure case // so you can redirect mydocs via the property page LPITEMIDLIST pidl; HRESULT hr = SHGetFolderLocation(NULL, CSIDL_PERSONAL | CSIDL_FLAG_NO_ALIAS, NULL, 0, &pidl); if (SUCCEEDED(hr)) { IShellFolder *psf; LPITEMIDLIST pidlLast; hr = _BindToIDListParent(pidl, NULL, &psf, &pidlLast); if (SUCCEEDED(hr)) { dwRet = SHGetAttributes(psf, pidlLast, dwAttributes); psf->Release(); } ILFree(pidl); } return dwRet; } #define MYDOCS_CLSID TEXT("{450d8fba-ad25-11d0-98a8-0800361b1103}") // CLSID_MyDocuments DWORD MyDocsGetAttributes() { DWORD dwAttributes = SFGAO_CANLINK | // 00000004 SFGAO_CANRENAME | // 00000010 SFGAO_CANDELETE | // 00000020 SFGAO_HASPROPSHEET | // 00000040 SFGAO_DROPTARGET | // 00000100 SFGAO_FILESYSANCESTOR | // 10000000 SFGAO_FOLDER | // 20000000 SFGAO_FILESYSTEM | // 40000000 SFGAO_HASSUBFOLDER | // 80000000 SFGAO_STORAGEANCESTOR | SFGAO_STORAGE; // SFGAO_NONENUMERATED // 00100000 // // F0400174 HKEY hkey; if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, TEXT("CLSID\\") MYDOCS_CLSID TEXT("\\ShellFolder"), &hkey)) { DWORD dwSize = sizeof(dwAttributes); RegQueryValueEx(hkey, TEXT("Attributes"), NULL, NULL, (BYTE *)&dwAttributes, &dwSize); RegCloseKey(hkey); } return dwAttributes; } // these are the attributes from the real mydocs folder that we want to merge // in with the desktop icons attributes #define SFGAO_ATTRIBS_MERGE (SFGAO_SHARE | SFGAO_HASPROPSHEET) STDMETHODIMP CMyDocsFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfInOut) { HRESULT hr; if (IsSelf(cidl, apidl)) { DWORD dwRequested = *rgfInOut; *rgfInOut = MyDocsGetAttributes(); if (dwRequested & SFGAO_ATTRIBS_MERGE) *rgfInOut |= _GetRealMyDocsAttributes(SFGAO_ATTRIBS_MERGE); // RegItem "CallForAttributes" gets us here... switch(_WhoIsCalling()) { case APP_IS_OFFICE: *rgfInOut &= ~(SFGAO_FILESYSANCESTOR | SFGAO_CANMONIKER | SFGAO_HASPROPSHEET | SFGAO_NONENUMERATED); break; } if (SHRestricted(REST_MYDOCSNOPROP)) { (*rgfInOut) &= ~SFGAO_HASPROPSHEET; } hr = S_OK; } else { hr = _GetFolder(); if (SUCCEEDED(hr)) hr = _psf->GetAttributesOf(cidl, apidl, rgfInOut); } return hr; } STDMETHODIMP CMyDocsFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *aidl, REFIID riid, UINT *pRes, void **ppv) { HRESULT hr = _GetFolder(); if (SUCCEEDED(hr)) hr = _psf->GetUIObjectOf(hwnd, cidl, aidl, riid, pRes, ppv); return hr; } STDMETHODIMP CMyDocsFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName) { HRESULT hr; if (IsSelf(1, &pidl)) { TCHAR szMyDocsPath[MAX_PATH]; hr = SHGetFolderPath(NULL, CSIDL_PERSONAL | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szMyDocsPath); if (SUCCEEDED(hr)) { // RegItems "WantsFORPARSING" gets us here. allows us to control our parsing name LPTSTR psz = ((uFlags & SHGDN_INFOLDER) ? PathFindFileName(szMyDocsPath) : szMyDocsPath); hr = StringToStrRet(psz, pName); } } else { hr = _GetFolder(); if (SUCCEEDED(hr)) hr = _psf->GetDisplayNameOf(pidl, uFlags, pName); } return hr; } STDMETHODIMP CMyDocsFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pName, DWORD uFlags, LPITEMIDLIST *ppidlOut) { HRESULT hr = _GetFolder(); if (SUCCEEDED(hr)) hr = _psf->SetNameOf(hwnd, pidl, pName, uFlags, ppidlOut); return hr; } STDMETHODIMP CMyDocsFolder::GetDefaultSearchGUID(LPGUID lpGuid) { HRESULT hr = _GetFolder2(); if (SUCCEEDED(hr)) hr = _psf2->GetDefaultSearchGUID(lpGuid); return hr; } STDMETHODIMP CMyDocsFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum) { HRESULT hr = _GetFolder2(); if (SUCCEEDED(hr)) hr = _psf2->EnumSearches(ppenum); return hr; } STDMETHODIMP CMyDocsFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay) { HRESULT hr = _GetFolder2(); if (SUCCEEDED(hr)) hr = _psf2->GetDefaultColumn(dwRes, pSort, pDisplay); return hr; } STDMETHODIMP CMyDocsFolder::GetDefaultColumnState(UINT iColumn, DWORD *pbState) { HRESULT hr = _GetFolder2(); if (SUCCEEDED(hr)) hr = _psf2->GetDefaultColumnState(iColumn, pbState); return hr; } STDMETHODIMP CMyDocsFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv) { HRESULT hr = _GetFolder2(); if (SUCCEEDED(hr)) hr = _psf2->GetDetailsEx(pidl, pscid, pv); return hr; } STDMETHODIMP CMyDocsFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, LPSHELLDETAILS pDetail) { HRESULT hr = _GetFolder2(); if (SUCCEEDED(hr)) hr = _psf2->GetDetailsOf(pidl, iColumn, pDetail); return hr; } STDMETHODIMP CMyDocsFolder::MapColumnToSCID(UINT iCol, SHCOLUMNID *pscid) { HRESULT hr = _GetFolder2(); if (SUCCEEDED(hr)) hr = _psf2->MapColumnToSCID(iCol, pscid); return hr; } HRESULT CMyDocsFolder::_GetFolderOverlayInfo(int *pIndex, BOOL fIconIndex) { HRESULT hr; if (pIndex) { LPITEMIDLIST pidl; hr = SHGetFolderLocation(NULL, CSIDL_PERSONAL | CSIDL_FLAG_NO_ALIAS, NULL, 0, &pidl); if (SUCCEEDED(hr)) { IShellFolder *psf; LPITEMIDLIST pidlLast; hr = _BindToIDListParent(pidl, NULL, &psf, &pidlLast); if (SUCCEEDED(hr)) { IShellIconOverlay* psio; hr = psf->QueryInterface(IID_PPV_ARG(IShellIconOverlay, &psio)); if (SUCCEEDED(hr)) { if (fIconIndex) hr = psio->GetOverlayIconIndex(pidlLast, pIndex); else hr = psio->GetOverlayIndex(pidlLast, pIndex); psio->Release(); } psf->Release(); } ILFree(pidl); } } else { hr = E_INVALIDARG; } return hr; } STDMETHODIMP CMyDocsFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex) { HRESULT hr = E_FAIL; if (IsSelf(1, &pidl)) { if (pIndex && *pIndex == OI_ASYNC) hr = E_PENDING; else hr = _GetFolderOverlayInfo(pIndex, FALSE); } else { // forward to aggregated dude if (SUCCEEDED(_GetShellIconOverlay())) { hr = _psio->GetOverlayIndex(pidl, pIndex); } } return hr; } STDMETHODIMP CMyDocsFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIconIndex) { HRESULT hr = E_FAIL; if (IsSelf(1, &pidl)) { hr = _GetFolderOverlayInfo(pIconIndex, TRUE); } else if (SUCCEEDED(_GetShellIconOverlay())) { // forward to aggregated dude hr = _psio->GetOverlayIconIndex(pidl, pIconIndex); } return hr; }