|
|
#include "shellprv.h"
class CLocalCopyHelper : public ILocalCopy , public IItemHandler { public: CLocalCopyHelper(); // IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef () ; STDMETHODIMP_(ULONG) Release ();
// ILocalCopy methods
STDMETHODIMP Download(LCFLAGS flags, IBindCtx *pbc, LPWSTR *ppszOut); STDMETHODIMP Upload(LCFLAGS flags, IBindCtx *pbc);
// IPersist
STDMETHODIMP GetClassID(CLSID *pclsid) { *pclsid = CLSID_LocalCopyHelper; return S_OK;}
// IItemHandler
STDMETHODIMP SetItem(IShellItem *psi); STDMETHODIMP GetItem(IShellItem **ppsi);
protected: ~CLocalCopyHelper();
// private methods
HRESULT _InitCacheEntry(void); HRESULT _SetCacheName(void); HRESULT _FinishLocal(BOOL fReadOnly); HRESULT _GetLocalStream(DWORD grfMode, IStream **ppstm, FILETIME *pft); HRESULT _GetRemoteStream(DWORD grfMode, IBindCtx *pbc, IStream **ppstm, FILETIME *pft);
// members
long _cRef; IShellItem *_psi; LPWSTR _pszName; // name retrieved from psi
LPWSTR _pszCacheName; // name used to ID cache entry
LPCWSTR _pszExt; // points into _pszName
// caches of the MTIMEs for the streams
FILETIME _ftRemoteGet; FILETIME _ftLocalGet; FILETIME _ftRemoteCommit; FILETIME _ftLocalCommit; BOOL _fIsLocalFile; // this is actually file system item (pszName is a FS path)
BOOL _fMadeLocal; // we have already copied this item locally
// put this at the end so we can see all the rest of the pointers easily in debug
WCHAR _szLocalPath[MAX_PATH]; };
CLocalCopyHelper::CLocalCopyHelper() : _cRef(1) { }
CLocalCopyHelper::~CLocalCopyHelper() { ATOMICRELEASE(_psi);
if (_pszName) CoTaskMemFree(_pszName);
if (_pszCacheName) LocalFree(_pszCacheName); } STDMETHODIMP CLocalCopyHelper::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CLocalCopyHelper, ILocalCopy), QITABENT(CLocalCopyHelper, IItemHandler), QITABENTMULTI(CLocalCopyHelper, IPersist, IItemHandler), { 0 }, };
return QISearch(this, qit, riid, ppv); }
STDMETHODIMP_(ULONG) CLocalCopyHelper::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CLocalCopyHelper::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
STDMETHODIMP CLocalCopyHelper::SetItem(IShellItem *psi) { if (!_psi) { SFGAOF flags = SFGAO_STREAM; if (SUCCEEDED(psi->GetAttributes(flags, &flags)) && (flags & SFGAO_STREAM)) { HRESULT hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &_pszName);
if (SUCCEEDED(hr)) { _fIsLocalFile = TRUE; } else hr = psi->GetDisplayName(SIGDN_PARENTRELATIVEEDITING, &_pszName);
if (SUCCEEDED(hr)) { _psi = psi; _psi->AddRef(); }
return hr; } } return E_UNEXPECTED; }
STDMETHODIMP CLocalCopyHelper::GetItem(IShellItem **ppsi) { *ppsi = _psi;
if (_psi) { _psi->AddRef(); return S_OK; } else return E_UNEXPECTED; }
#define SZTEMPURL TEXTW("temp:")
#define CCHTEMPURL SIZECHARS(SZTEMPURL) -1
HRESULT CLocalCopyHelper::_SetCacheName(void) { ASSERT(!_pszCacheName); int cchCacheName = lstrlenW(_pszName) + CCHTEMPURL + 1; _pszCacheName = (LPWSTR) LocalAlloc(LPTR, CbFromCchW(cchCacheName)); if (_pszCacheName) { LPCWSTR pszName = _pszName;
StringCchCopy(_pszCacheName, cchCacheName, SZTEMPURL); StringCchCat(_pszCacheName, cchCacheName, _pszName);
if (UrlIs(_pszName, URLIS_URL)) { // need to push past all slashes
pszName = StrRChr(pszName, NULL, TEXT('/')); } // the cache APIs need the extension without the dot
if (pszName) { _pszExt = PathFindExtension(pszName); if (*_pszExt) _pszExt++; }
return S_OK; }
return E_OUTOFMEMORY; }
void _GetMTime(IStream *pstm, FILETIME *pft) { // see if we can get an accurate Mod time
STATSTG stat; if (S_OK == pstm->Stat(&stat, STATFLAG_NONAME)) *pft = stat.mtime; else { GetSystemTimeAsFileTime(pft); } }
HRESULT CLocalCopyHelper::_InitCacheEntry(void) { if (!_pszCacheName) { HRESULT hr = _SetCacheName();
if (SUCCEEDED(hr) && !CreateUrlCacheEntryW(_pszCacheName, 0, _pszExt, _szLocalPath, 0)) { hr = HRESULT_FROM_WIN32(GetLastError()); LocalFree(_pszCacheName); _pszCacheName = NULL; }
return hr; } return S_OK; }
HRESULT CLocalCopyHelper::_GetLocalStream(DWORD grfMode, IStream **ppstm, FILETIME *pft) { HRESULT hr = _InitCacheEntry(); if (SUCCEEDED(hr)) { hr = SHCreateStreamOnFileW(_szLocalPath, grfMode, ppstm);
if (SUCCEEDED(hr)) _GetMTime(*ppstm, pft); }
return hr; }
HRESULT CLocalCopyHelper::_FinishLocal(BOOL fReadOnly) { HRESULT hr = S_OK; FILETIME ftExp = {0}; if (CommitUrlCacheEntryW(_pszCacheName, _szLocalPath, ftExp, _ftLocalGet, STICKY_CACHE_ENTRY, NULL, 0, NULL, NULL)) { // we could also check _GetRemoteStream(STGM_WRITE)
// and if it fails we could fail this as well.
if (fReadOnly) SetFileAttributesW(_szLocalPath, FILE_ATTRIBUTE_READONLY); } else hr = HRESULT_FROM_WIN32(GetLastError());
return hr; }
HRESULT CLocalCopyHelper::_GetRemoteStream(DWORD grfMode, IBindCtx *pbc, IStream **ppstm, FILETIME *pft) { HRESULT hr = E_OUTOFMEMORY; if (!pbc) CreateBindCtx(0, &pbc); else pbc->AddRef(); if (pbc) { BIND_OPTS bo = {sizeof(bo)}; // Requires size filled in.
if (SUCCEEDED(pbc->GetBindOptions(&bo))) { bo.grfMode = grfMode; pbc->SetBindOptions(&bo); }
hr = _psi->BindToHandler(pbc, BHID_Storage, IID_PPV_ARG(IStream, ppstm));
if (SUCCEEDED(hr)) _GetMTime(*ppstm, pft); pbc->Release(); }
return hr; }
STDMETHODIMP CLocalCopyHelper::Download(LCFLAGS flags, IBindCtx *pbc, LPWSTR *ppsz) { if (!_psi) return E_UNEXPECTED; HRESULT hr; if (_fIsLocalFile) { hr = SHStrDup(_pszName, ppsz); } else if (_fMadeLocal && !(flags & LC_FORCEROUNDTRIP)) { hr = S_OK; } else if (flags & LC_SAVEAS) { hr = _InitCacheEntry(); if (SUCCEEDED(hr)) { _fMadeLocal = TRUE; } } else { // get the local stream first because it is the cheapest operation.
IStream *pstmDst; hr = _GetLocalStream(STGM_WRITE, &pstmDst, &_ftLocalGet);
if (SUCCEEDED(hr)) { // we need to create the temp file here
IStream *pstmSrc; hr = _GetRemoteStream(STGM_READ, pbc, &pstmSrc, &_ftRemoteGet);
if (SUCCEEDED(hr)) { hr = CopyStreamUI(pstmSrc, pstmDst, NULL, 0);
pstmSrc->Release(); // now that we have copied the stream
}
pstmDst->Release();
// need to release teh dest stream first
if (SUCCEEDED(hr)) { // finish cleaning up the local file
hr = _FinishLocal(flags & LCDOWN_READONLY); _fMadeLocal = SUCCEEDED(hr); } }
}
if (_fMadeLocal) { ASSERT(SUCCEEDED(hr)); hr = SHStrDup(_szLocalPath, ppsz); } else ASSERT(_fIsLocalFile || FAILED(hr));
return hr; }
STDMETHODIMP CLocalCopyHelper::Upload(LCFLAGS flags, IBindCtx *pbc) { if (!_psi) return E_UNEXPECTED;
HRESULT hr = S_OK; if (!_fIsLocalFile) { // get the local stream first because it is the cheapest operation.
IStream *pstmSrc; hr = _GetLocalStream(STGM_READ, &pstmSrc, &_ftLocalCommit);
if (SUCCEEDED(hr)) { DWORD stgmRemote = STGM_WRITE;
if (flags & LC_SAVEAS) { hr = _FinishLocal(FALSE); stgmRemote |= STGM_CREATE; } if (SUCCEEDED(hr)) { IStream *pstmDst; hr = _GetRemoteStream(stgmRemote, pbc, &pstmDst, &_ftRemoteCommit);
if (SUCCEEDED(hr)) { // we only bother copying when the local copy changed
// or caller forces us to.
//
// FEATURE - UI needs to handle when the remot changes
// if the remote copy changes while the local
// copy is being updated we will overwrite the remote copy
// local changes WIN!
//
if (flags & LC_FORCEROUNDTRIP || 0 != CompareFileTime(&_ftLocalCommit, &_ftLocalGet)) hr = CopyStreamUI(pstmSrc, pstmDst, NULL, 0); else hr = S_FALSE; pstmDst->Release(); }
}
pstmSrc->Release(); } }
return hr; } STDAPI CLocalCopyHelper_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { HRESULT hr; CLocalCopyHelper * p = new CLocalCopyHelper();
if (p) { hr = p->QueryInterface(riid, ppv); p->Release(); } else { *ppv = NULL; hr = E_OUTOFMEMORY; } return hr; }
|