Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1098 lines
34 KiB

#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;
}