mirror of https://github.com/UMSKT/xpmgr.git
322 lines
11 KiB
C++
322 lines
11 KiB
C++
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
|
#pragma once
|
|
#endif
|
|
|
|
#ifndef _INC_SHIDFACT
|
|
#define _INC_SHIDFACT
|
|
|
|
#ifdef __cplusplus
|
|
|
|
#include <shobjidl.h> // for IDelegateFolder
|
|
#include <propvarutil.h> // for PropVariantToVariant
|
|
|
|
template <class T, DWORD dwMagic>
|
|
class CItemIDFactory : public IDelegateFolder
|
|
{
|
|
protected:
|
|
CItemIDFactory() : _pmalloc(NULL) {}
|
|
|
|
// if we have an IMalloc from IDelegateFolder::SetItemAlloc then clean it up
|
|
|
|
virtual ~CItemIDFactory()
|
|
{
|
|
if (_pmalloc)
|
|
{
|
|
_pmalloc->Release();
|
|
}
|
|
}
|
|
|
|
public:
|
|
// IUnknown provided by derived classes
|
|
|
|
// IDelegateFolder
|
|
IFACEMETHODIMP SetItemAlloc(__inout_opt IMalloc *pmalloc)
|
|
{
|
|
IUnknown_Set((IUnknown**)&_pmalloc, pmalloc);
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL IsDelegateFolder()
|
|
{
|
|
return (_pmalloc != NULL);
|
|
}
|
|
|
|
// get a read only pointer to the client provided structure in the first ItemID in the IDList.
|
|
// returns NULL if the IDList isn't valid.
|
|
|
|
static const UNALIGNED T* GetDataFromIDList(PCUIDLIST_RELATIVE pidl)
|
|
{
|
|
PCITEM pitem = _IsValid(pidl);
|
|
return pitem ? &pitem->innerData : NULL;
|
|
}
|
|
|
|
static HRESULT GetDataFromIDList(PCUIDLIST_RELATIVE pidl, __out const UNALIGNED T** ppData)
|
|
{
|
|
*ppData = GetDataFromIDList(pidl);
|
|
return (*ppData) ? S_OK : E_INVALIDARG;
|
|
}
|
|
|
|
// return a read only pointer to the serialized property storage that we use for storing metadata
|
|
|
|
static PCUSERIALIZEDPROPSTORAGE GetPropertyStorage(PCUIDLIST_RELATIVE pidl, __out DWORD* pcb)
|
|
{
|
|
PCITEM pitem = _IsValid(pidl);
|
|
if (pitem && pitem->cbPropStore)
|
|
{
|
|
*pcb = pitem->cbPropStore;
|
|
return (PCUSERIALIZEDPROPSTORAGE)(pitem + 1);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// return a property from the IPropertyStore within the IDList, convert the resulting PROPVARIANT
|
|
// to a variant (useful when implementing IShellFolder2::GetDetailsEx)
|
|
// return vt == VT_EMPTY if not found
|
|
|
|
static HRESULT GetPropertyFromIDList(PCUIDLIST_RELATIVE pidl, REFPROPERTYKEY rkey, __out VARIANT *pvar)
|
|
{
|
|
PROPVARIANT pv = {pvar->vt};
|
|
HRESULT hr = GetPropertyFromIDList(pidl, rkey, &pv);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = PropVariantToVariant(&pv, pvar);
|
|
PropVariantClear(&pv);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT GetPropertyFromIDList(PCUIDLIST_RELATIVE pidl, LPCWSTR pszName, __out VARIANT *pvar)
|
|
{
|
|
PROPVARIANT pv = {pvar->vt};
|
|
HRESULT hr = GetPropertyFromIDList(pidl, pszName, &pv);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = PropVariantToVariant(&pv, pvar);
|
|
PropVariantClear(&pv);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// read a PROPVARIANT from the IPropertyStore within the first ItemID in the IDList.
|
|
|
|
static HRESULT GetPropertyFromIDList(PCUIDLIST_RELATIVE pidl, REFPROPERTYKEY rkey, __out PROPVARIANT *pv)
|
|
{
|
|
PropVariantInit(pv);
|
|
HRESULT hr = E_INVALIDARG;
|
|
DWORD cb;
|
|
PCUSERIALIZEDPROPSTORAGE psps = GetPropertyStorage(pidl, &cb);
|
|
if (psps)
|
|
{
|
|
hr = PSGetPropertyFromPropertyStorage(psps, cb, rkey, pv);
|
|
if (SUCCEEDED(hr) && (pv->vt == VT_EMPTY))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// read a PROPVARIANT from the IPropertyStore within the first ItemID in the IDList using the named property
|
|
|
|
static HRESULT GetPropertyFromIDList(PCUIDLIST_RELATIVE pidl, LPCWSTR pszName, __out PROPVARIANT *pv)
|
|
{
|
|
PropVariantInit(pv);
|
|
HRESULT hr = E_INVALIDARG;
|
|
DWORD cb;
|
|
PCUSERIALIZEDPROPSTORAGE psps = GetPropertyStorage(pidl, &cb);
|
|
if (psps)
|
|
{
|
|
hr = PSGetNamedPropertyFromPropertyStorage(psps, cb, pszName, pv);
|
|
if (SUCCEEDED(hr) && (pv->vt == VT_EMPTY))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// create an instance of the IPropertyStore based on the serialized property storage associated
|
|
// with the first ItemID.
|
|
|
|
static HRESULT GetPropertyStorageFromIDList(PCUIDLIST_RELATIVE pidl, REFIID riid, __out void **ppv)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
if (pidl && ppv)
|
|
{
|
|
DWORD cb;
|
|
PCUSERIALIZEDPROPSTORAGE psps = GetPropertyStorage(pidl, &cb);
|
|
if (psps)
|
|
{
|
|
IPersistSerializedPropStorage* ppsps;
|
|
hr = PSCreateMemoryPropertyStore(IID_PPV_ARGS(&ppsps));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ppsps->SetPropertyStorage(psps, cb);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ppsps->SetFlags(FPSPS_READONLY);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ppsps->QueryInterface(riid, ppv);
|
|
}
|
|
}
|
|
ppsps->Release();
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// package up the user supplied data into an ItemID. pinner points to the clients structure
|
|
// that should be copied, and the IPropertyStore is serialized into the ItemID. If the
|
|
// client has called IDelegateFolder::SetItemAlloc then we will allocate with that
|
|
// allocator, otherwise the COM allocator is used. the size of the user supplied data must
|
|
// equal sizeof(T). don't use structs with variably allocated array/string members. the
|
|
// struct must also follow standard SHITEMID for persistance and portability. see
|
|
// shtypes.idl for details.
|
|
HRESULT CreateItemID(const UNALIGNED T *pinner, __inout_opt IPropertyStore *pps, __out PITEMID_CHILD *ppidl)
|
|
{
|
|
return s_CreateItemID(pinner, pps, ppidl, _pmalloc);
|
|
}
|
|
|
|
static HRESULT s_CreateItemID(const UNALIGNED T *pinner, __inout_opt IPropertyStore *pps, __out PITEMID_CHILD *ppidl, IMalloc *pMalloc=NULL)
|
|
{
|
|
*ppidl = NULL;
|
|
|
|
SERIALIZEDPROPSTORAGE *pspstg = NULL;
|
|
DWORD cbPropStore = 0;
|
|
|
|
// do we have a IPropertyStore, if so we are going to add this to the ItemID
|
|
// so we need to get the serialized version of it.
|
|
|
|
HRESULT hr = S_OK;
|
|
if (pps)
|
|
{
|
|
IPersistSerializedPropStorage *ppsps;
|
|
hr = pps->QueryInterface(IID_PPV_ARGS(&ppsps));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ppsps->GetPropertyStorage(&pspstg, &cbPropStore);
|
|
ppsps->Release();
|
|
}
|
|
}
|
|
|
|
// either we succeeded at getting the serialized data, or an IPropertyStore wasn't passed to us.
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
UINT cbInner = sizeof(CHILDITEMID) - (sizeof(DELEGATEITEMID) - 1) + cbPropStore;
|
|
CHILDITEMID *pitem = s_Alloc(cbInner + sizeof(USHORT), pMalloc);
|
|
if (pitem)
|
|
{
|
|
pitem->dwMagic = dwMagic;
|
|
pitem->cbPropStore = (WORD)cbPropStore; // size of the property store
|
|
pitem->cbInnerData = sizeof(pitem->innerData); // size of the "innerData" structure (useful for debugging)
|
|
|
|
if (pinner)
|
|
pitem->innerData = *pinner; // copy the structure provided by the user
|
|
|
|
if (pspstg)
|
|
{
|
|
BYTE *pbData = (BYTE*)(pitem+1);
|
|
memcpy(pbData, pspstg, cbPropStore); // copy property store to ItemID
|
|
}
|
|
|
|
*ppidl = (PITEMID_CHILD)pitem;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
CoTaskMemFree(pspstg);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
private:
|
|
|
|
// ItemID form is derived from the delegate folder verison, this allows us to easily
|
|
// switch allocators without having to recompute the ItemID structure dynamically.
|
|
|
|
#include <pshpack1.h>
|
|
typedef struct // typedef struct
|
|
{ // {
|
|
// these need to line up -----------------------
|
|
WORD cbSize; // WORD cbSize; // Size of entire item ID
|
|
WORD wOuter; // WORD wOuter; // Private data owned by the outer folder
|
|
WORD cbInner; // WORD cbInner; // Size of delegate's data
|
|
// ---------------------------------------------
|
|
DWORD dwMagic; // guard word used to compare to ensure its valid
|
|
WORD cbPropStore; // size of the property store at the end of the ItemID
|
|
WORD cbInnerData; // size of the innerData structure
|
|
T innerData; // inner data returned by the client
|
|
} CHILDITEMID;
|
|
#include <poppack.h>
|
|
|
|
typedef UNALIGNED CHILDITEMID * PITEM;
|
|
typedef const UNALIGNED CHILDITEMID * PCITEM;
|
|
|
|
// given the size of the ItemID we want, let's allocate using the supplied
|
|
// allocator and initialize the structure
|
|
|
|
IMalloc *_pmalloc; // used to allocate ItemID when we are a delegate folder
|
|
|
|
static CHILDITEMID* s_Alloc(SIZE_T cbInner, IMalloc *pMalloc)
|
|
{
|
|
DELEGATEITEMID *pidl;
|
|
if (pMalloc)
|
|
{
|
|
pidl = (DELEGATEITEMID *)pMalloc->Alloc(cbInner);
|
|
}
|
|
else
|
|
{
|
|
SIZE_T cbAlloc =
|
|
sizeof(DELEGATEITEMID) - sizeof(pidl->rgb[0]) + // header
|
|
cbInner + // inner
|
|
sizeof(WORD); // trailing null (pidl terminator)
|
|
|
|
const SIZE_T cbSizeMax = (1 << (sizeof(pidl->cbSize)*8)) - 1;
|
|
if (cbAlloc > cbSizeMax + sizeof(WORD))
|
|
{
|
|
// cbSize will overflow
|
|
pidl = NULL;
|
|
}
|
|
else
|
|
{
|
|
pidl = (DELEGATEITEMID *)CoTaskMemAlloc(cbAlloc);
|
|
if (pidl)
|
|
{
|
|
ZeroMemory(pidl, cbAlloc);
|
|
pidl->cbSize = (WORD)(cbAlloc - sizeof(WORD));
|
|
pidl->cbInner = (WORD)cbInner;
|
|
}
|
|
}
|
|
}
|
|
return (CHILDITEMID*)pidl;
|
|
}
|
|
|
|
// given a relative IDList, determine if it's valid and therefore if
|
|
// we should continue to process the contents of it.
|
|
|
|
static PCITEM _IsValid(PCUIDLIST_RELATIVE pidl)
|
|
{
|
|
PCITEM pitem = (PCITEM)pidl;
|
|
|
|
if (!pitem || pitem->cbSize < sizeof(CHILDITEMID))
|
|
return NULL;
|
|
|
|
if ((pitem->dwMagic != dwMagic) || (pitem->cbSize < (sizeof(CHILDITEMID) + pitem->cbPropStore)))
|
|
return NULL;
|
|
|
|
return pitem;
|
|
}
|
|
};
|
|
|
|
#endif // __cplusplus
|
|
|
|
#endif // _INC_SHIDFACT
|
|
|