|
|
#include "shellprv.h"
#include "util.h"
#include "datautil.h"
#include "idlcomm.h"
#include "stgutil.h"
#include "ole2dup.h"
// determines if a string pszChild refers to a stream/storage that exists
// in the parent storage pStorageParent.
STDAPI_(BOOL) StgExists(IStorage * pStorageParent, LPCTSTR pszChild) { BOOL fResult = FALSE; WCHAR wszChild[MAX_PATH]; HRESULT hr; DWORD grfModeOpen = STGM_READ; IStream * pStreamOpened;
RIPMSG(pszChild && IS_VALID_STRING_PTR(pszChild, -1), "StgExists: caller passed bad pszPath");
SHTCharToUnicode(pszChild, wszChild, ARRAYSIZE(wszChild));
hr = pStorageParent->OpenStream(wszChild, NULL, grfModeOpen, 0, &pStreamOpened);
if (SUCCEEDED(hr)) { pStreamOpened->Release(); fResult = TRUE; } else { IStorage * pStorageOpened; hr = pStorageParent->OpenStorage(wszChild, NULL, grfModeOpen, NULL, 0, &pStorageOpened);
if (SUCCEEDED(hr)) { pStorageOpened->Release(); fResult = TRUE; } }
return fResult; }
STDAPI StgCopyFileToStream(LPCTSTR pszSrc, IStream *pStream) { IStream *pStreamSrc; DWORD grfModeSrc = STGM_READ | STGM_DIRECT | STGM_SHARE_DENY_WRITE; HRESULT hr = SHCreateStreamOnFileEx(pszSrc, grfModeSrc, 0, FALSE, NULL, &pStreamSrc);
if (SUCCEEDED(hr)) { ULARGE_INTEGER ulMax = {-1, -1}; hr = pStreamSrc->CopyTo(pStream, ulMax, NULL, NULL); pStreamSrc->Release(); }
if (SUCCEEDED(hr)) { hr = pStream->Commit(STGC_DEFAULT); }
return hr; }
STDAPI StgDeleteUsingDataObject(HWND hwnd, UINT uFlags, IDataObject *pdtobj) { // TODO: stick this into aidan's pidl storage and delete via dave's engine
HRESULT hr = E_FAIL; STGMEDIUM medium; LPIDA pida = DataObj_GetHIDA(pdtobj, &medium); if (pida) { IStorage *pstg; LPCITEMIDLIST pidlFolder = IDA_GetIDListPtr(pida, -1); hr = StgBindToObject(pidlFolder, STGM_READWRITE, IID_PPV_ARG(IStorage, &pstg)); if (SUCCEEDED(hr)) { IShellFolder *psf; hr = pstg->QueryInterface(IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { for (UINT i = 0; (i < pida->cidl) && SUCCEEDED(hr); i++) { LPCITEMIDLIST pidl = IDA_GetIDListPtr(pida, i); WCHAR wzName[MAX_PATH]; hr = DisplayNameOf(psf, pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, wzName, ARRAYSIZE(wzName)); if (SUCCEEDED(hr)) { hr = pstg->DestroyElement(wzName); } }
if (SUCCEEDED(hr)) hr = pstg->Commit(STGC_DEFAULT); SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, pidlFolder, NULL); psf->Release(); } pstg->Release(); }
HIDA_ReleaseStgMedium(pida, &medium); }
return hr; }
STDAPI StgBindToObject(LPCITEMIDLIST pidl, DWORD grfMode, REFIID riid, void **ppv) { IBindCtx *pbc; HRESULT hr = BindCtx_CreateWithMode(grfMode, &pbc); if (SUCCEEDED(hr)) { hr = SHBindToObjectEx(NULL, pidl, pbc, riid, ppv);
pbc->Release(); } return hr; }
typedef HRESULT (WINAPI * PSTGOPENSTORAGEONHANDLE)(HANDLE,DWORD,void*,void*,REFIID,void**);
STDAPI SHStgOpenStorageOnHandle(HANDLE h, DWORD grfMode, void *res1, void *res2, REFIID riid, void **ppv) { static PSTGOPENSTORAGEONHANDLE pfn = NULL; if (pfn == NULL) { HMODULE hmodOle32 = LoadLibraryA("ole32.dll");
if (hmodOle32) { pfn = (PSTGOPENSTORAGEONHANDLE)GetProcAddress(hmodOle32, "StgOpenStorageOnHandle"); } }
if (pfn) { return pfn(h, grfMode, res1, res2, riid, ppv); } else { return E_OUTOFMEMORY; } }
STDAPI StgOpenStorageOnFolder(LPCTSTR pszFolder, DWORD grfFlags, REFIID riid, void **ppv) { *ppv = NULL;
DWORD dwDesiredAccess, dwShareMode, dwCreationDisposition; HRESULT hr = ModeToCreateFileFlags(grfFlags, FALSE, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); if (SUCCEEDED(hr)) { // For IPropertySetStorage, we don't want to unnecessarily tie up access to the folder, if all
// we're doing is dealing with property sets. The implementation of IPropertySetStorage for
// NTFS files is defined so that the sharing/access only applies to the property set stream, not
// it's other streams. So it makes sense to do a CreateFile on a folder with full sharing, while perhaps specifying
// STGM_SHARE_EXCLUSIVE for the property set storage.
if (riid == IID_IPropertySetStorage) dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
HANDLE h = CreateFile(pszFolder, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_FLAG_BACKUP_SEMANTICS, INVALID_HANDLE_VALUE); if (INVALID_HANDLE_VALUE != h) { hr = SHStgOpenStorageOnHandle(h, grfFlags, NULL, NULL, riid, ppv); CloseHandle(h); } else { hr = HRESULT_FROM_WIN32(GetLastError()); } } return hr; }
class CDocWrapperStorage : public IStorage { public: CDocWrapperStorage(LPCTSTR szPath, CDocWrapperStorage *pstgParent, IStorage *pstg); ~CDocWrapperStorage();
public: // IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release();
// IStorage
STDMETHODIMP Revert() { return _pstgInner->Revert(); } STDMETHODIMP DestroyElement(LPCWSTR pszRel) { return _pstgInner->DestroyElement(pszRel); } STDMETHODIMP RenameElement(LPCWSTR pwcsOldName, LPCWSTR pwcsNewName) { return _pstgInner->RenameElement(pwcsOldName, pwcsNewName); } STDMETHODIMP SetElementTimes(LPCWSTR pszRel, const FILETIME *pctime, const FILETIME *patime, const FILETIME *pmtime) { return _pstgInner->SetElementTimes(pszRel, pctime, patime, pmtime); } STDMETHODIMP SetClass(REFCLSID clsid) { return _pstgInner->SetClass(clsid); } STDMETHODIMP SetStateBits(DWORD grfStateBits, DWORD grfMask) { return _pstgInner->SetStateBits(grfStateBits, grfMask); } STDMETHODIMP Stat(STATSTG *pstatstg, DWORD grfStatFlag) { return _pstgInner->Stat(pstatstg, grfStatFlag); } STDMETHODIMP CopyTo(DWORD ciidExclude, const IID *rgiidExclude, SNB snbExclude, IStorage *pstgDest) { return _pstgInner->CopyTo(ciidExclude, rgiidExclude, snbExclude, pstgDest); } STDMETHODIMP MoveElementTo(LPCWSTR pszRel, IStorage *pstgDest, LPCWSTR pwcsNewName, DWORD grfFlags) { return _pstgInner->MoveElementTo(pszRel, pstgDest, pwcsNewName, grfFlags); } STDMETHODIMP EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3, IEnumSTATSTG **ppenum) { return _pstgInner->EnumElements(reserved1, reserved2, reserved3, ppenum); }
STDMETHODIMP Commit(DWORD grfCommitFlags); STDMETHODIMP CreateStream(LPCWSTR pwcsName, DWORD grfMode, DWORD res1, DWORD res2, IStream **ppstm); STDMETHODIMP OpenStream(LPCWSTR pszRel, VOID *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm); STDMETHODIMP CreateStorage(LPCWSTR pwcsName, DWORD grfMode, DWORD res1, DWORD res2, IStorage **ppstg); STDMETHODIMP OpenStorage(LPCWSTR pszRel, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstg);
protected: LONG _cRef;
IStorage *_pstgInner; CDocWrapperStorage *_pstgParent; TCHAR _szPath[MAX_PATH];
STDMETHODIMP _MakeNewStorage(DWORD grfMode, CDocWrapperStorage **ppwstg); STDMETHODIMP _GetRelPath(LPTSTR pszRelPath); STDMETHODIMP _GetNewRootStorage(DWORD grfMode, CDocWrapperStorage **ppwstgRoot); STDMETHODIMP _ReOpen(LPCTSTR pszRelPath, DWORD grfMode, CDocWrapperStorage **ppwstg); STDMETHODIMP _OpenStorage(LPCWSTR pszRel, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, CDocWrapperStorage **ppwstg); };
class CDocWrapperStream : public IStream { public: CDocWrapperStream(CDocWrapperStorage *pstgParent, IStream *pstm); ~CDocWrapperStream();
public: // IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release();
// IStream
STDMETHODIMP Read(void *pv, ULONG cb, ULONG *pcbRead) { return _pstmInner->Read(pv, cb, pcbRead); } STDMETHODIMP Write(void const *pv, ULONG cb, ULONG *pcbWritten) { return _pstmInner->Write(pv, cb, pcbWritten); } STDMETHODIMP Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) { return _pstmInner->Seek(dlibMove, dwOrigin, plibNewPosition); } STDMETHODIMP SetSize(ULARGE_INTEGER libNewSize) { return _pstmInner->SetSize(libNewSize); } STDMETHODIMP CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) { return _pstmInner->CopyTo(pstm, cb, pcbRead, pcbWritten); } STDMETHODIMP Commit(DWORD grfCommitFlags) { return _pstmInner->Commit(grfCommitFlags); } STDMETHODIMP Revert() { return _pstmInner->Revert(); } STDMETHODIMP LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { return _pstmInner->LockRegion(libOffset, cb, dwLockType); } STDMETHODIMP UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { return _pstmInner->UnlockRegion(libOffset, cb, dwLockType); } STDMETHODIMP Clone(IStream **ppstm) { return _pstmInner->Clone(ppstm); } STDMETHODIMP Stat(STATSTG *pstatstg, DWORD grfStatFlag) { return _pstmInner->Stat(pstatstg, grfStatFlag); }
protected: LONG _cRef;
IStream *_pstmInner; CDocWrapperStorage *_pstgParent; };
// this switches up the grfMode for elements in a docfile.
// docfiles are limited to ALWAYS opening elements in exclusive mode, so we
// enforce that here.
DWORD _MungeModeForElements(DWORD grfMode) { grfMode &= ~(STGM_TRANSACTED | STGM_SHARE_DENY_NONE | STGM_SHARE_DENY_WRITE | STGM_SHARE_DENY_READ); grfMode |= STGM_DIRECT | STGM_SHARE_EXCLUSIVE;
return grfMode; }
// this switches up the grfMode for the root storage for a docfile.
// if we open the root in exclusive mode, then we're in trouble for sure since
// nobody else can get to any of the elements.
// the root is the only guy that docfiles can share too so we take advantage
// of that by enforcing STGM_TRANSACTED and STGM_SHARE_DENY_NONE.
DWORD _MungeModeForRoot(DWORD grfMode) { grfMode &= ~(STGM_DIRECT | STGM_SHARE_DENY_READ | STGM_SHARE_DENY_WRITE | STGM_SHARE_EXCLUSIVE); grfMode |= STGM_TRANSACTED | STGM_SHARE_DENY_NONE;
return grfMode; }
STDAPI StgGetStorageFromFile(LPCWSTR wzPath, DWORD grfMode, IStorage **ppstg) { IStorage *pstgUnwrapped; HRESULT hr = StgOpenStorageEx(wzPath, _MungeModeForRoot(grfMode), STGFMT_ANY, 0, NULL, NULL, IID_PPV_ARG(IStorage, &pstgUnwrapped));
if (SUCCEEDED(hr)) { // wrap the docfile storage.
CDocWrapperStorage *pwstg = new CDocWrapperStorage(wzPath, NULL, pstgUnwrapped); if (pwstg) { hr = pwstg->QueryInterface(IID_PPV_ARG(IStorage, ppstg)); pwstg->Release(); } else { hr = E_OUTOFMEMORY; } pstgUnwrapped->Release(); } return hr; }
// CDocWrapperStorage
CDocWrapperStorage::CDocWrapperStorage(LPCTSTR pszPath, CDocWrapperStorage *pstgParent, IStorage *pstg) : _cRef(1) { _pstgParent = pstgParent; if (_pstgParent) _pstgParent->AddRef();
_pstgInner = pstg; _pstgInner->AddRef();
// if this is the root docfile storage, keep track of the fs path that was used to
// open it (since we might need to open it again).
lstrcpyn(_szPath, pszPath, ARRAYSIZE(_szPath));
DllAddRef(); }
CDocWrapperStorage::~CDocWrapperStorage() { ATOMICRELEASE(_pstgParent); ATOMICRELEASE(_pstgInner);
DllRelease(); }
// IUnknown
STDMETHODIMP_(ULONG) CDocWrapperStorage::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CDocWrapperStorage::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
HRESULT CDocWrapperStorage::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CDocWrapperStorage, IStorage), // IID_IStorage
{ 0 }, }; // NOTE: this will fail on IPropertySetStorage.
// can the docfile storage be aggregated?
// in any case the IPropertySetStorage routines don't need to use this
// wrapper for any lifetime issues.
return QISearch(this, qit, riid, ppv); }
// IStorage
STDMETHODIMP CDocWrapperStorage::Commit(DWORD grfCommitFlags) { HRESULT hr = _pstgInner->Commit(grfCommitFlags); if (_pstgParent && SUCCEEDED(hr)) hr = _pstgParent->Commit(grfCommitFlags); return hr; }
STDMETHODIMP CDocWrapperStorage::CreateStream(LPCWSTR pwcsName, DWORD grfMode, DWORD res1, DWORD res2, IStream **ppstm) { IStream *pstm; HRESULT hr = _pstgInner->CreateStream(pwcsName, _MungeModeForElements(grfMode), res1, res2, &pstm); if (SUCCEEDED(hr)) { CDocWrapperStream *pwstm = new CDocWrapperStream(this, pstm); if (pwstm) { hr = pwstm->QueryInterface(IID_PPV_ARG(IStream, ppstm)); pwstm->Release(); } else { hr = E_OUTOFMEMORY; } pstm->Release(); } return hr; }
STDMETHODIMP CDocWrapperStorage::CreateStorage(LPCWSTR pwcsName, DWORD grfMode, DWORD res1, DWORD res2, IStorage **ppstg) { IStorage *pstg; HRESULT hr = _pstgInner->CreateStorage(pwcsName, _MungeModeForElements(grfMode), res1, res2, &pstg); if (SUCCEEDED(hr)) { CDocWrapperStorage *pwstg = new CDocWrapperStorage(NULL, this, pstg); if (pwstg) { hr = pwstg->QueryInterface(IID_PPV_ARG(IStorage, ppstg)); pwstg->Release(); } else { hr = E_OUTOFMEMORY; } pstg->Release(); } return hr; }
// TODO: move this away from a MAX_PATH total length restriction
// gets a "path" relative from the root storage down to this element.
// see comments for _MakeNewStorage.
STDMETHODIMP CDocWrapperStorage::_GetRelPath(LPTSTR pszRelPath) { HRESULT hr = S_OK; if (_pstgParent) { // first get the path up to here from the root.
hr = _pstgParent->_GetRelPath(pszRelPath); if (SUCCEEDED(hr)) { STATSTG stat; hr = Stat(&stat, STATFLAG_DEFAULT); if (SUCCEEDED(hr)) { // now append the name of this element.
if (!PathAppend(pszRelPath, stat.pwcsName)) hr = E_FAIL; CoTaskMemFree(stat.pwcsName); } } } else { // we are the root, so init the string.
pszRelPath[0] = 0; } return hr; }
// opens another instance of the root storage.
// see comments for _MakeNewStorage.
STDMETHODIMP CDocWrapperStorage::_GetNewRootStorage(DWORD grfMode, CDocWrapperStorage **ppwstgRoot) { HRESULT hr = S_OK; if (_pstgParent) { // get it from our parent.
hr = _pstgParent->_GetNewRootStorage(grfMode, ppwstgRoot); } else { // we are the root.
IStorage *pstgUnwrapped; hr = StgOpenStorageEx(_szPath, _MungeModeForRoot(grfMode), STGFMT_ANY, 0, NULL, NULL, IID_PPV_ARG(IStorage, &pstgUnwrapped)); if (SUCCEEDED(hr)) { *ppwstgRoot = new CDocWrapperStorage(_szPath, NULL, pstgUnwrapped); if (!*ppwstgRoot) hr = E_OUTOFMEMORY; pstgUnwrapped->Release(); } } return hr; }
// opens storages using the "path" in pszRelPath.
// see comments for _MakeNewStorage.
STDMETHODIMP CDocWrapperStorage::_ReOpen(LPCTSTR pszRelPath, DWORD grfMode, CDocWrapperStorage **ppwstg) { HRESULT hr = S_OK; // no relative path signifies that we want this storage itself
if (!pszRelPath || !pszRelPath[0]) { *ppwstg = this; AddRef(); } else { TCHAR szElementName[MAX_PATH]; hr = _NextSegment(&pszRelPath, szElementName, ARRAYSIZE(szElementName), TRUE); if (SUCCEEDED(hr)) { CDocWrapperStorage *pwstg; hr = _OpenStorage(szElementName, NULL, grfMode, NULL, 0, &pwstg); if (SUCCEEDED(hr)) { hr = pwstg->_ReOpen(pszRelPath, grfMode, ppwstg); pwstg->Release(); } } } return hr; }
// NOTE: this is needed if we want to open an element of the docfile in non-exclusive mode.
// to do that we need to get back to the root, open another copy of the root in
// non-exclusive mode, and then come back down.
STDMETHODIMP CDocWrapperStorage::_MakeNewStorage(DWORD grfMode, CDocWrapperStorage **ppwstg) { TCHAR szRelPath[MAX_PATH]; HRESULT hr = _GetRelPath(szRelPath); if (SUCCEEDED(hr)) { CDocWrapperStorage *pwstgRoot; hr = _GetNewRootStorage(grfMode, &pwstgRoot); if (SUCCEEDED(hr)) { hr = pwstgRoot->_ReOpen(szRelPath, grfMode, ppwstg); pwstgRoot->Release(); } } return hr; }
STDMETHODIMP CDocWrapperStorage::OpenStream(LPCWSTR pwcsName, void *res1, DWORD grfMode, DWORD res2, IStream **ppstm) { IStream *pstm; HRESULT hr = _pstgInner->OpenStream(pwcsName, res1, _MungeModeForElements(grfMode), res2, &pstm);
if (hr == STG_E_ACCESSDENIED) { // we're in trouble -- the stream has been opened with SHARE_EXCLUSIVE already
// so we need to back up to the root storage, open another instance of that,
// and come back down here.
CDocWrapperStorage *pstgNew; hr = _MakeNewStorage(grfMode, &pstgNew); if (SUCCEEDED(hr)) { hr = pstgNew->OpenStream(pwcsName, res1, grfMode, res2, ppstm); pstgNew->Release(); } } else if (SUCCEEDED(hr)) { CDocWrapperStream *pwstm = new CDocWrapperStream(this, pstm); if (pwstm) { hr = pwstm->QueryInterface(IID_PPV_ARG(IStream, ppstm)); pwstm->Release(); } else { hr = E_OUTOFMEMORY; } pstm->Release(); }
return hr; }
STDMETHODIMP CDocWrapperStorage::_OpenStorage(LPCWSTR pwcsName, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD res, CDocWrapperStorage **ppwstg) { IStorage *pstg; HRESULT hr = _pstgInner->OpenStorage(pwcsName, pstgPriority, _MungeModeForElements(grfMode), snbExclude, res, &pstg); if (hr == STG_E_ACCESSDENIED) { // we're in trouble -- the storage has been opened with SHARE_EXCLUSIVE already
// so we need to back up to the root storage, open another instance of that,
// and come back down here.
CDocWrapperStorage *pstgNew; hr = _MakeNewStorage(grfMode, &pstgNew); if (SUCCEEDED(hr)) { hr = pstgNew->_OpenStorage(pwcsName, pstgPriority, grfMode, snbExclude, res, ppwstg); pstgNew->Release(); } } else if (SUCCEEDED(hr)) { *ppwstg = new CDocWrapperStorage(NULL, this, pstg); if (!*ppwstg) hr = E_OUTOFMEMORY; pstg->Release(); } return hr; }
STDMETHODIMP CDocWrapperStorage::OpenStorage(LPCWSTR pwcsName, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD res, IStorage **ppstg) { CDocWrapperStorage *pwstg; HRESULT hr = _OpenStorage(pwcsName, pstgPriority, grfMode, snbExclude, res, &pwstg); if (SUCCEEDED(hr)) { hr = pwstg->QueryInterface(IID_PPV_ARG(IStorage, ppstg)); pwstg->Release(); } else hr = E_OUTOFMEMORY;
return hr; }
// CDocWrapperStream
CDocWrapperStream::CDocWrapperStream(CDocWrapperStorage *pstgParent, IStream *pstm) : _cRef(1) { _pstgParent = pstgParent; _pstgParent->AddRef();
_pstmInner = pstm; _pstmInner->AddRef();
DllAddRef(); }
CDocWrapperStream::~CDocWrapperStream() { ATOMICRELEASE(_pstgParent); ATOMICRELEASE(_pstmInner);
DllRelease(); }
// IUnknown
STDMETHODIMP_(ULONG) CDocWrapperStream::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CDocWrapperStream::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
HRESULT CDocWrapperStream::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CDocWrapperStream, IStream), // IID_IStream
{ 0 }, }; return QISearch(this, qit, riid, ppv); }
// CShortcutStorage
class CShortcutStorage : public IStorage { public: CShortcutStorage(IStorage *pstg); ~CShortcutStorage();
public: // IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release();
// IStorage
STDMETHODIMP CreateStream(LPCWSTR pszRel, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStream **ppstm) { return _pstgInner->CreateStream(pszRel, grfMode, reserved1, reserved2, ppstm); } STDMETHODIMP CreateStorage(LPCWSTR pszRel, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStorage **ppstg) { return _pstgInner->CreateStorage(pszRel, grfMode, reserved1, reserved2, ppstg); } STDMETHODIMP Commit(DWORD grfCommitFlags) { return _pstgInner->Commit(grfCommitFlags); } STDMETHODIMP Revert() { return _pstgInner->Revert(); } STDMETHODIMP DestroyElement(LPCWSTR pszRel) { return _pstgInner->DestroyElement(pszRel); } STDMETHODIMP RenameElement(LPCWSTR pwcsOldName, LPCWSTR pwcsNewName) { return _pstgInner->RenameElement(pwcsOldName, pwcsNewName); } STDMETHODIMP SetElementTimes(LPCWSTR pszRel, const FILETIME *pctime, const FILETIME *patime, const FILETIME *pmtime) { return _pstgInner->SetElementTimes(pszRel, pctime, patime, pmtime); } STDMETHODIMP SetClass(REFCLSID clsid) { return _pstgInner->SetClass(clsid); } STDMETHODIMP SetStateBits(DWORD grfStateBits, DWORD grfMask) { return _pstgInner->SetStateBits(grfStateBits, grfMask); } STDMETHODIMP Stat(STATSTG *pstatstg, DWORD grfStatFlag) { return _pstgInner->Stat(pstatstg, grfStatFlag); }
STDMETHODIMP OpenStream(LPCWSTR pszRel, VOID *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm); STDMETHODIMP OpenStorage(LPCWSTR pszRel, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstg); STDMETHODIMP CopyTo(DWORD ciidExclude, const IID *rgiidExclude, SNB snbExclude, IStorage *pstgDest) { return E_NOTIMPL; }; STDMETHODIMP MoveElementTo(LPCWSTR pszRel, IStorage *pstgDest, LPCWSTR pwcsNewName, DWORD grfFlags) { return E_NOTIMPL; }; STDMETHODIMP EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3, IEnumSTATSTG **ppenum);
protected: LONG _cRef; IStorage *_pstgInner; };
class CShortcutStream : public IStream { public: CShortcutStream(LPCWSTR pwzRealName, IStream *pstm); ~CShortcutStream();
public: // IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release();
// IStream
STDMETHODIMP Read(void *pv, ULONG cb, ULONG *pcbRead) { return _pstmInner->Read(pv, cb, pcbRead); } STDMETHODIMP Write(void const *pv, ULONG cb, ULONG *pcbWritten) { return _pstmInner->Write(pv, cb, pcbWritten); } STDMETHODIMP Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) { return _pstmInner->Seek(dlibMove, dwOrigin, plibNewPosition); } STDMETHODIMP SetSize(ULARGE_INTEGER libNewSize) { return _pstmInner->SetSize(libNewSize); } STDMETHODIMP CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) { return _pstmInner->CopyTo(pstm, cb, pcbRead, pcbWritten); } STDMETHODIMP Commit(DWORD grfCommitFlags) { return _pstmInner->Commit(grfCommitFlags); } STDMETHODIMP Revert() { return _pstmInner->Revert(); } STDMETHODIMP LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { return _pstmInner->LockRegion(libOffset, cb, dwLockType); } STDMETHODIMP UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { return _pstmInner->UnlockRegion(libOffset, cb, dwLockType); } STDMETHODIMP Clone(IStream **ppstm) { return _pstmInner->Clone(ppstm); }
STDMETHODIMP Stat(STATSTG *pstatstg, DWORD grfStatFlag);
protected: LONG _cRef; WCHAR _wzName[MAX_PATH]; IStream *_pstmInner; };
class CShortcutStorageEnumSTATSTG : public IEnumSTATSTG { public: // IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release();
// IEnumSTATSTG
STDMETHODIMP Skip(ULONG celt) { return _penumInner->Skip(celt); }; STDMETHODIMP Reset() { return _penumInner->Reset(); }; STDMETHODIMP Clone(IEnumSTATSTG **ppenum) { return _penumInner->Clone(ppenum); };
STDMETHODIMP Next(ULONG celt, STATSTG *rgelt, ULONG *pceltFetched);
protected: CShortcutStorageEnumSTATSTG(CShortcutStorage *psstg, IEnumSTATSTG *penum); ~CShortcutStorageEnumSTATSTG();
private: LONG _cRef; CShortcutStorage *_psstg; IEnumSTATSTG *_penumInner; friend CShortcutStorage; };
CShortcutStorage::CShortcutStorage(IStorage *pstg) : _cRef(1), _pstgInner(pstg) { _pstgInner->AddRef(); DllAddRef(); }
CShortcutStorage::~CShortcutStorage() { ATOMICRELEASE(_pstgInner); DllRelease(); }
// IUnknown
STDMETHODIMP_(ULONG) CShortcutStorage::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CShortcutStorage::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
HRESULT CShortcutStorage::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CShortcutStorage, IStorage), { 0 }, }; return QISearch(this, qit, riid, ppv); }
// IStorage
STDMETHODIMP CShortcutStorage::OpenStream(LPCWSTR pwcsName, void *res1, DWORD grfMode, DWORD res2, IStream **ppstm) { IStream *pstmLink; HRESULT hr = _pstgInner->OpenStream(pwcsName, res1, grfMode, res2, &pstmLink); if (SUCCEEDED(hr)) { IPersistStream *pps; hr = SHCoCreateInstance(NULL, &CLSID_ShellLink, NULL, IID_PPV_ARG(IPersistStream, &pps)); if (SUCCEEDED(hr)) { hr = pps->Load(pstmLink); IShellLink *psl; if (SUCCEEDED(hr)) hr = pps->QueryInterface(IID_PPV_ARG(IShellLink, &psl)); if (SUCCEEDED(hr)) { LPITEMIDLIST pidl; hr = psl->GetIDList(&pidl); if (hr == S_OK) { IStream *pstmReal; hr = SHBindToObject(NULL, IID_X_PPV_ARG(IStream, pidl, &pstmReal)); if (SUCCEEDED(hr)) { CShortcutStream *psstm = new CShortcutStream(pwcsName, pstmReal); if (psstm) { hr = psstm->QueryInterface(IID_PPV_ARG(IStream, ppstm)); psstm->Release(); } else { hr = E_OUTOFMEMORY; } pstmReal->Release(); } ILFree(pidl); } else { // munge S_FALSE into E_FAIL (initialization must have failed for the link)
hr = SUCCEEDED(hr) ? E_FAIL : hr; } psl->Release(); } pps->Release(); }
// fall back to the non-link stream
if (FAILED(hr)) { hr = pstmLink->QueryInterface(IID_PPV_ARG(IStream, ppstm)); }
pstmLink->Release(); } return hr; }
STDMETHODIMP CShortcutStorage::OpenStorage(LPCWSTR pwcsName, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD res, IStorage **ppstg) { IStorage *pstg; HRESULT hr = _pstgInner->OpenStorage(pwcsName, pstgPriority, grfMode, snbExclude, res, &pstg); if (SUCCEEDED(hr)) { CShortcutStorage *psstg = new CShortcutStorage(pstg); if (psstg) { hr = psstg->QueryInterface(IID_PPV_ARG(IStorage, ppstg)); psstg->Release(); } else { hr = E_OUTOFMEMORY; } pstg->Release(); } return hr; }
STDMETHODIMP CShortcutStorage::EnumElements(DWORD res1, void *res2, DWORD res3, IEnumSTATSTG **ppenum) { IEnumSTATSTG *penum; HRESULT hr = _pstgInner->EnumElements(res1, res2, res3, &penum); if (SUCCEEDED(hr)) { CShortcutStorageEnumSTATSTG *pssenum = new CShortcutStorageEnumSTATSTG(this, penum); if (pssenum) { *ppenum = (IEnumSTATSTG *) pssenum; hr = S_OK; } else { *ppenum = NULL; hr = E_OUTOFMEMORY; } penum->Release(); } return hr; }
// CShortcutStorageEnumSTATSTG
CShortcutStorageEnumSTATSTG::CShortcutStorageEnumSTATSTG(CShortcutStorage *psstg, IEnumSTATSTG *penum) : _cRef(1) { _psstg = psstg; _psstg->AddRef();
_penumInner = penum; _penumInner->AddRef();
DllAddRef(); }
CShortcutStorageEnumSTATSTG::~CShortcutStorageEnumSTATSTG() { ATOMICRELEASE(_psstg); ATOMICRELEASE(_penumInner);
DllRelease(); }
// IUnknown
STDMETHODIMP_(ULONG) CShortcutStorageEnumSTATSTG::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CShortcutStorageEnumSTATSTG::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
HRESULT CShortcutStorageEnumSTATSTG::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CShortcutStorageEnumSTATSTG, IEnumSTATSTG), { 0 }, }; return QISearch(this, qit, riid, ppv); }
// IStorage
HRESULT CShortcutStorageEnumSTATSTG::Next(ULONG celt, STATSTG *rgelt, ULONG *pceltFetched) { ASSERTMSG((rgelt != NULL), "bad input parameter rgelt in CShortcutStorageEnumSTATSTG::Next");
ZeroMemory(rgelt, sizeof(STATSTG)); // per COM conventions
STATSTG stat; // we just get the next element from the inner enumeration so that we can
// get the name of it and keep our ordering. all the other data is useless.
HRESULT hr = _penumInner->Next(1, &stat, NULL); if (hr == S_OK) { switch (stat.type) { case STGTY_STORAGE: // if it's a storage, the data is good enough.
memcpy(rgelt, &stat, sizeof(STATSTG)); break; case STGTY_STREAM: // we need to dereference the link and get the real data.
IStream *pstm; // TODO: make sure that nobody else has this guy open in exclusive mode.
hr = _psstg->OpenStream(stat.pwcsName, NULL, STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pstm); if (SUCCEEDED(hr)) { hr = pstm->Stat(rgelt, STATFLAG_DEFAULT);
pstm->Release(); } CoTaskMemFree(stat.pwcsName); break;
default: ASSERTMSG(FALSE, "Unknown type in storage."); break; } if (SUCCEEDED(hr) && pceltFetched) *pceltFetched = 1; }
return hr; }
// CShortcutStream
CShortcutStream::CShortcutStream(LPCWSTR pwzRealName, IStream *pstm) : _cRef(1), _pstmInner(pstm) { _pstmInner->AddRef(); lstrcpyn(_wzName, pwzRealName, ARRAYSIZE(_wzName)); DllAddRef(); }
CShortcutStream::~CShortcutStream() { ATOMICRELEASE(_pstmInner); DllRelease(); }
// IUnknown
STDMETHODIMP_(ULONG) CShortcutStream::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CShortcutStream::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
HRESULT CShortcutStream::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CShortcutStream, IStream), { 0 }, }; return QISearch(this, qit, riid, ppv); }
// IStream
HRESULT CShortcutStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag) { ASSERTMSG((pstatstg != NULL), "bad input parameter pstatstg in CShortcutStream::Stat");
STATSTG stat; HRESULT hr = _pstmInner->Stat(&stat, grfStatFlag); if (SUCCEEDED(hr)) { // move all the fields over (we won't change most of em)
memcpy(pstatstg, &stat, sizeof(STATSTG));
// overwrite the name field with what we have
if (grfStatFlag == STATFLAG_DEFAULT) { hr = SHStrDup(_wzName, &pstatstg->pwcsName);
CoTaskMemFree(stat.pwcsName); } } return hr; }
STDAPI CShortcutStorage_CreateInstance(IStorage *pstg, REFIID riid, void **ppv) { if (!pstg) return E_INVALIDARG;
CShortcutStorage *pscstg = new CShortcutStorage(pstg); if (!pscstg) return E_OUTOFMEMORY;
HRESULT hr = pscstg->QueryInterface(riid, ppv); pscstg->Release(); return hr; }
|