Leaked source code of windows server 2003
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.

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