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

1140 lines
33 KiB
C++

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