Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1032 lines
28 KiB

  1. #include "precomp.h"
  2. #include "imagprop.h"
  3. #include "imgprop.h"
  4. #include <Stdio.h>
  5. #pragma hdrstop
  6. static const STATPROPSTG g_cImageSummaryProps[] =
  7. {
  8. {NULL, PIDISI_CX, VT_UI4},
  9. {NULL, PIDISI_CY, VT_UI4},
  10. {NULL, PIDISI_RESOLUTIONX, VT_UI4},
  11. {NULL, PIDISI_RESOLUTIONY, VT_UI4},
  12. {NULL, PIDISI_BITDEPTH, VT_UI4},
  13. {NULL, PIDISI_FRAMECOUNT, VT_UI4},
  14. {NULL, PIDISI_DIMENSIONS, VT_LPWSTR},
  15. };
  16. HRESULT GetImageFrameCount(Image *pImage, PROPVARIANT *ppv);
  17. // simple IEnumSTATPROPSTG for FMTID_ImageSummaryInformation
  18. class CPropEnum : public IEnumSTATPROPSTG, public NonATLObject
  19. {
  20. public:
  21. CPropEnum(const STATPROPSTG *pStats, ULONG nStat);
  22. // IUnknown
  23. STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  24. STDMETHODIMP_(ULONG) AddRef();
  25. STDMETHODIMP_(ULONG) Release();
  26. // IEnumSTATPROPSTG
  27. STDMETHODIMP Next(ULONG celt, STATPROPSTG *rgelt, ULONG *pceltFetched);
  28. STDMETHODIMP Skip(ULONG celt);
  29. STDMETHODIMP Reset(void);
  30. STDMETHODIMP Clone(IEnumSTATPROPSTG **ppenum);
  31. private:
  32. ~CPropEnum();
  33. LONG _cRef;
  34. ULONG _idx;
  35. const STATPROPSTG *_pStat;
  36. ULONG _nStat;
  37. FMTID _fmtid;
  38. };
  39. CImagePropSet::CImagePropSet(Image *pimg, IShellImageData *pData,
  40. IPropertyStorage *pps, REFFMTID fmtid, FNPROPCHANGE fnCallback)
  41. : _pimg(pimg),
  42. _pData(pData),
  43. _ppsImg(pps),
  44. _cRef(1),
  45. _fDirty(FALSE),
  46. _fmtid(fmtid),
  47. _fnPropChanged(fnCallback),
  48. _fEditable(TRUE)
  49. {
  50. if (_pData)
  51. {
  52. _pData->AddRef();
  53. _fEditable = (S_OK == _pData->IsEditable());
  54. }
  55. if (_ppsImg)
  56. {
  57. _ppsImg->AddRef();
  58. }
  59. _Module.Lock();
  60. }
  61. CImagePropSet::~CImagePropSet()
  62. {
  63. ATOMICRELEASE(_ppsImg);
  64. ATOMICRELEASE(_pData);
  65. _Module.Unlock();
  66. }
  67. // IUnknown
  68. STDMETHODIMP CImagePropSet::QueryInterface(REFIID riid, void **ppv)
  69. {
  70. static const QITAB qit[] =
  71. {
  72. QITABENT(CImagePropSet, IPropertyStorage),
  73. { 0 },
  74. };
  75. return QISearch(this, qit, riid, ppv);
  76. }
  77. STDMETHODIMP_(ULONG) CImagePropSet::AddRef()
  78. {
  79. return InterlockedIncrement(&_cRef);
  80. }
  81. STDMETHODIMP_(ULONG) CImagePropSet::Release()
  82. {
  83. if (InterlockedDecrement(&_cRef))
  84. return _cRef;
  85. delete this;
  86. return 0;
  87. }
  88. // IPropertyStorage methods
  89. STDMETHODIMP CImagePropSet::ReadMultiple(ULONG cpspec, const PROPSPEC rgpspec[], PROPVARIANT rgvar[])
  90. {
  91. HRESULT hr = E_UNEXPECTED;
  92. if (FMTID_ImageSummaryInformation == _fmtid)
  93. {
  94. hr = _GetImageSummaryProps(cpspec, rgpspec, rgvar);
  95. }
  96. else if (_ppsImg)
  97. {
  98. hr = _ppsImg->ReadMultiple(cpspec, rgpspec, rgvar);
  99. }
  100. return hr;
  101. }
  102. STDMETHODIMP CImagePropSet::WriteMultiple(ULONG cpspec, const PROPSPEC rgpspec[], const PROPVARIANT rgvar[], PROPID propidNameFirst)
  103. {
  104. HRESULT hr = E_UNEXPECTED;
  105. if (!_fEditable)
  106. {
  107. hr = STG_E_ACCESSDENIED;
  108. }
  109. else if (_ppsImg)
  110. {
  111. hr = _ppsImg->WriteMultiple(cpspec, rgpspec, rgvar, propidNameFirst);
  112. if (SUCCEEDED(hr))
  113. {
  114. _fDirty = TRUE;
  115. if (_pData && _fnPropChanged)
  116. {
  117. SHCOLUMNID scid;
  118. scid.fmtid = _fmtid;
  119. for (ULONG i=0;i<cpspec;i++)
  120. {
  121. scid.pid = rgpspec[i].propid;
  122. (*_fnPropChanged)(_pData, &scid);
  123. }
  124. }
  125. }
  126. }
  127. return hr;
  128. }
  129. STDMETHODIMP CImagePropSet::DeleteMultiple(ULONG cpspec, const PROPSPEC rgpspec[])
  130. {
  131. return E_NOTIMPL;
  132. }
  133. STDMETHODIMP CImagePropSet::ReadPropertyNames(ULONG cpropid, const PROPID rgpropid[], LPOLESTR rglpwstrName[])
  134. {
  135. HRESULT hr = E_UNEXPECTED;
  136. if (_ppsImg)
  137. {
  138. hr = _ppsImg->ReadPropertyNames(cpropid, rgpropid, rglpwstrName);
  139. }
  140. return hr;
  141. }
  142. STDMETHODIMP CImagePropSet::WritePropertyNames(ULONG cpropid, const PROPID rgpropid[], const LPOLESTR rglpwstrName[])
  143. {
  144. HRESULT hr = E_UNEXPECTED;
  145. if (_ppsImg)
  146. {
  147. hr = _ppsImg->WritePropertyNames(cpropid, rgpropid, rglpwstrName);
  148. }
  149. return hr;
  150. }
  151. STDMETHODIMP CImagePropSet::DeletePropertyNames(ULONG cpropid, const PROPID rgpropid[])
  152. {
  153. HRESULT hr = E_UNEXPECTED;
  154. if (_ppsImg)
  155. {
  156. hr = _ppsImg->DeletePropertyNames(cpropid, rgpropid);
  157. }
  158. return hr;
  159. }
  160. STDMETHODIMP CImagePropSet::SetClass(REFCLSID clsid)
  161. {
  162. HRESULT hr = E_UNEXPECTED;
  163. if (_ppsImg)
  164. {
  165. hr = _ppsImg->SetClass(clsid);
  166. }
  167. return hr;
  168. }
  169. STDMETHODIMP CImagePropSet::Commit(DWORD grfCommitFlags)
  170. {
  171. HRESULT hr = S_FALSE;
  172. if (_fDirty && _pData)
  173. {
  174. IPersistFile *pFile;
  175. if (SUCCEEDED(_pData->QueryInterface(IID_PPV_ARG(IPersistFile, &pFile))))
  176. {
  177. hr = pFile->Save(NULL, FALSE);
  178. pFile->Release();
  179. }
  180. else
  181. {
  182. hr = E_UNEXPECTED;
  183. }
  184. }
  185. return hr;
  186. }
  187. STDMETHODIMP CImagePropSet::Revert()
  188. {
  189. return E_NOTIMPL;
  190. }
  191. STDMETHODIMP CImagePropSet::Enum(IEnumSTATPROPSTG** ppenm)
  192. {
  193. HRESULT hr = E_UNEXPECTED;
  194. if (FMTID_ImageSummaryInformation == _fmtid)
  195. {
  196. CPropEnum *pEnum = new CPropEnum(g_cImageSummaryProps,
  197. ARRAYSIZE(g_cImageSummaryProps));
  198. if (pEnum)
  199. {
  200. hr = pEnum->QueryInterface(IID_PPV_ARG(IEnumSTATPROPSTG, ppenm));
  201. pEnum->Release();
  202. }
  203. else
  204. {
  205. hr = E_OUTOFMEMORY;
  206. }
  207. }
  208. else if (_ppsImg)
  209. {
  210. hr = _ppsImg->Enum(ppenm);
  211. }
  212. return hr;
  213. }
  214. STDMETHODIMP CImagePropSet::Stat(STATPROPSETSTG* pstatpsstg)
  215. {
  216. HRESULT hr = S_OK;
  217. if (_ppsImg)
  218. {
  219. hr = _ppsImg->Stat(pstatpsstg);
  220. }
  221. else if (FMTID_ImageSummaryInformation == _fmtid)
  222. {
  223. ZeroMemory(pstatpsstg, sizeof(STATPROPSETSTG));
  224. pstatpsstg->fmtid = _fmtid;
  225. pstatpsstg->grfFlags = STGM_READ | STGM_SHARE_DENY_NONE;
  226. }
  227. else
  228. {
  229. hr = E_UNEXPECTED;
  230. }
  231. if (!_fEditable)
  232. {
  233. pstatpsstg->grfFlags = STGM_READ | STGM_SHARE_DENY_NONE;
  234. }
  235. return hr;
  236. }
  237. STDMETHODIMP CImagePropSet::SetTimes(const FILETIME* pmtime, const FILETIME* pctime, const FILETIME* patime)
  238. {
  239. return E_NOTIMPL;
  240. }
  241. void CImagePropSet::SaveProps(Image *pImage, CDSA<SHCOLUMNID> *pdsaChanges)
  242. {
  243. // enum the properties in our propertystorage and convert them to PropertyItem structs, and
  244. // save them to the given frame.
  245. if (_ppsImg)
  246. {
  247. for (int i=0;i<pdsaChanges->GetItemCount();i++)
  248. {
  249. SHCOLUMNID scid;
  250. if (pdsaChanges->GetItem(i,&scid))
  251. {
  252. if (scid.fmtid == _fmtid)
  253. {
  254. PropertyItem pi;
  255. PROPID idUnicode;
  256. PROPID idStandard;
  257. if (SUCCEEDED(_MapPropidToImgPropid(scid.pid, &idStandard, &idUnicode)))
  258. {
  259. PROPVARIANT pv = {0};
  260. PROPSPEC ps = {PRSPEC_PROPID, scid.pid};
  261. if (SUCCEEDED(_ppsImg->ReadMultiple(1, &ps, &pv)))
  262. {
  263. if (pv.vt == VT_NULL || pv.vt == VT_EMPTY)
  264. {
  265. if (idUnicode)
  266. {
  267. pImage->RemovePropertyItem(idUnicode);
  268. }
  269. pImage->RemovePropertyItem(idStandard);
  270. }
  271. else if (SUCCEEDED(_PropVarToImgProp(idUnicode?idUnicode:idStandard, &pv, &pi, idUnicode?TRUE:FALSE)))
  272. {
  273. // if SetPropertyItem fails what should we do?
  274. // for now just ignore it and move on
  275. if (Ok == pImage->SetPropertyItem(&pi))
  276. {
  277. if (idUnicode)
  278. {
  279. // remove the old ascii tag.
  280. pImage->RemovePropertyItem(idStandard);
  281. }
  282. }
  283. delete [] (BYTE*)pi.value;
  284. }
  285. PropVariantClear(&pv);
  286. }
  287. }
  288. }
  289. }
  290. }
  291. }
  292. _fDirty = FALSE;
  293. }
  294. // Helper functions
  295. HRESULT CImagePropSet::_PropVarToImgProp(PROPID pid, const PROPVARIANT *ppv, PropertyItem *pprop, BOOL bUnicode)
  296. {
  297. HRESULT hr = S_OK;
  298. CHAR szValue[MAX_PATH*2];
  299. void *pBits = NULL;
  300. ULONG cbData = 0;
  301. szValue[0] = 0;
  302. SAFEARRAY *psa = NULL;
  303. switch (ppv->vt)
  304. {
  305. case VT_UI1:
  306. pprop->type = PropertyTagTypeByte;
  307. cbData = sizeof(ppv->bVal);
  308. pBits = (void *)&ppv->bVal;
  309. break;
  310. case VT_UI2:
  311. pprop->type = PropertyTagTypeShort;
  312. cbData = sizeof(ppv->uiVal);
  313. pBits = (void *)&ppv->uiVal;
  314. break;
  315. case VT_UI4:
  316. pprop->type = PropertyTagTypeLong;
  317. cbData = sizeof(ppv->ulVal);
  318. pBits = (void *)&ppv->ulVal;
  319. break;
  320. case VT_LPSTR:
  321. if (!bUnicode)
  322. {
  323. pprop->type = PropertyTagTypeASCII;
  324. cbData = sizeof(CHAR)*(lstrlenA(ppv->pszVal)+1);
  325. pBits = ppv->pszVal ? ppv->pszVal : szValue;
  326. }
  327. else
  328. {
  329. pprop->type = PropertyTagTypeByte;
  330. cbData = SHAnsiToUnicode(ppv->pszVal, (LPWSTR)szValue, sizeof(szValue)/sizeof(WCHAR))*sizeof(WCHAR);
  331. pBits = szValue;
  332. }
  333. break;
  334. case VT_BSTR:
  335. if (!bUnicode)
  336. {
  337. pprop->type = PropertyTagTypeASCII;
  338. cbData = sizeof(CHAR)*SHUnicodeToAnsi(ppv->bstrVal, szValue, ARRAYSIZE(szValue));
  339. pBits = szValue;
  340. }
  341. else
  342. {
  343. pprop->type = PropertyTagTypeByte;
  344. cbData = sizeof(WCHAR)*(1+lstrlenW(ppv->bstrVal));
  345. pBits = ppv->bstrVal;
  346. }
  347. break;
  348. case VT_LPWSTR:
  349. if (!bUnicode)
  350. {
  351. pprop->type = PropertyTagTypeASCII;
  352. cbData = sizeof(CHAR)*SHUnicodeToAnsi(ppv->pwszVal, szValue, ARRAYSIZE(szValue));
  353. pBits = szValue;
  354. }
  355. else
  356. {
  357. pprop->type = PropertyTagTypeByte;
  358. cbData = sizeof(WCHAR)*(1+lstrlenW(ppv->pwszVal));
  359. pBits = ppv->pwszVal;
  360. }
  361. break;
  362. case VT_UI1|VT_ARRAY:
  363. pprop->type = PropertyTagTypeByte;
  364. psa = ppv->parray;
  365. hr = SafeArrayAccessData(psa, &pBits);
  366. if (SUCCEEDED(hr))
  367. {
  368. SafeArrayGetUBound(psa, 1, (LONG*)&cbData);
  369. }
  370. break;
  371. case VT_UI4|VT_ARRAY:
  372. pprop->type = PropertyTagTypeLong;
  373. psa = ppv->parray;
  374. hr = SafeArrayAccessData(psa, &pBits);
  375. if (SUCCEEDED(hr))
  376. {
  377. SafeArrayGetUBound(psa, 1, (LONG*)&cbData);
  378. }
  379. break;
  380. // we ignore rational values because we can't convert back to numerator/denominator pairs
  381. case VT_R8:
  382. default:
  383. hr = E_INVALIDARG;
  384. break;
  385. }
  386. if (SUCCEEDED(hr))
  387. {
  388. pprop->id = pid;
  389. pprop->length = cbData;
  390. pprop->value = (void **)new BYTE[cbData];
  391. if (pprop->value)
  392. {
  393. CopyMemory(pprop->value, pBits, cbData);
  394. }
  395. else
  396. {
  397. hr = E_OUTOFMEMORY;
  398. }
  399. }
  400. if (psa)
  401. {
  402. SafeArrayUnaccessData(psa);
  403. }
  404. return hr;
  405. }
  406. typedef HRESULT (CALLBACK* PROPPROC)(Image *pimg, PROPVARIANT *ppv);
  407. const static struct
  408. {
  409. FMTID fmtid;
  410. PROPID pid;
  411. PROPPROC fnPropProc;
  412. } c_aPropList [] =
  413. {
  414. {PSGUID_SUMMARYINFORMATION, PIDSI_PAGECOUNT, GetImageFrameCount},
  415. };
  416. #define UNI_AUTHOR 0x001
  417. #define UNI_COMMENT 0x002
  418. #define UNI_TITLE 0x004
  419. #define UNI_KEYWORD 0x008
  420. #define UNI_SUBJECT 0x010
  421. const static struct
  422. {
  423. FMTID fmtid;
  424. PROPID propid;
  425. PROPID imgPropid;
  426. PROPID imgPropidUnicode;
  427. DWORD dwMask;
  428. }
  429. c_rgImagePropertyMap[] =
  430. {
  431. {PSGUID_SUMMARYINFORMATION, PIDSI_TITLE, PropertyTagImageDescription, PropertyTagUnicodeDescription, UNI_TITLE},
  432. {PSGUID_SUMMARYINFORMATION, PIDSI_COMMENT, 0, PropertyTagUnicodeComment, UNI_COMMENT},
  433. {PSGUID_SUMMARYINFORMATION, PIDSI_AUTHOR, PropertyTagArtist, PropertyTagUnicodeArtist, UNI_AUTHOR},
  434. {PSGUID_SUMMARYINFORMATION, PIDSI_APPNAME, PropertyTagSoftwareUsed,0},
  435. {PSGUID_SUMMARYINFORMATION, PIDSI_CREATE_DTM, PropertyTagDateTime,0},
  436. // some tags have no standard EXIF/TIFF equivalent
  437. {PSGUID_SUMMARYINFORMATION, PIDSI_KEYWORDS, 0, PropertyTagUnicodeKeywords, UNI_KEYWORD},
  438. {PSGUID_SUMMARYINFORMATION, PIDSI_SUBJECT, 0, PropertyTagUnicodeSubject, UNI_SUBJECT},
  439. };
  440. BOOL IsAsciiPropertyPresent(PROPID pidUnicode, PROPID *aid, UINT cProperties)
  441. {
  442. // first find the ASCII value
  443. UINT i;
  444. BOOL bRet = FALSE;
  445. PROPID pidAscii = 0;
  446. for (i=0;!pidAscii && i<ARRAYSIZE(c_rgImagePropertyMap);i++)
  447. {
  448. if (pidUnicode == c_rgImagePropertyMap[i].imgPropidUnicode)
  449. {
  450. pidAscii = c_rgImagePropertyMap[i].imgPropid;
  451. }
  452. }
  453. if (pidAscii)
  454. {
  455. for (i=0;i<cProperties;i++)
  456. {
  457. if (pidAscii == aid[i])
  458. {
  459. bRet = TRUE;
  460. }
  461. }
  462. }
  463. return bRet;
  464. }
  465. void _UpdateUnicodeMask(DWORD *pdwMask, PROPID pid)
  466. {
  467. for (int i=0;i<ARRAYSIZE(c_rgImagePropertyMap);i++)
  468. {
  469. if (pid == c_rgImagePropertyMap[i].imgPropidUnicode)
  470. {
  471. *pdwMask |= c_rgImagePropertyMap[i].dwMask;
  472. }
  473. }
  474. }
  475. // sync all of the properties in the image file (regular header and EXIF header)
  476. // into the property storage that we have here
  477. // for properties that we write UNICODE equivalents, we always defer to the ASCII version
  478. // if present in the file.
  479. HRESULT CImagePropSet::SyncImagePropsToStorage()
  480. {
  481. UINT cProperties = _pimg->GetPropertyCount();
  482. PROPSPEC pspec;
  483. pspec.ulKind = PRSPEC_PROPID;
  484. PROPVARIANT pvar = {0};
  485. // create a simple mask for determining which of the unicode properties are written
  486. // if they aren't written we will special case them and write empty strings with VT_LPWSTR type
  487. DWORD dwUnicodeWritten = 0;
  488. if (cProperties)
  489. {
  490. PROPID *aid = new PROPID[cProperties];
  491. if (aid)
  492. {
  493. if (Ok == _pimg->GetPropertyIdList(cProperties, aid))
  494. {
  495. BOOL bUnicode;
  496. for (UINT i = 0; i < cProperties; i++)
  497. {
  498. if (SUCCEEDED(_MapImgPropidToPropid(aid[i], &pspec.propid, &bUnicode)))
  499. {
  500. if (!bUnicode || !IsAsciiPropertyPresent(aid[i], aid, cProperties))
  501. {
  502. UINT cbSize = _pimg->GetPropertyItemSize(aid[i]);
  503. if (cbSize)
  504. {
  505. PropertyItem *ppi = (PropertyItem*)LocalAlloc(LPTR, cbSize);
  506. if (ppi)
  507. {
  508. if (Ok == _pimg->GetPropertyItem(aid[i], cbSize, ppi))
  509. {
  510. if (SUCCEEDED(_PropImgToPropvar(ppi, &pvar, bUnicode)))
  511. {
  512. _ppsImg->WriteMultiple(1, &pspec, &pvar,2);
  513. if (_fmtid == FMTID_SummaryInformation)
  514. {
  515. _UpdateUnicodeMask(&dwUnicodeWritten, aid[i]);
  516. }
  517. PropVariantClear(&pvar);
  518. }
  519. }
  520. LocalFree(ppi);
  521. }
  522. }
  523. }
  524. }
  525. }
  526. }
  527. delete [] aid;
  528. }
  529. }
  530. //
  531. // Some properties are derived from other means than EXIF or TIFF tags, cycle
  532. // through the property list and add properties from callback functions
  533. //
  534. for (int i=0;i<ARRAYSIZE(c_aPropList);i++)
  535. {
  536. pspec.propid = c_aPropList[i].pid;
  537. if (_fmtid == c_aPropList[i].fmtid &&
  538. SUCCEEDED(c_aPropList[i].fnPropProc(_pimg, &pvar)))
  539. {
  540. _ppsImg->WriteMultiple(1, &pspec, &pvar,2);
  541. PropVariantClear(&pvar);
  542. }
  543. }
  544. //
  545. // Write the empty unicode strings if needed
  546. //
  547. if (_fEditable && _fmtid == FMTID_SummaryInformation)
  548. {
  549. PropVariantInit(&pvar);
  550. pvar.vt = VT_LPWSTR;
  551. pvar.pwszVal = L"";
  552. if (pvar.pwszVal)
  553. {
  554. for (int i=0;i<ARRAYSIZE(c_rgImagePropertyMap);i++)
  555. {
  556. if (c_rgImagePropertyMap[i].dwMask && !(c_rgImagePropertyMap[i].dwMask & dwUnicodeWritten))
  557. {
  558. pspec.propid = c_rgImagePropertyMap[i].propid;
  559. _ppsImg->WriteMultiple(1, &pspec, &pvar, 2);
  560. }
  561. }
  562. }
  563. // don't clear the propvar since we didn't heap alloc the string
  564. }
  565. return S_OK;
  566. }
  567. HRESULT StrDupNW(LPCWSTR psz, WCHAR **ppwsz, DWORD cch)
  568. {
  569. WCHAR *pwsz;
  570. DWORD cb = cch*sizeof(WCHAR);
  571. if (psz)
  572. {
  573. if (psz[cch-1] != L'\0')
  574. {
  575. cb+=sizeof(WCHAR); // need space for NULL
  576. }
  577. pwsz = (WCHAR *)CoTaskMemAlloc(cb);
  578. }
  579. else
  580. pwsz = NULL;
  581. *((PVOID UNALIGNED64 *) ppwsz) = pwsz;
  582. if (pwsz)
  583. {
  584. pwsz[(cb/sizeof(WCHAR))-1] = L'\0';
  585. memcpy(pwsz, psz, cch*sizeof(WCHAR));
  586. return S_OK;
  587. }
  588. return E_OUTOFMEMORY;
  589. }
  590. HRESULT CImagePropSet::_PropImgToPropvar(PropertyItem *pi, PROPVARIANT *pvar, BOOL bUnicode)
  591. {
  592. HRESULT hr = S_OK;
  593. if (!pi->length)
  594. {
  595. return E_FAIL;
  596. }
  597. switch (pi->type)
  598. {
  599. case PropertyTagTypeByte:
  600. pvar->vt = VT_UI1;
  601. // check for multi-valued property and convert to safearray or unicode string if found
  602. if (pi->length > sizeof(UCHAR))
  603. {
  604. if (!bUnicode)
  605. {
  606. SAFEARRAYBOUND bound;
  607. bound.cElements = pi->length/sizeof(UCHAR);
  608. bound.lLbound = 0;
  609. pvar->vt |= VT_ARRAY;
  610. hr = E_OUTOFMEMORY;
  611. pvar->parray = SafeArrayCreate(VT_UI1, 1, &bound);
  612. if (pvar->parray)
  613. {
  614. void *pv;
  615. hr = SafeArrayAccessData(pvar->parray, &pv);
  616. if (SUCCEEDED(hr))
  617. {
  618. CopyMemory(pv, pi->value, pi->length);
  619. SafeArrayUnaccessData(pvar->parray);
  620. }
  621. else
  622. {
  623. SafeArrayDestroy(pvar->parray);
  624. }
  625. }
  626. }
  627. else
  628. {
  629. pvar->vt = VT_LPWSTR;
  630. hr = StrDupNW((LPCWSTR)pi->value, &pvar->pwszVal, pi->length/sizeof(WCHAR));
  631. }
  632. }
  633. else
  634. {
  635. pvar->bVal = *((UCHAR*)pi->value);
  636. }
  637. break;
  638. case PropertyTagTypeShort:
  639. pvar->vt = VT_UI2;
  640. pvar->uiVal = *((USHORT*)pi->value);
  641. break;
  642. case PropertyTagTypeLong:
  643. pvar->vt = VT_UI4;
  644. if (pi->length > sizeof(ULONG))
  645. {
  646. SAFEARRAYBOUND bound;
  647. bound.cElements = pi->length/sizeof(ULONG);
  648. bound.lLbound = 0;
  649. pvar->vt |= VT_ARRAY;
  650. hr = E_OUTOFMEMORY;
  651. pvar->parray = SafeArrayCreate(VT_UI4, 1, &bound);
  652. if (pvar->parray)
  653. {
  654. void *pv;
  655. hr = SafeArrayAccessData (pvar->parray, &pv);
  656. if (SUCCEEDED(hr))
  657. {
  658. CopyMemory (pv, pi->value, pi->length);
  659. SafeArrayUnaccessData(pvar->parray);
  660. }
  661. else
  662. {
  663. SafeArrayDestroy(pvar->parray);
  664. }
  665. }
  666. }
  667. else
  668. {
  669. pvar->ulVal = *((ULONG*)pi->value);
  670. }
  671. break;
  672. case PropertyTagTypeASCII:
  673. // special case for date taken
  674. if (_fmtid == FMTID_ImageProperties && pi->id == PropertyTagExifDTOrig)
  675. {
  676. SYSTEMTIME st = {0};
  677. sscanf((LPSTR)pi->value, "%hd:%hd:%hd %hd:%hd:%hd",
  678. &st.wYear, &st.wMonth,
  679. &st.wDay, &st.wHour,
  680. &st.wMinute, &st.wSecond);
  681. if (st.wYear)
  682. {
  683. FILETIME ftUTC;
  684. FILETIME ftLocal;
  685. // we expect cameras to return local times. Need to convert to UTC.
  686. SystemTimeToFileTime(&st, &ftLocal);
  687. LocalFileTimeToFileTime(&ftLocal, &ftUTC);
  688. FileTimeToSystemTime(&ftUTC, &st);
  689. SystemTimeToVariantTime(&st, &pvar->date);
  690. pvar->vt = VT_DATE;
  691. }
  692. else
  693. {
  694. pvar->vt = VT_EMPTY;
  695. }
  696. }
  697. else
  698. {
  699. hr = SHStrDupA(pi->value ? (LPSTR)pi->value : "", &pvar->pwszVal);
  700. if (SUCCEEDED(hr))
  701. {
  702. pvar->vt = VT_LPWSTR;
  703. }
  704. }
  705. break;
  706. case PropertyTagTypeSRational:
  707. case PropertyTagTypeRational:
  708. {
  709. LONG *pl = (LONG*)pi->value;
  710. LONG num = pl[0];
  711. LONG den = pl[1];
  712. pvar->vt = VT_R8;
  713. if (0 == den)
  714. pvar->dblVal = 0; // don't divide by zero
  715. else
  716. pvar->dblVal = ((double)num)/((double)den);
  717. break;
  718. }
  719. case PropertyTagTypeUndefined:
  720. case PropertyTagTypeSLONG:
  721. default:
  722. hr = E_UNEXPECTED;
  723. break;
  724. }
  725. return hr;
  726. }
  727. HRESULT CImagePropSet::_MapPropidToImgPropid(PROPID propid, PROPID *ppid, PROPID *pidUnicode)
  728. {
  729. HRESULT hr;
  730. *ppid = 0;
  731. *pidUnicode = 0;
  732. if (_fmtid == FMTID_ImageProperties)
  733. {
  734. *ppid = propid; // these go into the EXIF header
  735. hr = S_OK;
  736. }
  737. else
  738. {
  739. hr = E_FAIL;
  740. for (int i = 0; i < ARRAYSIZE(c_rgImagePropertyMap); i++)
  741. {
  742. if (c_rgImagePropertyMap[i].fmtid == _fmtid && c_rgImagePropertyMap[i].propid == propid)
  743. {
  744. *ppid = c_rgImagePropertyMap[i].imgPropid;
  745. *pidUnicode = c_rgImagePropertyMap[i].imgPropidUnicode;
  746. hr = S_OK;
  747. break;
  748. }
  749. }
  750. }
  751. return hr;
  752. }
  753. HRESULT CImagePropSet::_MapImgPropidToPropid(PROPID propid, PROPID *ppid, BOOL *pbUnicode)
  754. {
  755. HRESULT hr;
  756. *pbUnicode = FALSE;
  757. if (_fmtid == FMTID_ImageProperties)
  758. {
  759. *ppid = propid; // EXIF properties don't need to be mapped
  760. hr = S_OK;
  761. }
  762. else
  763. {
  764. *ppid = 0;
  765. hr = E_FAIL;
  766. for (int i = 0; i < ARRAYSIZE(c_rgImagePropertyMap); i++)
  767. {
  768. if (c_rgImagePropertyMap[i].fmtid == _fmtid &&
  769. (c_rgImagePropertyMap[i].imgPropid == propid ||
  770. c_rgImagePropertyMap[i].imgPropidUnicode == propid))
  771. {
  772. *ppid = c_rgImagePropertyMap[i].propid;
  773. *pbUnicode = (c_rgImagePropertyMap[i].imgPropidUnicode == propid);
  774. hr = S_OK;
  775. break;
  776. }
  777. }
  778. }
  779. return hr;
  780. }
  781. HRESULT GetImageFrameCount(Image *pImage, PROPVARIANT *ppv)
  782. {
  783. HRESULT hr = S_FALSE;
  784. LONG lCount;
  785. lCount = 1; //Default to 1 image
  786. UINT uiDim = pImage->GetFrameDimensionsCount();
  787. ppv->vt = VT_EMPTY;
  788. if (uiDim)
  789. {
  790. GUID *pDim = new GUID[uiDim];
  791. if (pDim)
  792. {
  793. if (Ok == pImage->GetFrameDimensionsList(pDim, uiDim))
  794. {
  795. lCount = 0;
  796. ULONG uiN;
  797. for (ULONG i=0;i<uiDim;i++)
  798. {
  799. uiN = pImage->GetFrameCount(&pDim[i]);
  800. lCount += uiN;
  801. }
  802. ppv->vt = VT_UI4;
  803. ppv->lVal = lCount;
  804. hr = S_OK;
  805. }
  806. delete [] pDim;
  807. }
  808. else
  809. {
  810. hr = E_OUTOFMEMORY;
  811. }
  812. }
  813. return hr;
  814. }
  815. HRESULT CImagePropSet::_GetImageSummaryProps(ULONG cpspec, const PROPSPEC rgpspec[], PROPVARIANT rgvar[])
  816. {
  817. HRESULT hr = E_FAIL;
  818. if (_pimg)
  819. {
  820. hr = S_OK;
  821. for (ULONG i = 0; i < cpspec; i++)
  822. {
  823. PropVariantInit(&rgvar[i]);
  824. rgvar[i].vt = VT_UI4;
  825. switch (rgpspec[i].propid)
  826. {
  827. case PIDISI_CX:
  828. rgvar[i].ulVal = _pimg->GetWidth();
  829. break;
  830. case PIDISI_CY:
  831. rgvar[i].ulVal = _pimg->GetHeight();
  832. break;
  833. case PIDISI_RESOLUTIONX:
  834. rgvar[i].ulVal = (ULONG)_pimg->GetHorizontalResolution();
  835. break;
  836. case PIDISI_RESOLUTIONY:
  837. rgvar[i].ulVal = (ULONG)_pimg->GetVerticalResolution();
  838. break;
  839. case PIDISI_BITDEPTH:
  840. {
  841. PixelFormat pf = _pimg->GetPixelFormat();
  842. rgvar[i].ulVal = (pf >> 8) & 0xff;
  843. }
  844. break;
  845. case PIDISI_FRAMECOUNT:
  846. hr = GetImageFrameCount(_pimg, &rgvar[i]);
  847. break;
  848. case PIDISI_DIMENSIONS:
  849. {
  850. TCHAR szFmt[64];
  851. if (LoadString(_Module.GetModuleInstance(), IDS_DIMENSIONS_FMT, szFmt, ARRAYSIZE(szFmt)))
  852. {
  853. DWORD_PTR args[2];
  854. args[0] = (DWORD_PTR)_pimg->GetWidth();
  855. args[1] = (DWORD_PTR)_pimg->GetHeight();
  856. TCHAR szBuffer[64];
  857. FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
  858. szFmt, 0, 0, szBuffer, ARRAYSIZE(szBuffer), (va_list*)args);
  859. hr = SHStrDup(szBuffer, &rgvar[i].pwszVal);
  860. if (SUCCEEDED(hr))
  861. rgvar[i].vt = VT_LPWSTR;
  862. else
  863. rgvar[i].vt = VT_EMPTY;
  864. }
  865. break;
  866. }
  867. default:
  868. rgvar[i].vt = VT_EMPTY;
  869. hr = S_FALSE;
  870. break;
  871. }
  872. }
  873. }
  874. return hr;
  875. }
  876. STDMETHODIMP CPropEnum::Next(ULONG celt, STATPROPSTG *rgelt, ULONG *pceltFetched)
  877. {
  878. HRESULT hr = S_OK;
  879. ULONG uRet = 0;
  880. if (pceltFetched)
  881. {
  882. *pceltFetched = 0;
  883. }
  884. for (;_idx < _nStat && uRet < celt;_idx++)
  885. {
  886. rgelt[uRet] = _pStat[_idx];
  887. uRet++;
  888. }
  889. if (uRet < celt)
  890. {
  891. hr = S_FALSE;
  892. }
  893. if (pceltFetched)
  894. {
  895. *pceltFetched = uRet;
  896. }
  897. return hr;
  898. }
  899. STDMETHODIMP CPropEnum::Skip(ULONG celt)
  900. {
  901. HRESULT hr = S_OK;
  902. ULONG ul = min(_idx+celt, _nStat);
  903. if (ul - _idx < celt)
  904. {
  905. hr = S_FALSE;
  906. }
  907. _idx = ul;
  908. return hr;
  909. }
  910. STDMETHODIMP CPropEnum::Reset(void)
  911. {
  912. _idx = 0;
  913. return S_OK;
  914. }
  915. STDMETHODIMP CPropEnum::Clone(IEnumSTATPROPSTG **ppenum)
  916. {
  917. HRESULT hr = E_OUTOFMEMORY;
  918. CPropEnum *pNew = new CPropEnum(_pStat, _nStat);
  919. if (pNew)
  920. {
  921. hr = pNew->QueryInterface(IID_PPV_ARG(IEnumSTATPROPSTG, ppenum));
  922. pNew->Release();
  923. }
  924. return hr;
  925. }
  926. STDMETHODIMP CPropEnum::QueryInterface(REFIID riid, void **ppvObj)
  927. {
  928. static const QITAB qit[] =
  929. {
  930. QITABENT(CPropEnum, IEnumSTATPROPSTG),
  931. { 0 },
  932. };
  933. return QISearch(this, qit, riid, ppvObj);
  934. }
  935. STDMETHODIMP_(ULONG) CPropEnum::AddRef()
  936. {
  937. return InterlockedIncrement(&_cRef);
  938. }
  939. STDMETHODIMP_(ULONG) CPropEnum::Release()
  940. {
  941. if (InterlockedDecrement(&_cRef))
  942. return _cRef;
  943. delete this;
  944. return 0;
  945. }
  946. CPropEnum::CPropEnum(const STATPROPSTG *pStats, ULONG nStats) : _idx(0), _cRef(1), _pStat(pStats), _nStat(nStats)
  947. {
  948. _Module.Lock();
  949. }
  950. CPropEnum::~CPropEnum()
  951. {
  952. _Module.Unlock();
  953. }