1140 lines
33 KiB
C++
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;
|
||
|
}
|
||
|
|