windows-nt/Source/XPSP1/NT/shell/ext/shimgvw/imgprop.cpp

1033 lines
28 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include "precomp.h"
#include "imagprop.h"
#include "imgprop.h"
#include <Stdio.h>
#pragma hdrstop
static const STATPROPSTG g_cImageSummaryProps[] =
{
{NULL, PIDISI_CX, VT_UI4},
{NULL, PIDISI_CY, VT_UI4},
{NULL, PIDISI_RESOLUTIONX, VT_UI4},
{NULL, PIDISI_RESOLUTIONY, VT_UI4},
{NULL, PIDISI_BITDEPTH, VT_UI4},
{NULL, PIDISI_FRAMECOUNT, VT_UI4},
{NULL, PIDISI_DIMENSIONS, VT_LPWSTR},
};
HRESULT GetImageFrameCount(Image *pImage, PROPVARIANT *ppv);
// simple IEnumSTATPROPSTG for FMTID_ImageSummaryInformation
class CPropEnum : public IEnumSTATPROPSTG, public NonATLObject
{
public:
CPropEnum(const STATPROPSTG *pStats, ULONG nStat);
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IEnumSTATPROPSTG
STDMETHODIMP Next(ULONG celt, STATPROPSTG *rgelt, ULONG *pceltFetched);
STDMETHODIMP Skip(ULONG celt);
STDMETHODIMP Reset(void);
STDMETHODIMP Clone(IEnumSTATPROPSTG **ppenum);
private:
~CPropEnum();
LONG _cRef;
ULONG _idx;
const STATPROPSTG *_pStat;
ULONG _nStat;
FMTID _fmtid;
};
CImagePropSet::CImagePropSet(Image *pimg, IShellImageData *pData,
IPropertyStorage *pps, REFFMTID fmtid, FNPROPCHANGE fnCallback)
: _pimg(pimg),
_pData(pData),
_ppsImg(pps),
_cRef(1),
_fDirty(FALSE),
_fmtid(fmtid),
_fnPropChanged(fnCallback),
_fEditable(TRUE)
{
if (_pData)
{
_pData->AddRef();
_fEditable = (S_OK == _pData->IsEditable());
}
if (_ppsImg)
{
_ppsImg->AddRef();
}
_Module.Lock();
}
CImagePropSet::~CImagePropSet()
{
ATOMICRELEASE(_ppsImg);
ATOMICRELEASE(_pData);
_Module.Unlock();
}
// IUnknown
STDMETHODIMP CImagePropSet::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CImagePropSet, IPropertyStorage),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CImagePropSet::AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CImagePropSet::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
// IPropertyStorage methods
STDMETHODIMP CImagePropSet::ReadMultiple(ULONG cpspec, const PROPSPEC rgpspec[], PROPVARIANT rgvar[])
{
HRESULT hr = E_UNEXPECTED;
if (FMTID_ImageSummaryInformation == _fmtid)
{
hr = _GetImageSummaryProps(cpspec, rgpspec, rgvar);
}
else if (_ppsImg)
{
hr = _ppsImg->ReadMultiple(cpspec, rgpspec, rgvar);
}
return hr;
}
STDMETHODIMP CImagePropSet::WriteMultiple(ULONG cpspec, const PROPSPEC rgpspec[], const PROPVARIANT rgvar[], PROPID propidNameFirst)
{
HRESULT hr = E_UNEXPECTED;
if (!_fEditable)
{
hr = STG_E_ACCESSDENIED;
}
else if (_ppsImg)
{
hr = _ppsImg->WriteMultiple(cpspec, rgpspec, rgvar, propidNameFirst);
if (SUCCEEDED(hr))
{
_fDirty = TRUE;
if (_pData && _fnPropChanged)
{
SHCOLUMNID scid;
scid.fmtid = _fmtid;
for (ULONG i=0;i<cpspec;i++)
{
scid.pid = rgpspec[i].propid;
(*_fnPropChanged)(_pData, &scid);
}
}
}
}
return hr;
}
STDMETHODIMP CImagePropSet::DeleteMultiple(ULONG cpspec, const PROPSPEC rgpspec[])
{
return E_NOTIMPL;
}
STDMETHODIMP CImagePropSet::ReadPropertyNames(ULONG cpropid, const PROPID rgpropid[], LPOLESTR rglpwstrName[])
{
HRESULT hr = E_UNEXPECTED;
if (_ppsImg)
{
hr = _ppsImg->ReadPropertyNames(cpropid, rgpropid, rglpwstrName);
}
return hr;
}
STDMETHODIMP CImagePropSet::WritePropertyNames(ULONG cpropid, const PROPID rgpropid[], const LPOLESTR rglpwstrName[])
{
HRESULT hr = E_UNEXPECTED;
if (_ppsImg)
{
hr = _ppsImg->WritePropertyNames(cpropid, rgpropid, rglpwstrName);
}
return hr;
}
STDMETHODIMP CImagePropSet::DeletePropertyNames(ULONG cpropid, const PROPID rgpropid[])
{
HRESULT hr = E_UNEXPECTED;
if (_ppsImg)
{
hr = _ppsImg->DeletePropertyNames(cpropid, rgpropid);
}
return hr;
}
STDMETHODIMP CImagePropSet::SetClass(REFCLSID clsid)
{
HRESULT hr = E_UNEXPECTED;
if (_ppsImg)
{
hr = _ppsImg->SetClass(clsid);
}
return hr;
}
STDMETHODIMP CImagePropSet::Commit(DWORD grfCommitFlags)
{
HRESULT hr = S_FALSE;
if (_fDirty && _pData)
{
IPersistFile *pFile;
if (SUCCEEDED(_pData->QueryInterface(IID_PPV_ARG(IPersistFile, &pFile))))
{
hr = pFile->Save(NULL, FALSE);
pFile->Release();
}
else
{
hr = E_UNEXPECTED;
}
}
return hr;
}
STDMETHODIMP CImagePropSet::Revert()
{
return E_NOTIMPL;
}
STDMETHODIMP CImagePropSet::Enum(IEnumSTATPROPSTG** ppenm)
{
HRESULT hr = E_UNEXPECTED;
if (FMTID_ImageSummaryInformation == _fmtid)
{
CPropEnum *pEnum = new CPropEnum(g_cImageSummaryProps,
ARRAYSIZE(g_cImageSummaryProps));
if (pEnum)
{
hr = pEnum->QueryInterface(IID_PPV_ARG(IEnumSTATPROPSTG, ppenm));
pEnum->Release();
}
else
{
hr = E_OUTOFMEMORY;
}
}
else if (_ppsImg)
{
hr = _ppsImg->Enum(ppenm);
}
return hr;
}
STDMETHODIMP CImagePropSet::Stat(STATPROPSETSTG* pstatpsstg)
{
HRESULT hr = S_OK;
if (_ppsImg)
{
hr = _ppsImg->Stat(pstatpsstg);
}
else if (FMTID_ImageSummaryInformation == _fmtid)
{
ZeroMemory(pstatpsstg, sizeof(STATPROPSETSTG));
pstatpsstg->fmtid = _fmtid;
pstatpsstg->grfFlags = STGM_READ | STGM_SHARE_DENY_NONE;
}
else
{
hr = E_UNEXPECTED;
}
if (!_fEditable)
{
pstatpsstg->grfFlags = STGM_READ | STGM_SHARE_DENY_NONE;
}
return hr;
}
STDMETHODIMP CImagePropSet::SetTimes(const FILETIME* pmtime, const FILETIME* pctime, const FILETIME* patime)
{
return E_NOTIMPL;
}
void CImagePropSet::SaveProps(Image *pImage, CDSA<SHCOLUMNID> *pdsaChanges)
{
// enum the properties in our propertystorage and convert them to PropertyItem structs, and
// save them to the given frame.
if (_ppsImg)
{
for (int i=0;i<pdsaChanges->GetItemCount();i++)
{
SHCOLUMNID scid;
if (pdsaChanges->GetItem(i,&scid))
{
if (scid.fmtid == _fmtid)
{
PropertyItem pi;
PROPID idUnicode;
PROPID idStandard;
if (SUCCEEDED(_MapPropidToImgPropid(scid.pid, &idStandard, &idUnicode)))
{
PROPVARIANT pv = {0};
PROPSPEC ps = {PRSPEC_PROPID, scid.pid};
if (SUCCEEDED(_ppsImg->ReadMultiple(1, &ps, &pv)))
{
if (pv.vt == VT_NULL || pv.vt == VT_EMPTY)
{
if (idUnicode)
{
pImage->RemovePropertyItem(idUnicode);
}
pImage->RemovePropertyItem(idStandard);
}
else if (SUCCEEDED(_PropVarToImgProp(idUnicode?idUnicode:idStandard, &pv, &pi, idUnicode?TRUE:FALSE)))
{
// if SetPropertyItem fails what should we do?
// for now just ignore it and move on
if (Ok == pImage->SetPropertyItem(&pi))
{
if (idUnicode)
{
// remove the old ascii tag.
pImage->RemovePropertyItem(idStandard);
}
}
delete [] (BYTE*)pi.value;
}
PropVariantClear(&pv);
}
}
}
}
}
}
_fDirty = FALSE;
}
// Helper functions
HRESULT CImagePropSet::_PropVarToImgProp(PROPID pid, const PROPVARIANT *ppv, PropertyItem *pprop, BOOL bUnicode)
{
HRESULT hr = S_OK;
CHAR szValue[MAX_PATH*2];
void *pBits = NULL;
ULONG cbData = 0;
szValue[0] = 0;
SAFEARRAY *psa = NULL;
switch (ppv->vt)
{
case VT_UI1:
pprop->type = PropertyTagTypeByte;
cbData = sizeof(ppv->bVal);
pBits = (void *)&ppv->bVal;
break;
case VT_UI2:
pprop->type = PropertyTagTypeShort;
cbData = sizeof(ppv->uiVal);
pBits = (void *)&ppv->uiVal;
break;
case VT_UI4:
pprop->type = PropertyTagTypeLong;
cbData = sizeof(ppv->ulVal);
pBits = (void *)&ppv->ulVal;
break;
case VT_LPSTR:
if (!bUnicode)
{
pprop->type = PropertyTagTypeASCII;
cbData = sizeof(CHAR)*(lstrlenA(ppv->pszVal)+1);
pBits = ppv->pszVal ? ppv->pszVal : szValue;
}
else
{
pprop->type = PropertyTagTypeByte;
cbData = SHAnsiToUnicode(ppv->pszVal, (LPWSTR)szValue, sizeof(szValue)/sizeof(WCHAR))*sizeof(WCHAR);
pBits = szValue;
}
break;
case VT_BSTR:
if (!bUnicode)
{
pprop->type = PropertyTagTypeASCII;
cbData = sizeof(CHAR)*SHUnicodeToAnsi(ppv->bstrVal, szValue, ARRAYSIZE(szValue));
pBits = szValue;
}
else
{
pprop->type = PropertyTagTypeByte;
cbData = sizeof(WCHAR)*(1+lstrlenW(ppv->bstrVal));
pBits = ppv->bstrVal;
}
break;
case VT_LPWSTR:
if (!bUnicode)
{
pprop->type = PropertyTagTypeASCII;
cbData = sizeof(CHAR)*SHUnicodeToAnsi(ppv->pwszVal, szValue, ARRAYSIZE(szValue));
pBits = szValue;
}
else
{
pprop->type = PropertyTagTypeByte;
cbData = sizeof(WCHAR)*(1+lstrlenW(ppv->pwszVal));
pBits = ppv->pwszVal;
}
break;
case VT_UI1|VT_ARRAY:
pprop->type = PropertyTagTypeByte;
psa = ppv->parray;
hr = SafeArrayAccessData(psa, &pBits);
if (SUCCEEDED(hr))
{
SafeArrayGetUBound(psa, 1, (LONG*)&cbData);
}
break;
case VT_UI4|VT_ARRAY:
pprop->type = PropertyTagTypeLong;
psa = ppv->parray;
hr = SafeArrayAccessData(psa, &pBits);
if (SUCCEEDED(hr))
{
SafeArrayGetUBound(psa, 1, (LONG*)&cbData);
}
break;
// we ignore rational values because we can't convert back to numerator/denominator pairs
case VT_R8:
default:
hr = E_INVALIDARG;
break;
}
if (SUCCEEDED(hr))
{
pprop->id = pid;
pprop->length = cbData;
pprop->value = (void **)new BYTE[cbData];
if (pprop->value)
{
CopyMemory(pprop->value, pBits, cbData);
}
else
{
hr = E_OUTOFMEMORY;
}
}
if (psa)
{
SafeArrayUnaccessData(psa);
}
return hr;
}
typedef HRESULT (CALLBACK* PROPPROC)(Image *pimg, PROPVARIANT *ppv);
const static struct
{
FMTID fmtid;
PROPID pid;
PROPPROC fnPropProc;
} c_aPropList [] =
{
{PSGUID_SUMMARYINFORMATION, PIDSI_PAGECOUNT, GetImageFrameCount},
};
#define UNI_AUTHOR 0x001
#define UNI_COMMENT 0x002
#define UNI_TITLE 0x004
#define UNI_KEYWORD 0x008
#define UNI_SUBJECT 0x010
const static struct
{
FMTID fmtid;
PROPID propid;
PROPID imgPropid;
PROPID imgPropidUnicode;
DWORD dwMask;
}
c_rgImagePropertyMap[] =
{
{PSGUID_SUMMARYINFORMATION, PIDSI_TITLE, PropertyTagImageDescription, PropertyTagUnicodeDescription, UNI_TITLE},
{PSGUID_SUMMARYINFORMATION, PIDSI_COMMENT, 0, PropertyTagUnicodeComment, UNI_COMMENT},
{PSGUID_SUMMARYINFORMATION, PIDSI_AUTHOR, PropertyTagArtist, PropertyTagUnicodeArtist, UNI_AUTHOR},
{PSGUID_SUMMARYINFORMATION, PIDSI_APPNAME, PropertyTagSoftwareUsed,0},
{PSGUID_SUMMARYINFORMATION, PIDSI_CREATE_DTM, PropertyTagDateTime,0},
// some tags have no standard EXIF/TIFF equivalent
{PSGUID_SUMMARYINFORMATION, PIDSI_KEYWORDS, 0, PropertyTagUnicodeKeywords, UNI_KEYWORD},
{PSGUID_SUMMARYINFORMATION, PIDSI_SUBJECT, 0, PropertyTagUnicodeSubject, UNI_SUBJECT},
};
BOOL IsAsciiPropertyPresent(PROPID pidUnicode, PROPID *aid, UINT cProperties)
{
// first find the ASCII value
UINT i;
BOOL bRet = FALSE;
PROPID pidAscii = 0;
for (i=0;!pidAscii && i<ARRAYSIZE(c_rgImagePropertyMap);i++)
{
if (pidUnicode == c_rgImagePropertyMap[i].imgPropidUnicode)
{
pidAscii = c_rgImagePropertyMap[i].imgPropid;
}
}
if (pidAscii)
{
for (i=0;i<cProperties;i++)
{
if (pidAscii == aid[i])
{
bRet = TRUE;
}
}
}
return bRet;
}
void _UpdateUnicodeMask(DWORD *pdwMask, PROPID pid)
{
for (int i=0;i<ARRAYSIZE(c_rgImagePropertyMap);i++)
{
if (pid == c_rgImagePropertyMap[i].imgPropidUnicode)
{
*pdwMask |= c_rgImagePropertyMap[i].dwMask;
}
}
}
// sync all of the properties in the image file (regular header and EXIF header)
// into the property storage that we have here
// for properties that we write UNICODE equivalents, we always defer to the ASCII version
// if present in the file.
HRESULT CImagePropSet::SyncImagePropsToStorage()
{
UINT cProperties = _pimg->GetPropertyCount();
PROPSPEC pspec;
pspec.ulKind = PRSPEC_PROPID;
PROPVARIANT pvar = {0};
// create a simple mask for determining which of the unicode properties are written
// if they aren't written we will special case them and write empty strings with VT_LPWSTR type
DWORD dwUnicodeWritten = 0;
if (cProperties)
{
PROPID *aid = new PROPID[cProperties];
if (aid)
{
if (Ok == _pimg->GetPropertyIdList(cProperties, aid))
{
BOOL bUnicode;
for (UINT i = 0; i < cProperties; i++)
{
if (SUCCEEDED(_MapImgPropidToPropid(aid[i], &pspec.propid, &bUnicode)))
{
if (!bUnicode || !IsAsciiPropertyPresent(aid[i], aid, cProperties))
{
UINT cbSize = _pimg->GetPropertyItemSize(aid[i]);
if (cbSize)
{
PropertyItem *ppi = (PropertyItem*)LocalAlloc(LPTR, cbSize);
if (ppi)
{
if (Ok == _pimg->GetPropertyItem(aid[i], cbSize, ppi))
{
if (SUCCEEDED(_PropImgToPropvar(ppi, &pvar, bUnicode)))
{
_ppsImg->WriteMultiple(1, &pspec, &pvar,2);
if (_fmtid == FMTID_SummaryInformation)
{
_UpdateUnicodeMask(&dwUnicodeWritten, aid[i]);
}
PropVariantClear(&pvar);
}
}
LocalFree(ppi);
}
}
}
}
}
}
delete [] aid;
}
}
//
// Some properties are derived from other means than EXIF or TIFF tags, cycle
// through the property list and add properties from callback functions
//
for (int i=0;i<ARRAYSIZE(c_aPropList);i++)
{
pspec.propid = c_aPropList[i].pid;
if (_fmtid == c_aPropList[i].fmtid &&
SUCCEEDED(c_aPropList[i].fnPropProc(_pimg, &pvar)))
{
_ppsImg->WriteMultiple(1, &pspec, &pvar,2);
PropVariantClear(&pvar);
}
}
//
// Write the empty unicode strings if needed
//
if (_fEditable && _fmtid == FMTID_SummaryInformation)
{
PropVariantInit(&pvar);
pvar.vt = VT_LPWSTR;
pvar.pwszVal = L"";
if (pvar.pwszVal)
{
for (int i=0;i<ARRAYSIZE(c_rgImagePropertyMap);i++)
{
if (c_rgImagePropertyMap[i].dwMask && !(c_rgImagePropertyMap[i].dwMask & dwUnicodeWritten))
{
pspec.propid = c_rgImagePropertyMap[i].propid;
_ppsImg->WriteMultiple(1, &pspec, &pvar, 2);
}
}
}
// don't clear the propvar since we didn't heap alloc the string
}
return S_OK;
}
HRESULT StrDupNW(LPCWSTR psz, WCHAR **ppwsz, DWORD cch)
{
WCHAR *pwsz;
DWORD cb = cch*sizeof(WCHAR);
if (psz)
{
if (psz[cch-1] != L'\0')
{
cb+=sizeof(WCHAR); // need space for NULL
}
pwsz = (WCHAR *)CoTaskMemAlloc(cb);
}
else
pwsz = NULL;
*((PVOID UNALIGNED64 *) ppwsz) = pwsz;
if (pwsz)
{
pwsz[(cb/sizeof(WCHAR))-1] = L'\0';
memcpy(pwsz, psz, cch*sizeof(WCHAR));
return S_OK;
}
return E_OUTOFMEMORY;
}
HRESULT CImagePropSet::_PropImgToPropvar(PropertyItem *pi, PROPVARIANT *pvar, BOOL bUnicode)
{
HRESULT hr = S_OK;
if (!pi->length)
{
return E_FAIL;
}
switch (pi->type)
{
case PropertyTagTypeByte:
pvar->vt = VT_UI1;
// check for multi-valued property and convert to safearray or unicode string if found
if (pi->length > sizeof(UCHAR))
{
if (!bUnicode)
{
SAFEARRAYBOUND bound;
bound.cElements = pi->length/sizeof(UCHAR);
bound.lLbound = 0;
pvar->vt |= VT_ARRAY;
hr = E_OUTOFMEMORY;
pvar->parray = SafeArrayCreate(VT_UI1, 1, &bound);
if (pvar->parray)
{
void *pv;
hr = SafeArrayAccessData(pvar->parray, &pv);
if (SUCCEEDED(hr))
{
CopyMemory(pv, pi->value, pi->length);
SafeArrayUnaccessData(pvar->parray);
}
else
{
SafeArrayDestroy(pvar->parray);
}
}
}
else
{
pvar->vt = VT_LPWSTR;
hr = StrDupNW((LPCWSTR)pi->value, &pvar->pwszVal, pi->length/sizeof(WCHAR));
}
}
else
{
pvar->bVal = *((UCHAR*)pi->value);
}
break;
case PropertyTagTypeShort:
pvar->vt = VT_UI2;
pvar->uiVal = *((USHORT*)pi->value);
break;
case PropertyTagTypeLong:
pvar->vt = VT_UI4;
if (pi->length > sizeof(ULONG))
{
SAFEARRAYBOUND bound;
bound.cElements = pi->length/sizeof(ULONG);
bound.lLbound = 0;
pvar->vt |= VT_ARRAY;
hr = E_OUTOFMEMORY;
pvar->parray = SafeArrayCreate(VT_UI4, 1, &bound);
if (pvar->parray)
{
void *pv;
hr = SafeArrayAccessData (pvar->parray, &pv);
if (SUCCEEDED(hr))
{
CopyMemory (pv, pi->value, pi->length);
SafeArrayUnaccessData(pvar->parray);
}
else
{
SafeArrayDestroy(pvar->parray);
}
}
}
else
{
pvar->ulVal = *((ULONG*)pi->value);
}
break;
case PropertyTagTypeASCII:
// special case for date taken
if (_fmtid == FMTID_ImageProperties && pi->id == PropertyTagExifDTOrig)
{
SYSTEMTIME st = {0};
sscanf((LPSTR)pi->value, "%hd:%hd:%hd %hd:%hd:%hd",
&st.wYear, &st.wMonth,
&st.wDay, &st.wHour,
&st.wMinute, &st.wSecond);
if (st.wYear)
{
FILETIME ftUTC;
FILETIME ftLocal;
// we expect cameras to return local times. Need to convert to UTC.
SystemTimeToFileTime(&st, &ftLocal);
LocalFileTimeToFileTime(&ftLocal, &ftUTC);
FileTimeToSystemTime(&ftUTC, &st);
SystemTimeToVariantTime(&st, &pvar->date);
pvar->vt = VT_DATE;
}
else
{
pvar->vt = VT_EMPTY;
}
}
else
{
hr = SHStrDupA(pi->value ? (LPSTR)pi->value : "", &pvar->pwszVal);
if (SUCCEEDED(hr))
{
pvar->vt = VT_LPWSTR;
}
}
break;
case PropertyTagTypeSRational:
case PropertyTagTypeRational:
{
LONG *pl = (LONG*)pi->value;
LONG num = pl[0];
LONG den = pl[1];
pvar->vt = VT_R8;
if (0 == den)
pvar->dblVal = 0; // don't divide by zero
else
pvar->dblVal = ((double)num)/((double)den);
break;
}
case PropertyTagTypeUndefined:
case PropertyTagTypeSLONG:
default:
hr = E_UNEXPECTED;
break;
}
return hr;
}
HRESULT CImagePropSet::_MapPropidToImgPropid(PROPID propid, PROPID *ppid, PROPID *pidUnicode)
{
HRESULT hr;
*ppid = 0;
*pidUnicode = 0;
if (_fmtid == FMTID_ImageProperties)
{
*ppid = propid; // these go into the EXIF header
hr = S_OK;
}
else
{
hr = E_FAIL;
for (int i = 0; i < ARRAYSIZE(c_rgImagePropertyMap); i++)
{
if (c_rgImagePropertyMap[i].fmtid == _fmtid && c_rgImagePropertyMap[i].propid == propid)
{
*ppid = c_rgImagePropertyMap[i].imgPropid;
*pidUnicode = c_rgImagePropertyMap[i].imgPropidUnicode;
hr = S_OK;
break;
}
}
}
return hr;
}
HRESULT CImagePropSet::_MapImgPropidToPropid(PROPID propid, PROPID *ppid, BOOL *pbUnicode)
{
HRESULT hr;
*pbUnicode = FALSE;
if (_fmtid == FMTID_ImageProperties)
{
*ppid = propid; // EXIF properties don't need to be mapped
hr = S_OK;
}
else
{
*ppid = 0;
hr = E_FAIL;
for (int i = 0; i < ARRAYSIZE(c_rgImagePropertyMap); i++)
{
if (c_rgImagePropertyMap[i].fmtid == _fmtid &&
(c_rgImagePropertyMap[i].imgPropid == propid ||
c_rgImagePropertyMap[i].imgPropidUnicode == propid))
{
*ppid = c_rgImagePropertyMap[i].propid;
*pbUnicode = (c_rgImagePropertyMap[i].imgPropidUnicode == propid);
hr = S_OK;
break;
}
}
}
return hr;
}
HRESULT GetImageFrameCount(Image *pImage, PROPVARIANT *ppv)
{
HRESULT hr = S_FALSE;
LONG lCount;
lCount = 1; //Default to 1 image
UINT uiDim = pImage->GetFrameDimensionsCount();
ppv->vt = VT_EMPTY;
if (uiDim)
{
GUID *pDim = new GUID[uiDim];
if (pDim)
{
if (Ok == pImage->GetFrameDimensionsList(pDim, uiDim))
{
lCount = 0;
ULONG uiN;
for (ULONG i=0;i<uiDim;i++)
{
uiN = pImage->GetFrameCount(&pDim[i]);
lCount += uiN;
}
ppv->vt = VT_UI4;
ppv->lVal = lCount;
hr = S_OK;
}
delete [] pDim;
}
else
{
hr = E_OUTOFMEMORY;
}
}
return hr;
}
HRESULT CImagePropSet::_GetImageSummaryProps(ULONG cpspec, const PROPSPEC rgpspec[], PROPVARIANT rgvar[])
{
HRESULT hr = E_FAIL;
if (_pimg)
{
hr = S_OK;
for (ULONG i = 0; i < cpspec; i++)
{
PropVariantInit(&rgvar[i]);
rgvar[i].vt = VT_UI4;
switch (rgpspec[i].propid)
{
case PIDISI_CX:
rgvar[i].ulVal = _pimg->GetWidth();
break;
case PIDISI_CY:
rgvar[i].ulVal = _pimg->GetHeight();
break;
case PIDISI_RESOLUTIONX:
rgvar[i].ulVal = (ULONG)_pimg->GetHorizontalResolution();
break;
case PIDISI_RESOLUTIONY:
rgvar[i].ulVal = (ULONG)_pimg->GetVerticalResolution();
break;
case PIDISI_BITDEPTH:
{
PixelFormat pf = _pimg->GetPixelFormat();
rgvar[i].ulVal = (pf >> 8) & 0xff;
}
break;
case PIDISI_FRAMECOUNT:
hr = GetImageFrameCount(_pimg, &rgvar[i]);
break;
case PIDISI_DIMENSIONS:
{
TCHAR szFmt[64];
if (LoadString(_Module.GetModuleInstance(), IDS_DIMENSIONS_FMT, szFmt, ARRAYSIZE(szFmt)))
{
DWORD_PTR args[2];
args[0] = (DWORD_PTR)_pimg->GetWidth();
args[1] = (DWORD_PTR)_pimg->GetHeight();
TCHAR szBuffer[64];
FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
szFmt, 0, 0, szBuffer, ARRAYSIZE(szBuffer), (va_list*)args);
hr = SHStrDup(szBuffer, &rgvar[i].pwszVal);
if (SUCCEEDED(hr))
rgvar[i].vt = VT_LPWSTR;
else
rgvar[i].vt = VT_EMPTY;
}
break;
}
default:
rgvar[i].vt = VT_EMPTY;
hr = S_FALSE;
break;
}
}
}
return hr;
}
STDMETHODIMP CPropEnum::Next(ULONG celt, STATPROPSTG *rgelt, ULONG *pceltFetched)
{
HRESULT hr = S_OK;
ULONG uRet = 0;
if (pceltFetched)
{
*pceltFetched = 0;
}
for (;_idx < _nStat && uRet < celt;_idx++)
{
rgelt[uRet] = _pStat[_idx];
uRet++;
}
if (uRet < celt)
{
hr = S_FALSE;
}
if (pceltFetched)
{
*pceltFetched = uRet;
}
return hr;
}
STDMETHODIMP CPropEnum::Skip(ULONG celt)
{
HRESULT hr = S_OK;
ULONG ul = min(_idx+celt, _nStat);
if (ul - _idx < celt)
{
hr = S_FALSE;
}
_idx = ul;
return hr;
}
STDMETHODIMP CPropEnum::Reset(void)
{
_idx = 0;
return S_OK;
}
STDMETHODIMP CPropEnum::Clone(IEnumSTATPROPSTG **ppenum)
{
HRESULT hr = E_OUTOFMEMORY;
CPropEnum *pNew = new CPropEnum(_pStat, _nStat);
if (pNew)
{
hr = pNew->QueryInterface(IID_PPV_ARG(IEnumSTATPROPSTG, ppenum));
pNew->Release();
}
return hr;
}
STDMETHODIMP CPropEnum::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] =
{
QITABENT(CPropEnum, IEnumSTATPROPSTG),
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
STDMETHODIMP_(ULONG) CPropEnum::AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CPropEnum::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
CPropEnum::CPropEnum(const STATPROPSTG *pStats, ULONG nStats) : _idx(0), _cRef(1), _pStat(pStats), _nStat(nStats)
{
_Module.Lock();
}
CPropEnum::~CPropEnum()
{
_Module.Unlock();
}