|
|
#include "precomp.hxx"
#pragma hdrstop
#include <shguidp.h> // CLSID_ShellFSFolder
#include <shellp.h> // SHCoCreateInstance
#include <ccstock2.h> // DataObj_GetHIDA, HIDA_ReleaseStgMedium
#include <varutil.h> // VariantToBuffer
#include <stralign.h> // WSTR_ALIGNED_STACK_COPY
#include "resource.h"
#include "timewarp.h"
#include "twfldr.h"
#include "contextmenu.h"
#include "util.h"
// {9DB7A13C-F208-4981-8353-73CC61AE2783} CLSID_TimeWarpFolder
const CLSID CLSID_TimeWarpFolder = {0x9DB7A13C, 0xF208, 0x4981, {0x83, 0x53, 0x73, 0xCC, 0x61, 0xAE, 0x27, 0x83}};
const SHCOLUMNID SCID_DESCRIPTIONID = { PSGUID_SHELLDETAILS, PID_DESCRIPTIONID };
PCUIDTIMEWARP _IsValidTimeWarpID(PCUIDLIST_RELATIVE pidl) { if (pidl && pidl->mkid.cb>=sizeof(IDTIMEWARP) && ((PUIDTIMEWARP)pidl)->wSignature == TIMEWARP_SIGNATURE) return (PCUIDTIMEWARP)pidl; return NULL; }
HRESULT CTimeWarpRegFolder::CreateInstance(IUnknown* /*punkOuter*/, IUnknown **ppunk, LPCOBJECTINFO /*poi*/) { HRESULT hr;
*ppunk = NULL;
CTimeWarpRegFolder *ptwf = new CTimeWarpRegFolder(); if (ptwf) { hr = ptwf->QueryInterface(IID_PPV_ARG(IUnknown, ppunk)); ptwf->Release(); } else { hr = E_OUTOFMEMORY; }
return hr; }
CTimeWarpRegFolder::CTimeWarpRegFolder() : _cRef(1), _pmalloc(NULL), _pidl(NULL) { DllAddRef(); }
CTimeWarpRegFolder::~CTimeWarpRegFolder() { ATOMICRELEASE(_pmalloc); SHILFree((void*)_pidl); // const
DllRelease(); }
STDMETHODIMP CTimeWarpRegFolder::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CTimeWarpRegFolder, IDelegateFolder), QITABENT(CTimeWarpRegFolder, IShellFolder), QITABENTMULTI(CTimeWarpRegFolder, IPersist, IPersistFolder), QITABENT(CTimeWarpRegFolder, IPersistFolder), { 0 }, }; return QISearch(this, qit, riid, ppv); }
STDMETHODIMP_ (ULONG) CTimeWarpRegFolder::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_ (ULONG) CTimeWarpRegFolder::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
// IPersist methods
STDMETHODIMP CTimeWarpRegFolder::GetClassID(CLSID *pClassID) { *pClassID = CLSID_TimeWarpFolder; return S_OK; }
// IPersistFolder
HRESULT CTimeWarpRegFolder::Initialize(PCIDLIST_ABSOLUTE pidl) { if (_pidl) { SHILFree((void*)_pidl); // const
_pidl = NULL; } return pidl ? SHILCloneFull(pidl, &_pidl) : S_FALSE; }
// IDelegateFolder
HRESULT CTimeWarpRegFolder::SetItemAlloc(IMalloc *pmalloc) { IUnknown_Set((IUnknown**)&_pmalloc, pmalloc); return S_OK; }
// IShellFolder
STDMETHODIMP CTimeWarpRegFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pDisplayName, ULONG *pchEaten, PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes) { HRESULT hr = E_UNEXPECTED; FILETIME ftSnapTime;
TraceMsg(TF_TWREGFOLDER, "TimeWarp: parsing '%s'", pDisplayName);
// We could easily support a non-delegate mode, but we are never
// called that way, so there's no point. This check just prevents
// an AV below in the unlikely case that someone registers us
// as a non-delegate (like we used to be).
if (NULL == _pmalloc) { return E_UNEXPECTED; }
// Do this first to ensure we have a time warp path
DWORD dwErr = GetSnapshotTimeFromPath(pDisplayName, &ftSnapTime); if (ERROR_SUCCESS == dwErr) { // We only want to parse through the @GMT segment
LPWSTR pszNext = wcsstr(pDisplayName, SNAPSHOT_MARKER); if (pszNext) { pszNext += SNAPSHOT_NAME_LENGTH; ASSERT(pszNext <= pDisplayName + lstrlenW(pDisplayName)); ASSERT(*pszNext == L'\0' || *pszNext == L'\\');
USHORT cchParse = (USHORT)(pszNext - pDisplayName); USHORT cbID = sizeof(IDTIMEWARP) - FIELD_OFFSET(IDTIMEWARP,wSignature) + cchParse*sizeof(WCHAR);
ASSERT(NULL != _pmalloc); IDTIMEWARP *pid = (IDTIMEWARP*)_pmalloc->Alloc(cbID); if (pid) { ASSERT(pid->cbInner == cbID); pid->wSignature = TIMEWARP_SIGNATURE; pid->dwFlags = 0; pid->ftSnapTime = ftSnapTime; lstrcpynW(pid->wszPath, pDisplayName, cchParse+1); // +1 to allow for NULL
if (*pszNext != L'\0' && *(pszNext+1) != L'\0') { // More to parse
IShellFolder *psfRight;
// skip the separator
ASSERT(*pszNext == L'\\'); pszNext++; cchParse++;
// Bind to the child folder and ask it to parse the rest
hr = BindToObject((PCUIDLIST_RELATIVE)pid, pbc, IID_PPV_ARG(IShellFolder, &psfRight)); if (SUCCEEDED(hr)) { PIDLIST_RELATIVE pidlRight;
hr = psfRight->ParseDisplayName(hwnd, pbc, pszNext, pchEaten, &pidlRight, pdwAttributes); if (SUCCEEDED(hr)) { *pchEaten += cchParse; hr = SHILCombine((PCIDLIST_ABSOLUTE)pid, pidlRight, (PIDLIST_ABSOLUTE*)ppidl); SHILFree(pidlRight); }
psfRight->Release(); }
// Don't need this one anymore
SHFree(pid); } else { // We're stopping here. Just return what we've got.
*pchEaten = cchParse;
if (pdwAttributes) { GetAttributesOf(1, (PCUITEMID_CHILD*)&pid, pdwAttributes); }
*ppidl = (PIDLIST_RELATIVE)pid; hr = S_OK; } } else { hr = E_OUTOFMEMORY; } } }
return hr; }
STDMETHODIMP CTimeWarpRegFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppEnumIdList) { return E_NOTIMPL; }
STDMETHODIMP CTimeWarpRegFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv) { HRESULT hr = S_OK; PITEMID_CHILD pidlAlloc = NULL; BOOL bOneLevel = FALSE;
*ppv = NULL;
PCUIDLIST_RELATIVE pidlNext = ILGetNext(pidl); if (ILIsEmpty(pidlNext)) { bOneLevel = TRUE; // we know for sure it is one level
} else { hr = SHILCloneFirst(pidl, &pidlAlloc); if (SUCCEEDED(hr)) { pidl = (PCUIDLIST_RELATIVE)pidlAlloc; // a single item IDLIST
} }
if (SUCCEEDED(hr)) { if (bOneLevel) { hr = _CreateAndInit(pidl, pbc, riid, ppv); } else { IShellFolder *psfNext; hr = _CreateAndInit(pidl, pbc, IID_PPV_ARG(IShellFolder, &psfNext)); if (SUCCEEDED(hr)) { hr = psfNext->BindToObject(pidlNext, pbc, riid, ppv); psfNext->Release(); } } }
if (pidlAlloc) SHILFree(pidlAlloc); // we allocated in this case
return hr; }
STDMETHODIMP CTimeWarpRegFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv) { return E_NOTIMPL; }
// Copied from ILCompareRelIDs which has moved into shell\lib in lab06 (longhorn).
// This can be deleted in lab06.
HRESULT _CompareRelIDs(IShellFolder *psfParent, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2, LPARAM lParam) { HRESULT hr; PCUIDLIST_RELATIVE pidlRel1 = ILGetNext(pidl1); PCUIDLIST_RELATIVE pidlRel2 = ILGetNext(pidl2); if (ILIsEmpty(pidlRel1)) { if (ILIsEmpty(pidlRel2)) hr = ResultFromShort(0); else hr = ResultFromShort(-1); } else { if (ILIsEmpty(pidlRel2)) { hr = ResultFromShort(1); } else { //
// pidlRel1 and pidlRel2 point to something
// (1) Bind to the next level of the IShellFolder
// (2) Call its CompareIDs to let it compare the rest of IDs.
//
PITEMID_CHILD pidlNext; hr = SHILCloneFirst(pidl1, &pidlNext); // pidl2 would work as well
if (SUCCEEDED(hr)) { IShellFolder *psfNext; hr = psfParent->BindToObject(pidlNext, NULL, IID_PPV_ARG(IShellFolder, &psfNext)); if (SUCCEEDED(hr)) { IShellFolder2 *psf2; if (SUCCEEDED(psfNext->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2)))) { psf2->Release(); // we can use the lParam
} else { lParam = 0; // cant use the lParam
}
// columns arent valid to pass down we just care about the flags param
hr = psfNext->CompareIDs((lParam & ~SHCIDS_COLUMNMASK), pidlRel1, pidlRel2); psfNext->Release(); } ILFree(pidlNext); } } } return hr; }
STDMETHODIMP CTimeWarpRegFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2) { PCUIDTIMEWARP pidTW1 = _IsValidTimeWarpID(pidl1); PCUIDTIMEWARP pidTW2 = _IsValidTimeWarpID(pidl2);
if (!pidTW1 || !pidTW2) return E_INVALIDARG;
int iResult = ualstrcmpiW(pidTW1->wszPath, pidTW2->wszPath); if (0 != iResult) return ResultFromShort(iResult);
return _CompareRelIDs(SAFECAST(this, IShellFolder*), pidl1, pidl2, lParam); }
STDMETHODIMP CTimeWarpRegFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv) { *ppv = NULL; return E_NOTIMPL; }
STDMETHODIMP CTimeWarpRegFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, SFGAOF *rgfInOut) { // Because of the limited way in which we're invoked, we know that all
// child items are folders. Furthermore, the TimeWarp space is read-only
// so we always return the same set of attributes.
*rgfInOut = SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANCOPY | SFGAO_READONLY; return S_OK; }
STDMETHODIMP CTimeWarpRegFolder::GetUIObjectOf(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT *pRes, void **ppv) { HRESULT hr = E_NOTIMPL; PCUIDTIMEWARP pidTW = cidl ? _IsValidTimeWarpID(apidl[0]) : NULL;
ASSERT(!cidl || ILIsChild(apidl[0])); // should be single level IDs only
ASSERT(!cidl || pidTW); // should always be TimeWarp PIDLs
if (pidTW && (IsEqualIID(riid, IID_IExtractIconW) || IsEqualIID(riid, IID_IExtractIconA))) { hr = _CreateDefExtIcon(pidTW, riid, ppv); } else if (IsEqualIID(riid, IID_IContextMenu) && pidTW) { IQueryAssociations *pqa; HKEY aKeys[2] = {0};
hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa)); if (SUCCEEDED(hr)) { // CLSID_ShellFSFolder = {F3364BA0-65B9-11CE-A9BA-00AA004AE837}
hr = pqa->Init(ASSOCF_INIT_NOREMAPCLSID | ASSOCF_INIT_DEFAULTTOFOLDER, L"{F3364BA0-65B9-11CE-A9BA-00AA004AE837}", NULL, hwnd); if (SUCCEEDED(hr)) { pqa->GetKey(0, ASSOCKEY_CLASS, NULL, &aKeys[0]); pqa->GetKey(0, ASSOCKEY_BASECLASS, NULL, &aKeys[1]); } pqa->Release(); } hr = THR(CDefFolderMenu_Create2(_pidl, hwnd, cidl, apidl, SAFECAST(this, IShellFolder*), ContextMenuCB, ARRAYSIZE(aKeys), aKeys, (IContextMenu**)ppv));
for (int i = 0; i < ARRAYSIZE(aKeys); i++) { if (aKeys[i]) RegCloseKey(aKeys[i]); } } else if (IsEqualIID(riid, IID_IDataObject) && cidl) { //hr = THR(SHCreateFileDataObject(_pidl, cidl, apidl, NULL, (IDataObject**)ppv));
hr = THR(CIDLData_CreateFromIDArray(_pidl, cidl, (PCUIDLIST_RELATIVE_ARRAY)apidl, (IDataObject**)ppv)); } else if (IsEqualIID(riid, IID_IDropTarget)) { hr = E_ACCESSDENIED; }
return hr; }
STDMETHODIMP CTimeWarpRegFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD uFlags, STRRET *pName) { HRESULT hr; PCUIDTIMEWARP pidTW = _IsValidTimeWarpID(pidl);
if (pidTW) { LPCWSTR pszPath; WSTR_ALIGNED_STACK_COPY(&pszPath, pidTW->wszPath);
// If we aren't being asked for a friendly name, just use the path
if ((uFlags & SHGDN_FORPARSING) && !(uFlags & SHGDN_FORADDRESSBAR)) { pName->uType = STRRET_WSTR; hr = SHStrDup(pszPath, &pName->pOleStr); } else { PIDLIST_ABSOLUTE pidlTarget;
// Ok, we're doing the friendly date thing. Start by getting the
// target pidl without the GMT stamp.
hr = GetFSIDListFromTimeWarpPath(&pidlTarget, pszPath); if (SUCCEEDED(hr)) { WCHAR szName[MAX_PATH];
// Get the name
hr = SHGetNameAndFlagsW(pidlTarget, uFlags, szName, ARRAYSIZE(szName), NULL); if (SUCCEEDED(hr)) { ASSERT(!(uFlags & SHGDN_FORPARSING) || (uFlags & SHGDN_FORADDRESSBAR));
// Add the date string
pName->uType = STRRET_WSTR; hr = FormatFriendlyDateName(&pName->pOleStr, szName, &pidTW->ftSnapTime); } SHILFree(pidlTarget); } } } else { hr = E_INVALIDARG; }
return THR(hr); }
STDMETHODIMP CTimeWarpRegFolder::SetNameOf(HWND hwnd, PCUITEMID_CHILD pidl, LPCOLESTR pName, SHGDNF uFlags, PITEMID_CHILD *ppidlOut) { return E_NOTIMPL; }
HRESULT CTimeWarpRegFolder::_CreateAndInit(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv) { HRESULT hr = E_FAIL; PCUIDTIMEWARP pidTW = _IsValidTimeWarpID(pidl);
ASSERT(ILIsChild(pidl)); // NULL is OK
*ppv = NULL;
if (pidTW) { // Can't do normal parsing, since it validates the path each step
// of the way. The @GMT element isn't enumerated in its parent dir,
// so normal parsing fails there with ERROR_PATH_NOT_FOUND.
//
// Therefore, we can't let the FS folder parse the target path.
// Instead, we create a simple pidl here and give it to him.
PIDLIST_ABSOLUTE pidlTarget;
LPCWSTR pszPath; WSTR_ALIGNED_STACK_COPY(&pszPath, pidTW->wszPath);
hr = SimpleIDListFromAttributes(pszPath, FILE_ATTRIBUTE_DIRECTORY, &pidlTarget); if (SUCCEEDED(hr)) { PIDLIST_ABSOLUTE pidlFull; hr = SHILCombine(_pidl, pidl, &pidlFull); if (SUCCEEDED(hr)) { hr = CTimeWarpFolder::CreateInstance(CLSID_ShellFSFolder, pidlFull, pidlTarget, pszPath, &pidTW->ftSnapTime, riid, ppv); SHILFree(pidlFull); } SHILFree(pidlTarget); } }
return hr; }
HRESULT CTimeWarpRegFolder::_CreateDefExtIcon(PCUIDTIMEWARP pidTW, REFIID riid, void **ppv) { // Truncation here isn't really a problem. SHCreateFileExtractIcon
// doesn't actually require the path to exist, so it succeeds anyway.
// Worst case, you might see the wrong icon in the treeview.
WCHAR szPath[MAX_PATH]; ualstrcpynW(szPath, pidTW->wszPath, ARRAYSIZE(szPath)); EliminateGMTPathSegment(szPath); return SHCreateFileExtractIconW(szPath, FILE_ATTRIBUTE_DIRECTORY, riid, ppv); }
void _LaunchPropSheet(HWND hwnd, IDataObject *pdtobj) { STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(pdtobj, &medium); if (pida) { PCUIDTIMEWARP pidTW = _IsValidTimeWarpID(IDA_GetPIDLItem(pida, 0)); if (pidTW) { PIDLIST_ABSOLUTE pidlTarget;
LPCWSTR pszPath; WSTR_ALIGNED_STACK_COPY(&pszPath, pidTW->wszPath);
HRESULT hr = GetFSIDListFromTimeWarpPath(&pidlTarget, pszPath); if (SUCCEEDED(hr)) { SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_INVOKEIDLIST, // fMask
hwnd, // hwnd
L"properties", // lpVerb
NULL, // lpFile
NULL, // lpParameters
NULL, // lpDirectory
SW_SHOWNORMAL, // nShow
NULL, // hInstApp
pidlTarget, // lpIDList
NULL, // lpClass
0, // hkeyClass
0, // dwHotKey
NULL // hIcon
};
ShellExecuteEx(&sei); SHILFree(pidlTarget); } } HIDA_ReleaseStgMedium(pida, &medium); } }
STDMETHODIMP CTimeWarpRegFolder::ContextMenuCB(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) { HRESULT hr = E_NOTIMPL;
switch(uMsg) { case DFM_MERGECONTEXTMENU: hr = S_OK; // use default extension
break;
case DFM_INVOKECOMMANDEX: switch(wParam) { default: ASSERT(FALSE); hr = S_FALSE; // do default
break;
case DFM_CMD_PROPERTIES: // Background properties
_LaunchPropSheet(hwnd, pdtobj); hr = S_OK; break; } break;
default: break; }
return hr; }
//
// Folder implementation aggregating the file system folder
//
STDMETHODIMP CTimeWarpFolder::CreateInstance(REFCLSID rclsid, PCIDLIST_ABSOLUTE pidlRoot, PCIDLIST_ABSOLUTE pidlTarget, LPCWSTR pszTargetPath, const FILETIME UNALIGNED *pftSnapTime, REFIID riid, void **ppv) { HRESULT hr; CTimeWarpFolder *psf = new CTimeWarpFolder(pftSnapTime); if (psf) { hr = psf->_Init(rclsid, pidlRoot, pidlTarget, pszTargetPath); if (SUCCEEDED(hr)) { hr = psf->QueryInterface(riid, ppv); } psf->Release(); } else { hr = E_OUTOFMEMORY; } return hr; }
CTimeWarpFolder::CTimeWarpFolder(const FILETIME UNALIGNED *pftSnapTime) : _cRef(1), _ftSnapTime(*pftSnapTime), _punk(NULL), _psf(NULL), _psf2(NULL), _ppf3(NULL), _pidlRoot(NULL) { DllAddRef(); }
CTimeWarpFolder::~CTimeWarpFolder() { _cRef = 1000; // deal with aggregation re-enter
if (_punk) { SHReleaseInnerInterface(SAFECAST(this, IShellFolder*), (IUnknown**)&_psf); SHReleaseInnerInterface(SAFECAST(this, IShellFolder*), (IUnknown**)&_psf2); SHReleaseInnerInterface(SAFECAST(this, IShellFolder*), (IUnknown**)&_ppf3); _punk->Release(); }
SHILFree((void*)_pidlRoot); // const
DllRelease(); }
HRESULT CTimeWarpFolder::_Init(REFCLSID rclsid, PCIDLIST_ABSOLUTE pidlRoot, PCIDLIST_ABSOLUTE pidlTarget, LPCWSTR pszTargetPath) { HRESULT hr = Initialize(pidlRoot); if (hr == S_OK) { // Aggregate the real folder object (usually CLSID_ShellFSFolder)
hr = SHCoCreateInstance(NULL, &rclsid, SAFECAST(this, IShellFolder*), IID_PPV_ARG(IUnknown, &_punk)); if (SUCCEEDED(hr)) { hr = SHQueryInnerInterface(SAFECAST(this, IShellFolder*), _punk, IID_PPV_ARG(IPersistFolder3, &_ppf3)); if (SUCCEEDED(hr)) { PERSIST_FOLDER_TARGET_INFO pfti;
pfti.pidlTargetFolder = (PIDLIST_ABSOLUTE)pidlTarget; pfti.szNetworkProvider[0] = L'\0'; pfti.dwAttributes = FILE_ATTRIBUTE_DIRECTORY; pfti.csidl = -1;
// We check the target path length in CTimeWarpFolder::_CreateAndInit.
// If it's too big, we shouldn't get this far.
ASSERT(lstrlenW(pszTargetPath) < ARRAYSIZE(pfti.szTargetParsingName));
lstrcpynW(pfti.szTargetParsingName, pszTargetPath, ARRAYSIZE(pfti.szTargetParsingName));
hr = _ppf3->InitializeEx(NULL, pidlRoot, &pfti); } } } return hr; }
STDMETHODIMP CTimeWarpFolder::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENTMULTI(CTimeWarpFolder, IShellFolder, IShellFolder2), QITABENT(CTimeWarpFolder, IShellFolder2), QITABENTMULTI(CTimeWarpFolder, IPersist, IPersistFolder), QITABENT(CTimeWarpFolder, IPersistFolder), { 0 }, }; HRESULT hr = QISearch(this, qit, riid, ppv); if (FAILED(hr) && _punk) hr = _punk->QueryInterface(riid, ppv); // aggregated guy
return hr; }
STDMETHODIMP_ (ULONG) CTimeWarpFolder::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_ (ULONG) CTimeWarpFolder::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
// IPersist
STDMETHODIMP CTimeWarpFolder::GetClassID(CLSID *pClassID) { *pClassID = CLSID_TimeWarpFolder; //CLSID_ShellFSFolder?
return S_OK; }
// IPersistFolder
HRESULT CTimeWarpFolder::Initialize(PCIDLIST_ABSOLUTE pidl) { if (_pidlRoot) { SHILFree((void*)_pidlRoot); // const
_pidlRoot = NULL; }
return pidl ? SHILCloneFull(pidl, &_pidlRoot) : S_FALSE; }
HRESULT CTimeWarpFolder::_CreateAndInit(REFCLSID rclsid, PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv) { HRESULT hr = E_FAIL;
ASSERT(ILIsChild(pidl)); // NULL is OK
*ppv = NULL;
if (pidl && _ppf3) { PERSIST_FOLDER_TARGET_INFO targetInfo;
hr = _ppf3->GetFolderTargetInfo(&targetInfo); if (SUCCEEDED(hr)) { ASSERT(NULL != _pidlRoot);
// Concatenate pidl onto both _pidlRoot and targetInfo.pidlTargetFolder
PIDLIST_ABSOLUTE pidlFull; hr = SHILCombine(_pidlRoot, pidl, &pidlFull); if (SUCCEEDED(hr)) { PIDLIST_ABSOLUTE pidlTargetFull; hr = SHILCombine(targetInfo.pidlTargetFolder, pidl, &pidlTargetFull); if (SUCCEEDED(hr)) { LPWSTR pszName;
// Concatenate the child name onto targetInfo.szTargetParsingName
hr = DisplayNameOfAsOLESTR(this, ILMAKECHILD(pidl), SHGDN_INFOLDER | SHGDN_FORPARSING, &pszName); if (SUCCEEDED(hr)) { TraceMsg(TF_TWFOLDER, "TimeWarpFolder: binding to '%s'", pszName);
// IPersistFolder3 has a fixed path limit (MAX_PATH),
// which happens to be the same limit as PathAppend.
// Fail here if the name is too long.
COMPILETIME_ASSERT(ARRAYSIZE(targetInfo.szTargetParsingName) >= MAX_PATH); if (PathAppend(targetInfo.szTargetParsingName, pszName)) { hr = CTimeWarpFolder::CreateInstance(rclsid, pidlFull, pidlTargetFull, targetInfo.szTargetParsingName, &_ftSnapTime, riid, ppv); } else { hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); } LocalFree(pszName); } SHILFree(pidlTargetFull); } SHILFree(pidlFull); } SHILFree(targetInfo.pidlTargetFolder); } }
return hr; }
// verify that _psf (aggregated file system folder) has been inited
HRESULT CTimeWarpFolder::_GetFolder() { HRESULT hr = S_OK; if (_psf == NULL) hr = SHQueryInnerInterface(SAFECAST(this, IShellFolder*), _punk, IID_PPV_ARG(IShellFolder, &_psf)); return hr; }
HRESULT CTimeWarpFolder::_GetFolder2() { HRESULT hr = S_OK; if (_psf2 == NULL) hr = SHQueryInnerInterface(SAFECAST(this, IShellFolder*), _punk, IID_PPV_ARG(IShellFolder2, &_psf2)); return hr; }
HRESULT CTimeWarpFolder::_GetClass(PCUITEMID_CHILD pidlChild, CLSID *pclsid) { HRESULT hr; VARIANT varDID;
hr = GetDetailsEx(pidlChild, &SCID_DESCRIPTIONID, &varDID); if (SUCCEEDED(hr)) { SHDESCRIPTIONID did;
if (VariantToBuffer(&varDID, &did, sizeof(did))) { // Ordinary directories (non-junctions) return GUID_NULL.
if (SHDID_FS_DIRECTORY == did.dwDescriptionId && IsEqualGUID(did.clsid, GUID_NULL)) *pclsid = CLSID_ShellFSFolder; else *pclsid = did.clsid; } else { hr = E_FAIL; }
VariantClear(&varDID); } return hr; }
STDMETHODIMP CTimeWarpFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pDisplayName, ULONG *pchEaten, PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes) { HRESULT hr = _GetFolder(); if (SUCCEEDED(hr)) { hr = _psf->ParseDisplayName(hwnd, pbc, pDisplayName, pchEaten, ppidl, pdwAttributes); if (SUCCEEDED(hr) && pdwAttributes) { // Time Warp is a read-only namespace. Don't allow move, delete, etc.
*pdwAttributes = (*pdwAttributes | SFGAO_READONLY) & ~(SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_CANLINK); } } return hr; }
STDMETHODIMP CTimeWarpFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppEnumIdList) { HRESULT hr = _GetFolder(); if (SUCCEEDED(hr)) hr = _psf->EnumObjects(hwnd, grfFlags, ppEnumIdList); return hr; }
STDMETHODIMP CTimeWarpFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv) { HRESULT hr = S_OK; PCUITEMID_CHILD pidlChild = pidl; PITEMID_CHILD pidlAlloc = NULL; BOOL bOneLevel = FALSE;
*ppv = NULL;
PCUIDLIST_RELATIVE pidlNext = ILGetNext(pidl); if (ILIsEmpty(pidlNext)) { bOneLevel = TRUE; // we know for sure it is one level
} else { hr = SHILCloneFirst(pidl, &pidlAlloc); if (SUCCEEDED(hr)) { pidlChild = pidlAlloc; // a single item IDLIST
} }
if (SUCCEEDED(hr)) { CLSID clsid;
// We might be at a junction to something other than FSFolder, e.g.
// a ZIP or CAB folder, so get the CLSID of the child.
hr = _GetClass(pidlChild, &clsid); if (SUCCEEDED(hr)) { if (bOneLevel) { hr = _CreateAndInit(clsid, pidlChild, pbc, riid, ppv); } else { IShellFolder *psfNext; hr = _CreateAndInit(clsid, pidlChild, pbc, IID_PPV_ARG(IShellFolder, &psfNext)); if (SUCCEEDED(hr)) { hr = psfNext->BindToObject(pidlNext, pbc, riid, ppv); psfNext->Release(); } } }
if (FAILED(hr)) { // Return an un-aggregated object
if (SUCCEEDED(_GetFolder())) { hr = _psf->BindToObject(pidl, pbc, riid, ppv); } } }
if (pidlAlloc) SHILFree(pidlAlloc); // we allocated in this case
return hr; }
STDMETHODIMP CTimeWarpFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv) { HRESULT hr = _GetFolder(); if (SUCCEEDED(hr)) hr = _psf->BindToStorage(pidl, pbc, riid, ppv); return hr; }
STDMETHODIMP CTimeWarpFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2) { HRESULT hr = _GetFolder(); if (SUCCEEDED(hr)) hr = _psf->CompareIDs(lParam, pidl1, pidl2); return hr; }
STDMETHODIMP CTimeWarpFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv) { HRESULT hr;
*ppv = NULL;
if (IsEqualIID(riid, IID_IDropTarget)) { // Drag/drop not allowed to a timewarp folder
TraceMsg(TF_TWFOLDER, "TimeWarpFolder denying IDropTarget (CVO)"); hr = E_ACCESSDENIED; } else { hr = _GetFolder(); if (SUCCEEDED(hr)) { hr = _psf->CreateViewObject(hwnd, riid, ppv); if (SUCCEEDED(hr) && IsEqualIID(riid, IID_IContextMenu)) { // Wrap the background menu object so we can disable the New submenu
void *pvWrap; if (SUCCEEDED(Create_ContextMenuWithoutPopups((IContextMenu*)*ppv, riid, &pvWrap))) { ((IUnknown*)*ppv)->Release(); *ppv = pvWrap; } } } }
return hr; }
STDMETHODIMP CTimeWarpFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, SFGAOF *rgfInOut) { HRESULT hr = _GetFolder(); if (SUCCEEDED(hr)) hr = _psf->GetAttributesOf(cidl, apidl, rgfInOut);
// Time Warp is a read-only namespace. Don't allow move, delete, etc.
*rgfInOut = (*rgfInOut | SFGAO_READONLY) & ~(SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_CANLINK);
return hr; }
STDMETHODIMP CTimeWarpFolder::GetUIObjectOf(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT *pRes, void **ppv) { HRESULT hr = E_NOTIMPL;
ASSERT(!cidl || ILIsChild(apidl[0])); // should be single level IDs only
if (IsEqualIID(riid, IID_IDropTarget)) { TraceMsg(TF_TWFOLDER, "TimeWarpFolder denying IDropTarget (GUIOO)"); hr = E_ACCESSDENIED; } else { hr = _GetFolder(); if (SUCCEEDED(hr)) { hr = _psf->GetUIObjectOf(hwnd, cidl, apidl, riid, pRes, ppv);
if (SUCCEEDED(hr) && IsEqualIID(riid, IID_IContextMenu)) { // Wrap the menu object so we can eliminate some commands
void *pvWrap; if (SUCCEEDED(Create_ContextMenuWithoutVerbs((IContextMenu*)*ppv, L"pin;find", riid, &pvWrap))) { ((IUnknown*)*ppv)->Release(); *ppv = pvWrap; } } } }
return hr; }
STDMETHODIMP CTimeWarpFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD uFlags, STRRET *pName) { HRESULT hr = _GetFolder(); if (SUCCEEDED(hr)) { hr = _psf->GetDisplayNameOf(pidl, uFlags, pName);
// If it's for the address bar, add the friendly date string
if (SUCCEEDED(hr)&& (uFlags & SHGDN_FORADDRESSBAR)) { WCHAR szName[MAX_PATH];
// Note that this clears the STRRET
hr = StrRetToBufW(pName, pidl, szName, ARRAYSIZE(szName)); if (SUCCEEDED(hr)) { if (uFlags & SHGDN_FORPARSING) { // Remove the GMT path segment in this case
EliminateGMTPathSegment(szName); } pName->uType = STRRET_WSTR; hr = FormatFriendlyDateName(&pName->pOleStr, szName, &_ftSnapTime); } } } return hr; }
STDMETHODIMP CTimeWarpFolder::SetNameOf(HWND hwnd, PCUITEMID_CHILD pidl, LPCOLESTR pName, SHGDNF uFlags, PITEMID_CHILD *ppidlOut) { HRESULT hr = _GetFolder(); if (SUCCEEDED(hr)) hr = _psf->SetNameOf(hwnd, pidl, pName, uFlags, ppidlOut); return hr; }
STDMETHODIMP CTimeWarpFolder::GetDefaultSearchGUID(LPGUID lpGuid) { HRESULT hr = _GetFolder2(); if (SUCCEEDED(hr)) hr = _psf2->GetDefaultSearchGUID(lpGuid); return hr; }
STDMETHODIMP CTimeWarpFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum) { HRESULT hr = _GetFolder2(); if (SUCCEEDED(hr)) hr = _psf2->EnumSearches(ppenum); return hr; }
STDMETHODIMP CTimeWarpFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay) { HRESULT hr = _GetFolder2(); if (SUCCEEDED(hr)) hr = _psf2->GetDefaultColumn(dwRes, pSort, pDisplay); return hr; }
STDMETHODIMP CTimeWarpFolder::GetDefaultColumnState(UINT iColumn, DWORD *pbState) { HRESULT hr = _GetFolder2(); if (SUCCEEDED(hr)) hr = _psf2->GetDefaultColumnState(iColumn, pbState); return hr; }
STDMETHODIMP CTimeWarpFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv) { HRESULT hr = _GetFolder2(); if (SUCCEEDED(hr)) hr = _psf2->GetDetailsEx(pidl, pscid, pv); return hr; }
STDMETHODIMP CTimeWarpFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, LPSHELLDETAILS pDetail) { HRESULT hr = _GetFolder2(); if (SUCCEEDED(hr)) hr = _psf2->GetDetailsOf(pidl, iColumn, pDetail); return hr; }
STDMETHODIMP CTimeWarpFolder::MapColumnToSCID(UINT iCol, SHCOLUMNID *pscid) { HRESULT hr = _GetFolder2(); if (SUCCEEDED(hr)) hr = _psf2->MapColumnToSCID(iCol, pscid); return hr; }
|