#include "shellprv.h" #include #include #include #include #include #include #include #include #include #include #include #define DSTYPE_STORAGE 0x1 #define DSTYPE_STREAM 0x2 // do not change order of these parameters, we have some in line variable initializations going on typedef struct { LPWSTR pwszTag; IShellItem *psi; DWORD_PTR grfMode; } DYNASTGDATA; // Prototype definitions STDAPI CDynamicStorage_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv); STDAPI CDynamicStorageEnum_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv); class CDynamicStorage : public IStorage, public IDynamicStorage { friend class CDynamicStorageEnum; friend HRESULT CDynamicStorage_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv); public: CDynamicStorage(); ~CDynamicStorage(); // IUnknown STDMETHODIMP QueryInterface(REFIID iid, void** ppv); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); // IDynamicStorage STDMETHODIMP AddIDList(DWORD cpidl, LPITEMIDLIST* rgpidl, DSTGF dstgf); STDMETHODIMP BindToItem(LPCWSTR pwszName, REFIID riid, void **ppv); STDMETHODIMP EnumItems(IEnumShellItems **ppesi); // IStorage // we only implement: CreateStream, OpenStream, OpenStorage, MoveElementTo, EnumElements. // others just return E_NOTIMPL; STDMETHODIMP CreateStream(const WCHAR * pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStream **ppstm); STDMETHODIMP OpenStream(const WCHAR *pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm); STDMETHODIMP CreateStorage(const WCHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStorage **ppstg) { return E_NOTIMPL; } STDMETHODIMP OpenStorage(const WCHAR *pwcsName, IStorage * pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage ** ppstg); STDMETHODIMP CopyTo(DWORD ciidExclude, IID const * rgiidExclude, SNB snbExclude, IStorage * pstgDest) { return E_NOTIMPL; } STDMETHODIMP MoveElementTo(const WCHAR * pwcsName, IStorage * pstgDest, const WCHAR * pwcsNewName, DWORD grfFlags); STDMETHODIMP Commit(DWORD grfCommitFlags) { return S_OK; } STDMETHODIMP Revert(void) { return E_NOTIMPL; } STDMETHODIMP EnumElements(DWORD reserved1, void * reserved2, DWORD reserved3, IEnumSTATSTG ** ppenum); STDMETHODIMP DestroyElement(const WCHAR* pwcsName) { return E_NOTIMPL; } STDMETHODIMP RenameElement(const WCHAR * pwcsOldName, const WCHAR * pwcsNewName) { return E_NOTIMPL; } STDMETHODIMP SetElementTimes(const WCHAR * pwcsName, FILETIME const * pctime, FILETIME const * patime, FILETIME const * pmtime) { return E_NOTIMPL; } STDMETHODIMP SetClass(REFCLSID clsid) { return E_NOTIMPL; } STDMETHODIMP SetStateBits(DWORD grfStateBits, DWORD grfMask) { return E_NOTIMPL; } STDMETHODIMP Stat(STATSTG * pstatstg, DWORD grfStatFlag) { return E_NOTIMPL; } private: HRESULT _Init(); HRESULT _EnsureDirectory(); HRESULT _InsertItem(LPWSTR pszTag, IShellItem *psi, DSTGF dstgf); HRESULT _GetStream(int i, DWORD grfMode, IStream **ppstm); HRESULT _GetStorage(int i, DWORD grfMode, IStorage **ppstg); static int s_DataCompare(DYNASTGDATA *pData1, DYNASTGDATA *pData2, LPARAM lParam); static int s_DataDestroy(DYNASTGDATA* pData, void* pv); private: CDPA _dpaData; IStorage* _pstgTemp; // pointer to our temp subfolder IStorage* _pstgTempDir; // the temp folder itself LPWSTR _pwszTemp; // name of our temp subfolder LONG _cRef; }; class CDynamicStorageEnum : public IEnumSTATSTG , public IEnumShellItems { public: CDynamicStorageEnum(); ~CDynamicStorageEnum(); STDMETHODIMP Init(CDynamicStorage* pDynStorage); // IUnknown STDMETHODIMP QueryInterface(REFIID iid, void** ppv); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); // IEnumXXX STDMETHODIMP Next(ULONG celt, IShellItem **rgelt, ULONG *pceltFetched); STDMETHODIMP Clone(IEnumShellItems **ppenum) { return E_NOTIMPL; } STDMETHODIMP Next(ULONG celt, STATSTG *rgelt, ULONG *pceltFetched); STDMETHODIMP Skip(ULONG celt); STDMETHODIMP Reset(); STDMETHODIMP Clone(IEnumSTATSTG **ppenum) { return E_NOTIMPL; } private: CDynamicStorage *_pDynStorage; IShellFolder *_psfParent; LPITEMIDLIST _pidlParent; ULONG _cItems; ULONG _cPos; LONG _cRef; }; #define DYNSTG_DPA_GROW_SIZE 10 CDynamicStorage::CDynamicStorage(): _cRef(1) { } int CDynamicStorage::s_DataDestroy(DYNASTGDATA* pData, void* pv) { ASSERTMSG(pData != NULL, "NULL dynamic storage data element"); CoTaskMemFree(pData->pwszTag); pData->psi->Release(); LocalFree(pData); return 1; } CDynamicStorage::~CDynamicStorage() { _dpaData.DestroyCallback(s_DataDestroy, NULL); if (_pstgTemp) { ASSERT(_pstgTempDir); _pstgTempDir->DestroyElement(_pwszTemp); _pstgTempDir->Release(); _pstgTemp->Release(); CoTaskMemFree(_pwszTemp); } } // IUnknown STDMETHODIMP CDynamicStorage::QueryInterface(REFIID iid, void** ppv) { static const QITAB qit[] = { QITABENT(CDynamicStorage, IDynamicStorage), QITABENT(CDynamicStorage, IStorage), { 0 }, }; return QISearch(this, qit, iid, ppv); } ULONG STDMETHODCALLTYPE CDynamicStorage::AddRef() { return ::InterlockedIncrement(&_cRef); } ULONG STDMETHODCALLTYPE CDynamicStorage::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } STDAPI CDynamicStorage_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv) { *ppv = NULL; if (punkOuter) return CLASS_E_NOAGGREGATION; HRESULT hr = E_OUTOFMEMORY; CDynamicStorage *pstgf = new CDynamicStorage(); if (pstgf) { hr = pstgf->_Init(); if (SUCCEEDED(hr)) { hr = pstgf->QueryInterface(riid, ppv); } pstgf->Release(); } return hr; } HRESULT CDynamicStorage::_InsertItem(LPWSTR pszTag, IShellItem *psi, DSTGF dstgf) { HRESULT hr = E_OUTOFMEMORY; DYNASTGDATA* pData = (DYNASTGDATA*)LocalAlloc(LPTR, sizeof(DYNASTGDATA)); if (pData) { hr = SHStrDup(pszTag, &pData->pwszTag); if (SUCCEEDED(hr)) { pData->psi = psi; pData->psi->AddRef(); if (!(dstgf & DSTGF_ALLOWDUP)) { int i = _dpaData.Search(pData, 0, s_DataCompare, 0, 0); if (i != -1) { s_DataDestroy(_dpaData.GetPtr(i), NULL); _dpaData.DeletePtr(i); } } // i != -1 if we succeeded, at which point pass out the data pointer or // ensure that we clean up. if (-1 == _dpaData.AppendPtr(pData)) { s_DataDestroy(pData, NULL); hr = E_FAIL; } } } return hr; } // IDynamicStorage STDMETHODIMP CDynamicStorage::AddIDList(DWORD cpidl, LPITEMIDLIST* rgpidl, DSTGF dstgf) { if (!rgpidl || !cpidl) return E_INVALIDARG; HRESULT hr = S_OK; for (DWORD i = 0; SUCCEEDED(hr) && i < cpidl; i++) { IShellItem *psi; hr = SHCreateShellItem(NULL, NULL, rgpidl[i], &psi); if (SUCCEEDED(hr)) { LPWSTR pszName; hr = psi->GetDisplayName(SIGDN_PARENTRELATIVEFORADDRESSBAR, &pszName); if (SUCCEEDED(hr)) { hr = _InsertItem(pszName, psi, dstgf); CoTaskMemFree(pszName); } psi->Release(); } } return hr; } HRESULT CDynamicStorage::BindToItem(LPCWSTR pwszName, REFIID riid, void **ppv) { HRESULT hr = STG_E_FILENOTFOUND; DYNASTGDATA data = {(LPWSTR)pwszName}; INT iResult = _dpaData.Search(&data, 0, s_DataCompare, 0, 0); if (iResult != -1) { DYNASTGDATA* pData = _dpaData.GetPtr(iResult); if (pData && riid == IID_IShellItem) hr = pData->psi->QueryInterface(riid, ppv); else hr = E_NOINTERFACE; } return hr; } HRESULT CDynamicStorage::EnumItems(IEnumShellItems **ppesi) { *ppesi = NULL; IEnumShellItems *penum = NULL; HRESULT hr = CDynamicStorageEnum_CreateInstance(NULL, IID_PPV_ARG(IEnumShellItems, &penum)); if (SUCCEEDED(hr)) { hr = ((CDynamicStorageEnum*)penum)->Init(this); if (FAILED(hr)) { penum->Release(); } } if (SUCCEEDED(hr)) { *ppesi = penum; } return hr; } HRESULT CDynamicStorage::_Init() { return _dpaData.Create(DYNSTG_DPA_GROW_SIZE) ? S_OK : E_OUTOFMEMORY; } HRESULT CDynamicStorage::_EnsureDirectory() { HRESULT hr = S_OK; if (!_pstgTemp) { hr = E_FAIL; WCHAR wszTempDir[MAX_PATH]; if (GetTempPath(ARRAYSIZE(wszTempDir), wszTempDir)) { LPITEMIDLIST pidl; hr = SHILCreateFromPath(wszTempDir, &pidl, NULL); if (SUCCEEDED(hr)) { hr = SHBindToObjectEx(NULL, pidl, NULL, IID_PPV_ARG(IStorage, &_pstgTempDir)); if (SUCCEEDED(hr)) { hr = StgMakeUniqueName(_pstgTempDir, L"dynastg", IID_PPV_ARG(IStorage, &_pstgTemp)); if (SUCCEEDED(hr)) { STATSTG statstg = {0}; hr = _pstgTemp->Stat(&statstg, STATFLAG_DEFAULT); if (SUCCEEDED(hr)) _pwszTemp = statstg.pwcsName; } if (FAILED(hr)) { ATOMICRELEASE(_pstgTempDir); // if we failed, _pwszTemp could not have been allocated... ATOMICRELEASE(_pstgTemp); } } ILFree(pidl); } } } return hr; } int CDynamicStorage::s_DataCompare(DYNASTGDATA *pData1, DYNASTGDATA *pData2, LPARAM lParam) { return lstrcmp(pData1->pwszTag, pData2->pwszTag); } HRESULT CDynamicStorage::_GetStream(int i, DWORD grfMode, IStream **ppstm) { *ppstm = NULL; HRESULT hr = E_FAIL; DYNASTGDATA* pData = _dpaData.GetPtr(i); if (pData) { IBindCtx *pbc; hr = BindCtx_CreateWithMode(grfMode, &pbc); if (SUCCEEDED(hr)) { hr = pData->psi->BindToHandler(pbc, BHID_Stream, IID_PPV_ARG(IStream, ppstm)); pbc->Release(); } } return hr; } STDMETHODIMP CDynamicStorage::OpenStream(const WCHAR * pwcsName, void * reserved1, DWORD grfMode, DWORD reserved2, IStream ** ppstm) { if (reserved1 || reserved2) return E_INVALIDARG; HRESULT hr = STG_E_FILENOTFOUND; DYNASTGDATA data = {(LPWSTR)pwcsName}; INT iResult = _dpaData.Search(&data, 0, s_DataCompare, 0, 0); if (iResult != -1) { hr = _GetStream(iResult, grfMode, ppstm); } return hr; } HRESULT CDynamicStorage::_GetStorage(int i, DWORD grfMode, IStorage **ppstg) { *ppstg = NULL; HRESULT hr = E_FAIL; DYNASTGDATA* pData = _dpaData.GetPtr(i); if (pData) { IBindCtx *pbc; hr = BindCtx_CreateWithMode(grfMode, &pbc); if (SUCCEEDED(hr)) { hr = pData->psi->BindToHandler(pbc, BHID_Storage, IID_PPV_ARG(IStorage, ppstg)); pbc->Release(); } } return hr; } STDMETHODIMP CDynamicStorage::OpenStorage(const WCHAR * pwcsName, IStorage * pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage ** ppstg) { if (pstgPriority || snbExclude || reserved) return E_INVALIDARG; HRESULT hr = STG_E_FILENOTFOUND; DYNASTGDATA data = {(LPWSTR)pwcsName}; INT iResult = _dpaData.Search(&data, 0, s_DataCompare, 0, 0); if (iResult != -1) { hr = _GetStorage(iResult, grfMode, ppstg); } return hr; } STDMETHODIMP CDynamicStorage::EnumElements(DWORD reserved1, void * reserved2, DWORD reserved3, IEnumSTATSTG ** ppenum) { if (reserved1 || reserved2 || reserved3 || !ppenum) return E_INVALIDARG; *ppenum = NULL; IEnumSTATSTG* pEnumObj = NULL; HRESULT hr = CDynamicStorageEnum_CreateInstance(NULL, IID_PPV_ARG(IEnumSTATSTG, &pEnumObj)); if (SUCCEEDED(hr)) { hr = ((CDynamicStorageEnum*)pEnumObj)->Init(this); if (FAILED(hr)) { pEnumObj->Release(); } } if (SUCCEEDED(hr)) { *ppenum = pEnumObj; } return hr; } STDMETHODIMP CDynamicStorage::MoveElementTo(const WCHAR * pwcsName, IStorage * pstgDest, const WCHAR* pwcsNewName, DWORD grfFlags) { if (!pwcsName || !pstgDest || !pwcsNewName || grfFlags != STGMOVE_COPY) return E_INVALIDARG; IStorage* pStorageSrc; HRESULT hr = OpenStorage(pwcsName, NULL, STGM_READ, NULL, 0, &pStorageSrc); if (SUCCEEDED(hr)) { IStorage* pStorageDest; hr = pstgDest->CreateStorage(pwcsNewName, STGM_WRITE | STGM_CREATE, 0, 0, &pStorageDest); if (SUCCEEDED(hr)) { hr = pStorageSrc->CopyTo(0, NULL, NULL, pStorageDest); } } else { IStream* pStreamSrc; hr = OpenStream(pwcsName, NULL, STGM_READ, 0, &pStreamSrc); if (SUCCEEDED(hr)) { IStream* pStreamDest; hr = pstgDest->CreateStream(pwcsNewName, STGM_WRITE | STGM_CREATE, 0, 0, &pStreamDest); if (SUCCEEDED(hr)) { ULARGE_INTEGER ulSize = {0xffffffff, 0xffffffff}; hr = pStreamSrc->CopyTo(pStreamDest, ulSize, NULL, NULL); pStreamDest->Release(); } pStreamSrc->Release(); } } return hr; } STDMETHODIMP CDynamicStorage::CreateStream(const WCHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStream **ppstm) { *ppstm = NULL; HRESULT hr = _EnsureDirectory(); if (SUCCEEDED(hr)) { hr = _pstgTemp->CreateStream(pwcsName, grfMode, 0, 0, ppstm); if (SUCCEEDED(hr)) { hr = E_FAIL; WCHAR wszPath[MAX_PATH]; if (GetTempPath(ARRAYSIZE(wszPath), wszPath) && PathCombine(wszPath, wszPath, _pwszTemp)) { STATSTG statstg; if (SUCCEEDED((*ppstm)->Stat(&statstg, 0))) { LPITEMIDLIST pidl; if (PathCombine(wszPath, wszPath, statstg.pwcsName) && SUCCEEDED(SHILCreateFromPath(wszPath, &pidl, NULL))) { hr = AddIDList(1, &pidl, DSTGF_NONE); ILFree(pidl); } CoTaskMemFree(statstg.pwcsName); } } if (FAILED(hr)) { // no need to DeleteElement here because the whole temp storage // will be deleted in destructor ATOMICRELEASE(*ppstm); } } } return hr; } CDynamicStorageEnum::CDynamicStorageEnum(): _cRef(1) { } CDynamicStorageEnum::~CDynamicStorageEnum() { if (_pDynStorage) _pDynStorage->Release(); if (_psfParent) _psfParent->Release(); ILFree(_pidlParent); } STDMETHODIMP CDynamicStorageEnum::Init(CDynamicStorage* pDynStorage) { _pDynStorage = pDynStorage; _pDynStorage->AddRef(); _cItems = _pDynStorage->_dpaData.GetPtrCount(); _cPos = 0; return S_OK; } STDMETHODIMP CDynamicStorageEnum::QueryInterface(REFIID iid, void** ppv) { static const QITAB qit[] = { QITABENT(CDynamicStorageEnum, IEnumSTATSTG), QITABENT(CDynamicStorageEnum, IEnumShellItems), { 0 }, }; return QISearch(this, qit, iid, ppv); } ULONG STDMETHODCALLTYPE CDynamicStorageEnum::AddRef() { return ::InterlockedIncrement(&_cRef); } ULONG STDMETHODCALLTYPE CDynamicStorageEnum::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } STDAPI CDynamicStorageEnum_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv) { *ppv = NULL; if (punkOuter) return CLASS_E_NOAGGREGATION; HRESULT hr = E_OUTOFMEMORY; CDynamicStorageEnum *pstgEnum = new CDynamicStorageEnum(); if (pstgEnum) { hr = pstgEnum->QueryInterface(riid, ppv); pstgEnum->Release(); } return hr; } STDMETHODIMP CDynamicStorageEnum::Next(ULONG celt, IShellItem **rgelt, ULONG *pceltFetched) { if (!rgelt || celt != 1) return E_INVALIDARG; if (pceltFetched) *pceltFetched = 0; *rgelt = NULL; if (_cPos >= _cItems) return S_FALSE; ASSERTMSG(_pDynStorage != NULL, "dynamic storage enumerator initialized with NULL dynamic storage"); DYNASTGDATA* pData = _pDynStorage->_dpaData.GetPtr(_cPos++); HRESULT hr; if (_cPos > _cItems) { hr = S_FALSE; } else { ASSERTMSG(pData != NULL, "dynamic storage has null DPA item"); hr = S_OK; rgelt[0] = pData->psi; rgelt[0]->AddRef(); if (pceltFetched) *pceltFetched = 1; } return hr; } STDMETHODIMP CDynamicStorageEnum::Next(ULONG celt, STATSTG *rgelt, ULONG *pceltFetched) { // ISSUE: for now, we only support calling next asking for one item if (!rgelt || celt != 1) return E_INVALIDARG; if (_cPos >= _cItems) return S_FALSE; ASSERTMSG(_pDynStorage != NULL, "dynamic storage enumerator initialized with NULL dynamic storage"); ZeroMemory(rgelt, sizeof(STATSTG)); // per COM conventions if (pceltFetched) *pceltFetched = 0; HRESULT hr = E_FAIL; IStorage *pstg; IStream *pstm; if (SUCCEEDED(_pDynStorage->_GetStream(_cPos, STGM_READ, &pstm))) { hr = pstm->Stat(rgelt, STATFLAG_DEFAULT); pstm->Release(); } else if (SUCCEEDED(_pDynStorage->_GetStorage(_cPos, STGM_READ, &pstg))) { hr = pstg->Stat(rgelt, STATFLAG_DEFAULT); pstg->Release(); } if (SUCCEEDED(hr)) { _cPos++; if (pceltFetched) *pceltFetched = 1; } return hr; } STDMETHODIMP CDynamicStorageEnum::Skip(ULONG celt) { ASSERTMSG(_pDynStorage != NULL, "dynamic storage enumerator initialized with NULL dynamic storage"); HRESULT hr; if (_cPos + celt > _cItems) { _cPos = _cItems; hr = S_FALSE; } else { _cPos += celt; hr = S_OK; } return hr; } STDMETHODIMP CDynamicStorageEnum::Reset() { ASSERTMSG(_pDynStorage != NULL, "dynamic storage enumerator initialized with NULL dynamic storage"); _cPos = 0; return S_OK; }