windows-nt/Source/XPSP1/NT/shell/shell32/mydocsfldr.cpp
2020-09-26 16:20:57 +08:00

921 lines
26 KiB
C++

#include "shellprv.h"
#pragma hdrstop
#include <shguidp.h> // CLSID_MyDocuments, CLSID_ShellFSFolder
#include <shellp.h> // SHCoCreateInstance
#include <shlguidp.h> // 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;
}