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.

1958 lines
66 KiB

  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Propio.c
  4. //
  5. // MS Office Properties IO
  6. //
  7. // Notes:
  8. // Because the Document Summary and User-defined objects both store
  9. // their data in one stream (different sections though), one of these
  10. // needs to also be responsible for saving any other sections that
  11. // we don't understand at this time. The rule used here is that
  12. // if the Document Summary object exists, it will store the
  13. // unknown data, otherwise the User-defined object will.
  14. //
  15. // Change history:
  16. //
  17. // Date Who What
  18. // --------------------------------------------------------------------------
  19. // 07/26/94 B. Wentz Created file
  20. // 07/08/96 MikeHill Add all properties to the UDProp list
  21. // (not just props that are UDTYPEs).
  22. //
  23. ////////////////////////////////////////////////////////////////////////////////
  24. #include "priv.h"
  25. #pragma hdrstop
  26. #include <stdio.h> // for sprintf
  27. #include <shlwapi.h>
  28. #ifdef DEBUG
  29. #define typSI 0
  30. #define typDSI 1
  31. #define typUD 2
  32. typedef struct _xopro
  33. {
  34. int typ;
  35. union{
  36. LPSIOBJ lpSIObj;
  37. LPDSIOBJ lpDSIObj;
  38. LPUDOBJ lpUDObj;
  39. };
  40. } XOPRO;
  41. // Plex of xopros
  42. DEFPL (PLXOPRO, XOPRO, ixoproMax, ixoproMac, rgxopro);
  43. #endif
  44. // The constant indicating that the object uses Intel byte-ordering.
  45. #define wIntelByteOrder 0xFFFE
  46. #ifndef CP_WINUNICODE
  47. #define CP_WINUNICODE 1200
  48. #endif
  49. // The name of the Document Summary Information stream.
  50. const GUID FMTID_SummaryInformation = {0xf29f85e0L,0x4ff9,0x1068,0xab,0x91,0x08,0x00,0x2b,0x27,0xb3,0xd9};
  51. const GUID FMTID_DocumentSummaryInformation = {0xd5cdd502L,0x2e9c,0x101b,0x93,0x97,0x08,0x00,0x2b,0x2c,0xf9,0xae};
  52. const GUID FMTID_UserDefinedProperties = {0xd5cdd505L,0x2e9c,0x101b,0x93,0x97,0x08,0x00,0x2b,0x2c,0xf9,0xae};
  53. // Internal prototypes
  54. static DWORD PASCAL DwLoadDocAndUser (LPDSIOBJ lpDSIObj, LPUDOBJ lpUDObj, LPSTORAGE lpStg, DWORD dwFlags, BOOL fIntOnly);
  55. static DWORD PASCAL DwSaveDocAndUser (LPDSIOBJ lpDSIObj, LPUDOBJ lpUDObj, LPSTORAGE lpStg, DWORD dwFlags);
  56. static DWORD PASCAL DwLoadPropSetRange (LPPROPERTYSETSTORAGE lpPropertySetStorage, REFFMTID pfmtid, UINT FAR * lpuCodePage, PROPID propidFirst, PROPID propidLast, PROPVARIANT rgpropvar[], DWORD grfStgMode);
  57. static DWORD PASCAL DwSavePropSetRange (LPPROPERTYSETSTORAGE lpPropertySetStorage, UINT uCodePage, REFFMTID pfmtid, PROPID propidFirst, PROPID propidLast, PROPVARIANT rgpropvarOriginal[], PROPID propidSkip, DWORD grfStgMode);
  58. static BOOL PASCAL FReadDocParts(LPSTREAM lpStm, LPDSIOBJ lpDSIObj);
  59. static BOOL PASCAL FReadAndInsertDocParts(LPSTREAM lpStm, LPDSIOBJ lpDSIObj);
  60. static BOOL PASCAL FReadHeadingPairs(LPSTREAM lpStm, LPDSIOBJ lpDSIObj);
  61. static BOOL PASCAL FReadAndInsertHeadingPairs(LPSTREAM lpStm, LPDSIOBJ lpDSIObj);
  62. static BOOL PASCAL FLoadUserDef(LPUDOBJ lpUDObj, LPPROPERTYSETSTORAGE lpPropertySetStorage, UINT *puCodePage, BOOL fIntOnly, DWORD grfStgMode);
  63. static BOOL PASCAL FSaveUserDef(LPUDOBJ lpUDObj, LPPROPERTYSETSTORAGE lpPropertySetStorage, UINT uCodePage, DWORD grfStgMode );
  64. BOOL OFC_CALLBACK FCPConvert( LPTSTR lpsz, DWORD dwFrom, DWORD dwTo, BOOL fMacintosh )
  65. {
  66. return TRUE;
  67. }
  68. BOOL OFC_CALLBACK FSzToNum(double *lpdbl, LPTSTR lpsz)
  69. {
  70. LPTSTR lpDec;
  71. LPTSTR lpTmp;
  72. double mult;
  73. //
  74. // First, find decimal point
  75. //
  76. for (lpDec = lpsz; *lpDec && *lpDec!=TEXT('.'); lpDec++)
  77. {
  78. ;
  79. }
  80. *lpdbl = 0.0;
  81. mult = 1.0;
  82. //
  83. // Do integer part
  84. //
  85. for (lpTmp = lpDec - 1; lpTmp >= lpsz; lpTmp--)
  86. {
  87. //
  88. // check for negative sign
  89. //
  90. if (*lpTmp == TEXT('-'))
  91. {
  92. //
  93. // '-' sign should only be at beginning of string
  94. //
  95. if (lpTmp == lpsz)
  96. {
  97. if (*lpdbl > 0.0)
  98. {
  99. *lpdbl *= -1.0;
  100. }
  101. continue;
  102. }
  103. else
  104. {
  105. *lpdbl = 0.0;
  106. return FALSE;
  107. }
  108. }
  109. //
  110. // check for positive sign
  111. //
  112. if (*lpTmp == TEXT('+'))
  113. {
  114. //
  115. // '+' sign should only be at beginning of string
  116. //
  117. if (lpTmp == lpsz)
  118. {
  119. if (*lpdbl < 0.0)
  120. {
  121. *lpdbl *= -1.0;
  122. }
  123. continue;
  124. }
  125. else
  126. {
  127. *lpdbl = 0.0;
  128. return FALSE;
  129. }
  130. }
  131. if ( (*lpTmp < TEXT('0')) || (*lpTmp > TEXT('9')) )
  132. {
  133. *lpdbl = 0.0;
  134. return FALSE;
  135. }
  136. *lpdbl += (mult * (double)(*lpTmp - TEXT('0')));
  137. mult *= 10.0;
  138. }
  139. //
  140. // Do decimal part
  141. //
  142. mult = 0.1;
  143. if (*lpDec)
  144. {
  145. for (lpTmp = lpDec + 1; *lpTmp; lpTmp++)
  146. {
  147. if ((*lpTmp < TEXT('0')) || (*lpTmp > TEXT('9')))
  148. {
  149. *lpdbl = 0.0;
  150. return FALSE;
  151. }
  152. *lpdbl += (mult * (double)(*lpTmp - TEXT('0')));
  153. mult *= 0.1;
  154. }
  155. }
  156. return TRUE;
  157. }
  158. BOOL OFC_CALLBACK FNumToSz(double *lpdbl, LPTSTR lpsz, DWORD cbMax)
  159. {
  160. StringCbPrintf(lpsz, cbMax, TEXT("%g"), *lpdbl);
  161. return TRUE;
  162. }
  163. BOOL OFC_CALLBACK FUpdateStats(HWND hwndParent, LPSIOBJ lpSIObj, LPDSIOBJ lpDSIObj)
  164. {
  165. return TRUE;
  166. }
  167. const void *rglpfnProp[] = {
  168. (void *) FCPConvert,
  169. (void *) FSzToNum,
  170. (void *) FNumToSz,
  171. (void *) FUpdateStats
  172. };
  173. ////////////////////////////////////////////////////////////////////////////////
  174. //
  175. // FOfficeCreateAndInitObjects
  176. //
  177. // Purpose:
  178. // Creates and initializes all non-null args.
  179. //
  180. ////////////////////////////////////////////////////////////////////////////////
  181. DLLFUNC BOOL OFC_CALLTYPE FOfficeCreateAndInitObjects(LPSIOBJ *lplpSIObj, LPDSIOBJ *lplpDSIObj, LPUDOBJ *lplpUDObj)
  182. {
  183. if (!FUserDefCreate (lplpUDObj, rglpfnProp))
  184. {
  185. FOfficeDestroyObjects(lplpSIObj, lplpDSIObj, lplpUDObj);
  186. return FALSE;
  187. }
  188. return TRUE;
  189. } // FOfficeCreateAndInitObjects
  190. ////////////////////////////////////////////////////////////////////////////////
  191. //
  192. // FOfficeClearObjects
  193. //
  194. // Purpose:
  195. // Clear any non-null objects
  196. //
  197. ////////////////////////////////////////////////////////////////////////////////
  198. DLLFUNC BOOL OFC_CALLTYPE FOfficeClearObjects (
  199. LPSIOBJ lpSIObj,
  200. LPDSIOBJ lpDSIObj,
  201. LPUDOBJ lpUDObj)
  202. {
  203. FUserDefClear (lpUDObj);
  204. return TRUE;
  205. } // FOfficeClearObjects
  206. ////////////////////////////////////////////////////////////////////////////////
  207. //
  208. // FOfficeDestroyObjects
  209. //
  210. // Purpose:
  211. // Destroy any non-null objects
  212. //
  213. ////////////////////////////////////////////////////////////////////////////////
  214. DLLFUNC BOOL OFC_CALLTYPE FOfficeDestroyObjects (
  215. LPSIOBJ *lplpSIObj,
  216. LPDSIOBJ *lplpDSIObj,
  217. LPUDOBJ *lplpUDObj)
  218. {
  219. FUserDefDestroy (lplpUDObj);
  220. return TRUE;
  221. } // FOfficeDestroyObjects
  222. ////////////////////////////////////////////////////////////////////////////////
  223. //
  224. // DwOfficeLoadProperties
  225. //
  226. // Purpose:
  227. // Populate the objects with data. lpStg is the root stream.
  228. //
  229. ////////////////////////////////////////////////////////////////////////////////
  230. UINT gdwFileCP = CP_ACP;
  231. DLLFUNC DWORD OFC_CALLTYPE DwOfficeLoadProperties (
  232. LPSTORAGE lpStg, // Pointer to root storage
  233. LPSIOBJ lpSIObj, // Pointer to Summary Obj
  234. LPDSIOBJ lpDSIObj, // Pointer to Document Summary obj
  235. LPUDOBJ lpUDObj, // Pointer to User-defined Obj
  236. DWORD dwFlags, // Flags
  237. DWORD grfStgMode) // STGM flags with which to open the property set
  238. {
  239. HRESULT hr = E_FAIL;
  240. BOOL fSuccess = FALSE;
  241. LPPROPERTYSETSTORAGE lpPropertySetStorage = NULL;
  242. // Validate the inputs.
  243. if (lpStg == NULL)
  244. goto Exit;
  245. // Get the IPropertySetStorage from the IStorage.
  246. hr = lpStg->lpVtbl->QueryInterface( lpStg,
  247. &IID_IPropertySetStorage,
  248. &lpPropertySetStorage );
  249. if (FAILED (hr))
  250. {
  251. AssertSz (0, TEXT("Couldn't query for IPropertySetStorage"));
  252. goto Exit;
  253. }
  254. if (lpUDObj != NULL)
  255. {
  256. // Make sure we start with an empty object.
  257. FUserDefClear (lpUDObj);
  258. OfficeDirtyUDObj(lpUDObj, FALSE);
  259. // Load the properties into a linked-list.
  260. if (!FLoadUserDef (lpUDObj,
  261. lpPropertySetStorage,
  262. &gdwFileCP,
  263. FALSE, // Not integers only.
  264. grfStgMode))
  265. {
  266. goto Exit;
  267. }
  268. OfficeDirtyUDObj (lpUDObj, FALSE);
  269. }
  270. // If none of the property sets had a code-page property, set it to
  271. // the current system default.
  272. if (gdwFileCP == CP_ACP)
  273. gdwFileCP = GetACP();
  274. fSuccess = TRUE;
  275. Exit:
  276. RELEASEINTERFACE( lpPropertySetStorage );
  277. if (fSuccess)
  278. {
  279. return (MSO_IO_SUCCESS);
  280. }
  281. else
  282. {
  283. DebugHr (hr);
  284. FOfficeClearObjects (lpSIObj, lpDSIObj, lpUDObj);
  285. OfficeDirtyUDObj (lpUDObj, FALSE);
  286. return (MSO_IO_ERROR);
  287. }
  288. } // DwOfficeLoadProperties
  289. ////////////////////////////////////////////////////////////////////////////////
  290. //
  291. // DwOfficeSaveProperties
  292. //
  293. // Purpose:
  294. // Write the data in the given objects. lpStg is the root stream.
  295. //
  296. ////////////////////////////////////////////////////////////////////////////////
  297. DLLFUNC DWORD OFC_CALLTYPE DwOfficeSaveProperties (
  298. LPSTORAGE lpStg, // Pointer to root storage
  299. LPSIOBJ lpSIObj, // Pointer to Summary Obj
  300. LPDSIOBJ lpDSIObj, // Pointer to Document Summary obj
  301. LPUDOBJ lpUDObj, // Pointer to User-defined Obj
  302. DWORD dwFlags, // Flags
  303. DWORD grfStgMode) // STGM flags with which to open the property set
  304. {
  305. // ------
  306. // Locals
  307. // ------
  308. HRESULT hr = E_FAIL;
  309. BOOL fSuccess = FALSE;
  310. LPPROPERTYSETSTORAGE lpPropertySetStorage = NULL;
  311. // Validate the inputs.
  312. if (lpStg == NULL)
  313. {
  314. AssertSz (0, TEXT("Invalid inputs to DwOfficeSaveProperties"));
  315. goto Exit;
  316. }
  317. // Get the IPropertySetStorage from the IStorage.
  318. hr = lpStg->lpVtbl->QueryInterface( lpStg,
  319. &IID_IPropertySetStorage,
  320. &lpPropertySetStorage );
  321. if (FAILED (hr))
  322. {
  323. AssertSz (0, TEXT("Couldn't query for IPropertySetStorage"));
  324. goto Exit;
  325. }
  326. // ---------------------------------
  327. // Save the User-Defined properties.
  328. // ---------------------------------
  329. if (lpUDObj != NULL)
  330. {
  331. if (((dwFlags & OIO_SAVEIFCHANGEONLY) && (FUserDefShouldSave (lpUDObj))) ||
  332. !(dwFlags & OIO_SAVEIFCHANGEONLY))
  333. {
  334. if (!FSaveUserDef (lpUDObj,
  335. lpPropertySetStorage,
  336. GetACP(),
  337. grfStgMode))
  338. {
  339. AssertSz (0, TEXT("Could not save UserDefined properties"));
  340. goto Exit;
  341. }
  342. }
  343. }
  344. //
  345. // Exit
  346. //
  347. fSuccess = TRUE;
  348. Exit:
  349. RELEASEINTERFACE( lpPropertySetStorage );
  350. if (fSuccess)
  351. {
  352. OfficeDirtyUDObj (lpUDObj, FALSE);
  353. return (TRUE);
  354. }
  355. else
  356. {
  357. DebugHr (hr);
  358. return (FALSE);
  359. }
  360. } // DwOfficeSaveProperties
  361. ///////////////////////////////////////////////////////
  362. //
  363. // DwLoadPropSetRange
  364. //
  365. // Purpose:
  366. // Load a range of properties (specified by the first and
  367. // last property ID) from a given PropertySetStorage. All
  368. // strings are converted to the appropriate system format
  369. // (LPTSTRs).
  370. //
  371. // Inputs:
  372. // LPPROPERTYSETSTORAGE - The set of property storage objects.
  373. // REFFMTID - The Format ID of the desired property set
  374. // UINT * - A location to put the PID_CODEPAGE. This
  375. // should be initialized by the caller to a
  376. // valid default, in case the PID_CODEPAGE
  377. // does not exist.
  378. // PROPID - The first property in the range.
  379. // PROPID - The last property in the range.
  380. // PROPVARIANT[] - An array of PropVariants, large enough
  381. // for the (pidLast-pidFirst+1) properties.
  382. // DWORD - Flags from the STGM enumeration to use when
  383. // opening the property storage.
  384. //
  385. // Output:
  386. // An MSO error code.
  387. //
  388. // Note:
  389. // When strings are converted to the system format, their
  390. // VarTypes are converted too. E.g., if an ANSI VT_LPSTR is
  391. // read from a property set, the string will be converted
  392. // to Unicode, and the VarType will be changed to VT_LPWSTR.
  393. //
  394. ////////////////////////////////////////////////////////////////////////////////
  395. static DWORD PASCAL DwLoadPropSetRange (
  396. LPPROPERTYSETSTORAGE lpPropertySetStorage,
  397. REFFMTID pfmtid,
  398. UINT FAR * lpuCodePage,
  399. PROPID propidFirst,
  400. PROPID propidLast,
  401. PROPVARIANT rgpropvar[],
  402. DWORD grfStgMode)
  403. {
  404. // ------
  405. // Locals
  406. // ------
  407. DWORD dwResult = MSO_IO_ERROR; // The return code.
  408. HRESULT hr; // OLE errors
  409. ULONG ulIndex; // Index into the rgpropvar
  410. // The requested IPropertyStorage
  411. LPPROPERTYSTORAGE lpPropertyStorage;
  412. PROPSPEC FAR * rgpropspec; // The PropSpecs for the ReadMultiple
  413. PROPVARIANT propvarCodePage; // A PropVariant with which to read the PID_CODEPAGE
  414. // The total number of properties to read.
  415. ULONG cProps = propidLast - propidFirst + 1;
  416. // ----------
  417. // Initialize
  418. // ----------
  419. Assert (lpPropertySetStorage != NULL);
  420. Assert (lpPropertySetStorage->lpVtbl != NULL);
  421. Assert (propidLast >= propidFirst);
  422. lpPropertyStorage = NULL;
  423. PropVariantInit( &propvarCodePage );
  424. // Initialize the PropVariants, so that if we
  425. // early-exit, we'll return VT_EMPTY for all the properties.
  426. for (ulIndex = 0; ulIndex < cProps; ulIndex++)
  427. PropVariantInit (&rgpropvar[ulIndex]);
  428. // Allocate an array of PropSpecs.
  429. rgpropspec = LocalAlloc( LPTR, cProps * sizeof (*rgpropspec) );
  430. if (rgpropspec == NULL)
  431. {
  432. AssertSz (0, TEXT("Couldn't alloc rgpropspec"));
  433. goto Exit;
  434. }
  435. // ----------------------
  436. // Open the property set.
  437. // ----------------------
  438. hr = lpPropertySetStorage->lpVtbl->Open(
  439. lpPropertySetStorage, // this pointer
  440. pfmtid, // Identifies propset
  441. grfStgMode, // STGM_ flags
  442. &lpPropertyStorage ); // Result
  443. if (FAILED(hr))
  444. {
  445. // We couldn't open the property set.
  446. if( hr == STG_E_FILENOTFOUND )
  447. {
  448. // No problem, it just didn't exist.
  449. dwResult = MSO_IO_SUCCESS;
  450. goto Exit;
  451. }
  452. else
  453. {
  454. AssertSz (0, TEXT("Couldn't open property set"));
  455. goto Exit;
  456. }
  457. }
  458. // -------------------
  459. // Read the properties
  460. // -------------------
  461. // Initialize the local PropSpec array in preparation for a ReadMultiple.
  462. // The PROPIDs range from propidFirst to propidLast.
  463. for (ulIndex = 0; ulIndex < cProps; ulIndex++)
  464. {
  465. rgpropspec[ ulIndex ].ulKind = PRSPEC_PROPID;
  466. rgpropspec[ ulIndex ].propid = ulIndex + propidFirst;
  467. }
  468. // Read in the properties
  469. hr = lpPropertyStorage->lpVtbl->ReadMultiple (
  470. lpPropertyStorage, // 'this' pointer
  471. cProps, // count
  472. rgpropspec, // Props to read
  473. rgpropvar); // Buffers for props
  474. // Did we fail to read anything?
  475. if (hr != S_OK)
  476. {
  477. // If S_FALSE, no problem; none of the properties existed.
  478. if (hr == S_FALSE)
  479. {
  480. dwResult = MSO_IO_SUCCESS;
  481. goto Exit;
  482. }
  483. else
  484. {
  485. // Otherwise, we have a problem.
  486. AssertSz (0, TEXT("Couldn't read from property set"));
  487. goto Exit;
  488. }
  489. }
  490. // -----------------
  491. // Get the Code-Page
  492. // -----------------
  493. rgpropspec[0].ulKind = PRSPEC_PROPID;
  494. rgpropspec[0].propid = PID_CODEPAGE;
  495. hr = lpPropertyStorage->lpVtbl->ReadMultiple (
  496. lpPropertyStorage, // 'this' pointer
  497. 1, // count
  498. rgpropspec, // Props to read
  499. &propvarCodePage); // Buffer for prop
  500. // We only set the code page if we actually read it.
  501. if (hr == S_OK
  502. &&
  503. propvarCodePage.vt == VT_I2)
  504. {
  505. *lpuCodePage = propvarCodePage.iVal;
  506. }
  507. //*lpuCodePage = GetACP() ;
  508. // ---------------------------
  509. // Correct the string formats.
  510. // ---------------------------
  511. // E.g., if this is a Unicode system, convert LPSTRs to LPWSTRs.
  512. for (ulIndex = 0; ulIndex < cProps; ulIndex++)
  513. {
  514. // Is this is vector of Variants?
  515. if (rgpropvar[ ulIndex ].vt == (VT_VARIANT | VT_VECTOR))
  516. {
  517. // Loop through each element of the vector, converting
  518. // any elements which are strings.
  519. ULONG ulVectorIndex;
  520. for (ulVectorIndex = 0;
  521. ulVectorIndex < rgpropvar[ ulIndex ].capropvar.cElems;
  522. ulVectorIndex++)
  523. {
  524. if (PROPVAR_STRING_CONVERSION_REQUIRED (
  525. &rgpropvar[ulIndex].capropvar.pElems[ulVectorIndex],
  526. *lpuCodePage
  527. ))
  528. {
  529. // Convert the PropVariant string, putting it in a new
  530. // PropVariant.
  531. PROPVARIANT propvarConvert;
  532. PropVariantInit (&propvarConvert);
  533. if (!FPropVarConvertString (&propvarConvert,
  534. &rgpropvar[ulIndex].capropvar.pElems[ulVectorIndex],
  535. *lpuCodePage ))
  536. {
  537. AssertSz (0, TEXT("Couldn't convert string"));
  538. goto Exit;
  539. }
  540. // Clear the old PropVar, and copy in the new one.
  541. PropVariantClear (&rgpropvar[ulIndex].capropvar.pElems[ulVectorIndex]);
  542. rgpropvar[ulIndex].capropvar.pElems[ulVectorIndex] = propvarConvert;
  543. }
  544. } // for (ulVectorIndex = 0; ...
  545. } // if ((rgpropvar[ ulIndex ].vt == (VT_VARIANT | VT_VECTOR))
  546. // This isn't a Variant Vector, but is it a string
  547. // of some kind which requires conversion?
  548. else if (PROPVAR_STRING_CONVERSION_REQUIRED (
  549. &rgpropvar[ ulIndex ],
  550. *lpuCodePage))
  551. {
  552. // Convert the PropVariant string into a new PropVariant
  553. // buffer. The string may be a singleton, or a vector.
  554. PROPVARIANT propvarConvert;
  555. PropVariantInit (&propvarConvert);
  556. if (!FPropVarConvertString (&propvarConvert,
  557. &rgpropvar[ ulIndex ],
  558. *lpuCodePage ))
  559. {
  560. AssertSz (0, TEXT("Couldn't convert string"));
  561. goto Exit;
  562. }
  563. // Free the old PropVar and load the new one.
  564. PropVariantClear (&rgpropvar[ ulIndex ]);
  565. rgpropvar[ ulIndex ] = propvarConvert;
  566. } // else if (PROPVAR_STRING_CONVERSION_REQUIRED ( ...
  567. } // for (ulIndex = 0; ulIndex < cProps; ulIndex++)
  568. // ----
  569. // Exit
  570. // ----
  571. dwResult = MSO_IO_SUCCESS;
  572. Exit:
  573. // Release the code-page just in case somebody put the wrong type
  574. // there (like a blob).
  575. PropVariantClear (&propvarCodePage);
  576. // Release the PropSpecs and the IPropertyStorage
  577. if (rgpropspec != NULL)
  578. {
  579. LocalFree(rgpropspec);
  580. }
  581. RELEASEINTERFACE (lpPropertyStorage);
  582. // If we failed, free the PropVariants.
  583. if (dwResult != MSO_IO_SUCCESS)
  584. {
  585. FreePropVariantArray( cProps, rgpropvar );
  586. DebugHr( hr );
  587. }
  588. return (dwResult);
  589. } // DwLoadPropSetRange
  590. ////////////////////////////////////////////////////////////////////////////////
  591. //
  592. // Wrap of IPropertySetStorage::Create
  593. //
  594. // Each new ANSI property set created by docprop must set PID_CODEPAGE to CP_UTF8
  595. // to avoid ansi<->unicode roundtripping issues.
  596. //
  597. HRESULT _CreatePropertyStorage(
  598. LPPROPERTYSETSTORAGE psetstg,
  599. REFFMTID rfmtid,
  600. CLSID* pclsid,
  601. DWORD grfMode,
  602. UINT* /*IN OUT*/ puCodePage,
  603. IPropertyStorage** ppstg )
  604. {
  605. DWORD grfFlags = (CP_WINUNICODE == (*puCodePage)) ?
  606. PROPSETFLAG_DEFAULT : PROPSETFLAG_ANSI;
  607. HRESULT hr = psetstg->lpVtbl->Create( psetstg, rfmtid, pclsid, grfFlags, grfMode, ppstg );
  608. if( SUCCEEDED( hr ) )
  609. {
  610. if( PROPSETFLAG_ANSI == grfFlags )
  611. {
  612. PROPSPEC propspec = { PRSPEC_PROPID, PID_CODEPAGE };
  613. PROPVARIANT varCP;
  614. varCP.vt = VT_I2;
  615. varCP.iVal = (SHORT)CP_UTF8;
  616. if( SUCCEEDED( (*ppstg)->lpVtbl->WriteMultiple( *ppstg, 1, &propspec, &varCP, PID_UDFIRST ) ) )
  617. *puCodePage = (UINT)MAKELONG(varCP.iVal, 0);
  618. }
  619. }
  620. return hr;
  621. }
  622. ///////////////////////////////////////////////////////
  623. //
  624. // DwSavePropSetRange
  625. //
  626. // Purpose:
  627. // Save a range of properties to a Property Set Storage.
  628. // The properties to be saved are provided in an
  629. // array of PropVariants, and their property IDs are
  630. // specified by the first and last PID for the range.
  631. // The caller may also specify that a property be
  632. // "skipped", i.e., not written.
  633. //
  634. // Inputs:
  635. // LPPROPERTYSETSTORAGE - The Property Set Storage
  636. // UINT - The code page with which the strings
  637. // should be written.
  638. // REFFMTID - The GUID identifying the Property Storage
  639. // within the Property Set Storage.
  640. // PROPID - The PID to assign to the first property.
  641. // PROPID - The PID to assign to the last property
  642. // PROPVARIANT [] - The propeties to write. All strings
  643. // are assumed to be in the system format
  644. // (e.g. VT_LPWSTRs for NT). This array
  645. // is returned un-modified to the caller.
  646. // PROPID - If non-zero, identifies a property
  647. // which should not be written, even if
  648. // it is non-empty. If the property exists
  649. // in the property set, it will be deleted.
  650. // (This was added to provide a way to skip
  651. // the PID_THUMBNAIL.)
  652. // DWORD - Flags from the STGM enumeration to use when
  653. // opening the property storage.
  654. //
  655. // Output:
  656. // An MSO error code.
  657. //
  658. // Notes:
  659. // - If the code page is Unicode, all strings are written as LPWSTRs,
  660. // otherwise, they are written as LPSTRs.
  661. // - Only non-empty properties are written.
  662. //
  663. // Implementation:
  664. // This routine creates a new PropVariant array which is the
  665. // subset of the caller's PropVariant array which must actually
  666. // be written (i.e, it doesn't include the VT_EMPTY properties
  667. // or the 'propidSkip').
  668. //
  669. // We allocate as little extra memory as possible. For example,
  670. // if we have to write a string, we'll copy the pointer to the
  671. // string into the subset PropVariant array. Thus we'll have
  672. // two pointers to the string.
  673. //
  674. // If a string to be written must be converted first (to another
  675. // code-page), then the original PropVariant array will continue
  676. // pointing to the original string, and the subset PropVariant
  677. // array will point to the converted string (and must consequently
  678. // be freed).
  679. //
  680. ////////////////////////////////////////////////////////////////////////////////
  681. static DWORD PASCAL DwSavePropSetRange (
  682. LPPROPERTYSETSTORAGE lpPropertySetStorage,
  683. UINT uCodePage,
  684. REFFMTID pfmtid,
  685. PROPID propidFirst,
  686. PROPID propidLast,
  687. PROPVARIANT rgpropvarOriginal[],
  688. PROPID propidSkip,
  689. DWORD grfStgMode)
  690. {
  691. // ------
  692. // Locals
  693. // ------
  694. DWORD dwResult = MSO_IO_ERROR; // The functions return code.
  695. HRESULT hr; // OLE results.
  696. // The Property Storage to write to
  697. LPPROPERTYSTORAGE lpPropertyStorage = NULL;
  698. ULONG cOriginal; // The size of rgpropvarOriginal,
  699. ULONG cNew; // and the number which must actually be written.
  700. ULONG ulIndex; // Index into rgpropvarOriginal
  701. PROPSPEC FAR * rgpropspecNew = NULL;// PropSpecs for the WriteMultiple
  702. LPPROPVARIANT rgpropvarNew = NULL; // The sub-set of rgpropvarOrigianl we must write.
  703. // The following array has an entry for each entry in rgpropvarNew.
  704. // Each entry identifies the corresponding entry in rgpropvarOriginal.
  705. // E.g. rgMapNewToOriginal[0] is the index in rgpropvarOriginal of
  706. // the first property to be written.
  707. ULONG *rgMapNewToOriginal = NULL;
  708. // ----------
  709. // Initialize
  710. // ----------
  711. cOriginal = propidLast - propidFirst + 1;
  712. cNew = 0;
  713. Assert (cOriginal <= max(NUM_SI_PROPERTIES, NUM_DSI_PROPERTIES));
  714. Assert (lpPropertySetStorage != NULL);
  715. Assert (lpPropertySetStorage->lpVtbl != NULL);
  716. Assert (propidLast >= propidFirst);
  717. Assert (rgpropvarOriginal != NULL);
  718. // Allocate an array of PropSpecs for the WriteMultiple.
  719. rgpropspecNew = LocalAlloc( LPTR, cOriginal * sizeof (*rgpropspecNew));
  720. if (rgpropspecNew == NULL)
  721. {
  722. AssertSz (0, TEXT("Couldn't alloc rgpropspecNew"));
  723. goto Exit;
  724. }
  725. // Allocate an array of PropVariants which will hold the subset
  726. // of the caller's properties which must be written.
  727. // Initialize to zeros so that we don't think we have memory
  728. // to free in the error path.
  729. rgpropvarNew = LocalAlloc( LPTR, cOriginal * sizeof (*rgpropvarNew));
  730. if (rgpropvarNew == NULL)
  731. {
  732. AssertSz (0, TEXT("Couldn't alloc rgpropvarNew"));
  733. goto Exit;
  734. }
  735. // Allocate the look-up-table which maps entries in rgpropvarNew
  736. // to rgpropvarOriginal
  737. rgMapNewToOriginal = LocalAlloc( LPTR, cOriginal * sizeof(*rgMapNewToOriginal));
  738. if (rgMapNewToOriginal == NULL)
  739. {
  740. AssertSz (0, TEXT("Couldn't alloc rgMapNewToOriginal"));
  741. goto Exit;
  742. }
  743. // -------------------------
  744. // Open the Property Storage
  745. // -------------------------
  746. hr = lpPropertySetStorage->lpVtbl->Open(
  747. lpPropertySetStorage, // this pointer
  748. pfmtid,
  749. grfStgMode,
  750. &lpPropertyStorage );
  751. // If it didn't exist, create it.
  752. if( hr == STG_E_FILENOTFOUND )
  753. {
  754. hr = _CreatePropertyStorage( lpPropertySetStorage,
  755. pfmtid,
  756. NULL,
  757. STGM_DIRECT | STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
  758. &uCodePage,
  759. &lpPropertyStorage );
  760. }
  761. // Check the result of the open/create.
  762. if (FAILED(hr))
  763. {
  764. AssertSz (0, TEXT("Couldn't open property set"));
  765. goto Exit;
  766. }
  767. // ---------------------------------------------------
  768. // Copy the properties to be written into rgpropvarNew
  769. // ---------------------------------------------------
  770. // Loop through all the properties in rgpropvarOriginal
  771. for (ulIndex = 0; ulIndex < cOriginal; ulIndex++)
  772. {
  773. // Is this property extant and not the one to skip?
  774. if (rgpropvarOriginal[ ulIndex ].vt != VT_EMPTY
  775. &&
  776. ( propidSkip == 0
  777. ||
  778. propidSkip != propidFirst + ulIndex )
  779. )
  780. {
  781. // We have a property which must be written.
  782. BOOL fVector;
  783. VARTYPE vt;
  784. // Record a mapping from the new index to the original.
  785. rgMapNewToOriginal[ cNew ] = ulIndex;
  786. // Add an entry to the PropSpec array.
  787. rgpropspecNew[ cNew ].ulKind = PRSPEC_PROPID;
  788. rgpropspecNew[ cNew ].propid = propidFirst + ulIndex;
  789. // Get the underlying VarType.
  790. fVector = (rgpropvarOriginal[ ulIndex ].vt & VT_VECTOR) ? TRUE : FALSE;
  791. vt = rgpropvarOriginal[ ulIndex ].vt & ~VT_VECTOR;
  792. // If this property is a vector of variants, some of those
  793. // elements may be strings which need to be converted.
  794. if ((vt == VT_VARIANT) && fVector)
  795. {
  796. ULONG ulVectorIndex;
  797. // We'll inintialize the capropvar.pElems in rgpropvarNew
  798. // so that it points to the one in rgpropvarOriginal. We'll
  799. // only allocate if a conversion is necessary. I.e., we handle
  800. // pElems as a copy-on-write.
  801. rgpropvarNew[ cNew ] = rgpropvarOriginal[ ulIndex ];
  802. // Loop through the elements of the vector.
  803. for (ulVectorIndex = 0;
  804. ulVectorIndex < rgpropvarNew[ cNew ].capropvar.cElems;
  805. ulVectorIndex++)
  806. {
  807. // Is this a string requiring a code-page conversion?
  808. if (PROPVAR_STRING_CONVERSION_REQUIRED(
  809. &rgpropvarOriginal[ulIndex].capropvar.pElems[ulVectorIndex],
  810. uCodePage ))
  811. {
  812. // We must convert this string. Have we allocated a pElems yet?
  813. if (rgpropvarNew[cNew].capropvar.pElems
  814. == rgpropvarOriginal[ulIndex].capropvar.pElems)
  815. {
  816. // Allocate a new pElems for rgpropvarNew
  817. rgpropvarNew[cNew].capropvar.pElems
  818. = CoTaskMemAlloc (rgpropvarNew[cNew].capropvar.cElems
  819. * sizeof(*rgpropvarNew[cNew].capropvar.pElems));
  820. if (rgpropvarNew[cNew].capropvar.pElems == NULL)
  821. {
  822. AssertSz (0, TEXT("Couldn't allocate pElems"));
  823. goto Exit;
  824. }
  825. // Initialize it to match that in rgpropvarOriginal
  826. CopyMemory( rgpropvarNew[cNew].capropvar.pElems,
  827. rgpropvarOriginal[ulIndex].capropvar.pElems,
  828. rgpropvarNew[cNew].capropvar.cElems * sizeof(*rgpropvarNew[cNew].capropvar.pElems)
  829. );
  830. }
  831. // Now, we can convert this string from rgpropvarOriginal into
  832. // rgpropvarNew.
  833. PropVariantInit (&rgpropvarNew[cNew].capropvar.pElems[ulVectorIndex]);
  834. if (!FPropVarConvertString(&rgpropvarNew[cNew].capropvar.pElems[ulVectorIndex],
  835. &rgpropvarOriginal[ulIndex].capropvar.pElems[ulVectorIndex],
  836. uCodePage))
  837. {
  838. AssertSz(0, TEXT("Couldn't convert code page of string"));
  839. goto Exit;
  840. }
  841. } // if (PROPVAR_STRING_CONVERSION_REQUIRED( ...
  842. } // for (ulVectorIndex = 0; ...
  843. } // if (vt == VT_VARIANT && fVector)
  844. // This isn't a variant vector, but is it some type of string
  845. // property for which we must make a conversion?
  846. else if (PROPVAR_STRING_CONVERSION_REQUIRED (
  847. &rgpropvarOriginal[ ulIndex ],
  848. uCodePage))
  849. {
  850. PropVariantInit (&rgpropvarNew[cNew]);
  851. if (!FPropVarConvertString (&rgpropvarNew[cNew],
  852. &rgpropvarOriginal[ulIndex],
  853. uCodePage))
  854. {
  855. AssertSz (0, TEXT("Couldn't convert string"));
  856. goto Exit;
  857. }
  858. } // else if (PROPVAR_STRING_CONVERSION_REQUIRED ( ...
  859. // If neither of the above special-cases were triggered,
  860. // then simply copy the PropVariant structure (but not
  861. // any referred-to data). We save memory by not duplicating
  862. // the referred-to data, but we must be careful in the exit
  863. // not to free it.
  864. else
  865. {
  866. rgpropvarNew[cNew] = rgpropvarOriginal[ulIndex];
  867. } // if ((vt == VT_VARIANT) && fVector) ... else
  868. // We're done copying/converting this property from rgpropvarOriginal
  869. // into rgpropvarNew.
  870. cNew++;
  871. } // if (rgpropvarOriginal[ ulIndex ].vt != VT_EMPTY ...
  872. } // for (ulIndex = 0; ulIndex < cProps; ulIndex++)
  873. // ------------------------
  874. // Write out the properties
  875. // ------------------------
  876. // Write out properties if we found any.
  877. if (cNew > 0)
  878. {
  879. hr = lpPropertyStorage->lpVtbl->WriteMultiple (
  880. lpPropertyStorage, // 'this' pointer
  881. cNew, // Count
  882. rgpropspecNew, // Props to write
  883. rgpropvarNew, // The props
  884. PID_UDFIRST);
  885. if (FAILED(hr))
  886. {
  887. AssertSz (0, TEXT("Couldn't write properties"));
  888. goto Exit;
  889. }
  890. } // if (cNew > 0)
  891. // ---------------------
  892. // Delete the propidSkip
  893. // ---------------------
  894. // If the caller specified a PID to skip, then it should
  895. // be deleted from the property set as well.
  896. if (propidSkip != 0)
  897. {
  898. rgpropspecNew[0].ulKind = PRSPEC_PROPID;
  899. rgpropspecNew[0].propid = propidSkip;
  900. hr = lpPropertyStorage->lpVtbl->DeleteMultiple (
  901. lpPropertyStorage, // this pointer
  902. 1, // Delete one property
  903. rgpropspecNew ); // The prop to delete
  904. if (FAILED(hr))
  905. {
  906. AssertSz (0, TEXT("Couldn't delete the propidSkip"));
  907. goto Exit;
  908. }
  909. }
  910. // ----
  911. // Exit
  912. // ----
  913. dwResult = MSO_IO_SUCCESS;
  914. Exit:
  915. // Clear any of the properties in rgpropvarNew for which new
  916. // buffers were allocated. Then free the rgpropvarNew array itself.
  917. // We know that buffers were allocated for rgpropvarNew if it's contents
  918. // don't match rgpropvarOriginal.
  919. if (rgpropvarNew != NULL)
  920. {
  921. // Loop through rgpropvarNew
  922. for (ulIndex = 0; ulIndex < cNew; ulIndex++)
  923. {
  924. // Was memory allocated for this rgpropvarNew?
  925. if (memcmp (&rgpropvarNew[ ulIndex ],
  926. &rgpropvarOriginal[ rgMapNewToOriginal[ulIndex] ],
  927. sizeof(rgpropvarNew[ ulIndex ])))
  928. {
  929. // Is this a variant vector?
  930. if (rgpropvarNew[ulIndex].vt == (VT_VECTOR | VT_VARIANT))
  931. {
  932. ULONG ulVectIndex;
  933. // Loop through the variant vector and free any PropVariants
  934. // that were allocated. We follow the same principle, if the
  935. // entry in rgpropvarNew doesn't match the entry in
  936. // rgpropvarOriginal, we must have allocated new memory.
  937. for (ulVectIndex = 0;
  938. ulVectIndex < rgpropvarNew[ulIndex].capropvar.cElems;
  939. ulVectIndex++)
  940. {
  941. if (memcmp(&rgpropvarNew[ ulIndex ].capropvar.pElems[ ulVectIndex ],
  942. &rgpropvarOriginal[ rgMapNewToOriginal[ulIndex] ].capropvar.pElems[ ulVectIndex ],
  943. sizeof(rgpropvarNew[ ulIndex ].capropvar.pElems[ ulVectIndex ])))
  944. {
  945. PropVariantClear (&rgpropvarNew[ulIndex].capropvar.pElems[ulVectIndex]);
  946. }
  947. }
  948. // Unconditionally free the pElems buffer.
  949. CoTaskMemFree (rgpropvarNew[ulIndex].capropvar.pElems);
  950. } // if (rgpropvarNew[ulIndex].vt == (VT_VECTOR | VT_VARIANT))
  951. // This isn't a variant vector
  952. else
  953. {
  954. // But does the rgpropvarNew have private memory (i.e.
  955. // a converted string buffer)?
  956. if (memcmp (&rgpropvarNew[ ulIndex ],
  957. &rgpropvarOriginal[ rgMapNewToOriginal[ulIndex] ],
  958. sizeof(rgpropvarNew[ ulIndex ])))
  959. {
  960. PropVariantClear (&rgpropvarNew[ulIndex]);
  961. }
  962. } // if (rgpropvarNew[ulIndex].vt == (VT_VECTOR | VT_VARIANT)) ... else
  963. } // if (rgpropvarNew[ulIndex] ...
  964. } // for (ulIndex = 0; ulIndex < cNew; ulIndex++)
  965. // Free the rgpropvarNew array itself.
  966. LocalFree(rgpropvarNew);
  967. } // if (rgpropvarNew != NULL)
  968. // Free the remaining arrays and release the Property Storage interface.
  969. if (rgpropspecNew != NULL)
  970. {
  971. LocalFree(rgpropspecNew);
  972. }
  973. if (rgMapNewToOriginal != NULL)
  974. {
  975. LocalFree(rgMapNewToOriginal);
  976. }
  977. RELEASEINTERFACE (lpPropertyStorage);
  978. // And we're done.
  979. return (dwResult);
  980. } // DwSavePropSetRange
  981. ////////////////////////////////////////////////////////////////////////////////
  982. //
  983. // FLoadUserDef
  984. //
  985. // Purpose:
  986. // Load the User-Defined properties (those in the second section of
  987. // the DocumentSummaryInformation property set). There can be any number
  988. // of these properties, and the user specifies they're name, value, and
  989. // type (from a limited subset of the VarTypes). Since this is
  990. // variable-sized, the properties are loaded into a linked-list.
  991. //
  992. // Inputs:
  993. // LPUDOBJ - All User-Defined data (including the properties).
  994. // Its m_lpData must point to a valid UDINFO structure.
  995. // LPPROPERTYSETSTORAGE - The Property Set Storage in which we'll find the
  996. // UD property storage.
  997. // UINT* - The PID_CODEPAGE, if it exists. Left unmodified
  998. // if it doesn't exist. All string properties will
  999. // converted to this format. This must be intialized
  1000. // by the caller to a valid default.
  1001. // BOOL - Only load integer values.
  1002. // DWORD - Flags from the STGM enumeration to use when opening
  1003. // the property storage.
  1004. //
  1005. ////////////////////////////////////////////////////////////////////////////////
  1006. static BOOL PASCAL FLoadUserDef (
  1007. LPUDOBJ lpUDObj,
  1008. LPPROPERTYSETSTORAGE lpPropertySetStorage,
  1009. UINT *puCodePage,
  1010. BOOL fIntOnly, // Load Int Properties only?
  1011. DWORD grfStgMode)
  1012. {
  1013. // ------
  1014. // Locals
  1015. // ------
  1016. BOOL fSuccess = FALSE; // Return code to the caller.
  1017. HRESULT hr; // Error codes for OLE calls.
  1018. LPPROPERTYSTORAGE lpPropertyStorage = NULL; // The UD property storage
  1019. LPENUMSTATPROPSTG lpEnum = NULL; // Enumerates the UD property storage
  1020. STATPROPSETSTG statpropsetstg; // Holds the ClassID from the property storage
  1021. // Used in ReadMultiple call.
  1022. PROPSPEC rgpropspec[ DEFAULT_IPROPERTY_COUNT ];
  1023. // A subset of the UD properties
  1024. PROPVARIANT rgpropvar[ DEFAULT_IPROPERTY_COUNT ];
  1025. // Stats on a subset of the UD properties
  1026. STATPROPSTG rgstatpropstg[ DEFAULT_IPROPERTY_COUNT ];
  1027. ULONG ulIndex; // Index into the above arrays.
  1028. PROPSPEC propspec; // PropSpec for reading the code-page
  1029. LPUDPROP lpudprop = NULL; // A single UD property (points to the PropVariant)
  1030. ULONG cEnumerated = 0; // Number of properties found in an enumeration
  1031. // --------------
  1032. // Initialization
  1033. // --------------
  1034. Assert (!fIntOnly); // No longer used.
  1035. Assert (lpUDObj != NULL && GETUDINFO(lpUDObj) != NULL);
  1036. Assert (puCodePage != NULL);
  1037. // We need to zero-out the PropVariant and StatPropStg
  1038. // arrays so that we don't think they need to be freed
  1039. // in the Exit block.
  1040. ZeroMemory(rgpropvar, sizeof (rgpropvar));
  1041. ZeroMemory(rgstatpropstg, sizeof (rgstatpropstg));
  1042. // -----------------------------------------
  1043. // Get the PropertyStorage and an Enumerator
  1044. // -----------------------------------------
  1045. // Open the IPropertyStorage and check for errors.
  1046. hr = lpPropertySetStorage->lpVtbl->Open(
  1047. lpPropertySetStorage, // this pointer
  1048. &FMTID_UserDefinedProperties,
  1049. grfStgMode,
  1050. &lpPropertyStorage );
  1051. if (FAILED(hr))
  1052. {
  1053. // We couldn't open the property set.
  1054. if( hr == STG_E_FILENOTFOUND )
  1055. {
  1056. // No problem, it just didn't exist.
  1057. fSuccess = TRUE;
  1058. goto Exit;
  1059. }
  1060. else
  1061. {
  1062. AssertSz (0, TEXT("Couldn't open property set"));
  1063. goto Exit;
  1064. }
  1065. }
  1066. // Save the property storage's class ID (identifying the application
  1067. // which is primarily responsible for it). We do this because
  1068. // we may later delete the existing property set.
  1069. hr = lpPropertyStorage->lpVtbl->Stat (lpPropertyStorage, &statpropsetstg);
  1070. if (FAILED(hr))
  1071. {
  1072. AssertSz (0, TEXT("Couldn't Stat the Property Storage"));
  1073. goto Exit;
  1074. }
  1075. GETUDINFO(lpUDObj)->clsid = statpropsetstg.clsid;
  1076. // Get the IEnum interface and check for errors.
  1077. hr = lpPropertyStorage->lpVtbl->Enum(
  1078. lpPropertyStorage,
  1079. &lpEnum );
  1080. if (FAILED(hr))
  1081. {
  1082. AssertSz (0, TEXT("Couldn't enumerate the PropertyStorage"));
  1083. goto Exit;
  1084. }
  1085. // ------------------
  1086. // Read the Code Page
  1087. // ------------------
  1088. propspec.ulKind = PRSPEC_PROPID;
  1089. propspec.propid = PID_CODEPAGE;
  1090. hr = lpPropertyStorage->lpVtbl->ReadMultiple (lpPropertyStorage, 1, &propspec, &rgpropvar[0]);
  1091. if (FAILED(hr))
  1092. {
  1093. AssertSz (0, TEXT("Couldn't get property set"));
  1094. }
  1095. // If this is a valid PID_CODEPAGE, give it to the caller.
  1096. if (hr == S_OK && rgpropvar[0].vt == VT_I2)
  1097. {
  1098. *puCodePage = (UINT)MAKELONG(rgpropvar[0].iVal, 0);
  1099. }
  1100. PropVariantClear (&rgpropvar[0]);
  1101. // -------------------------------------------------------------
  1102. // Loop through the properties and add to the UDPROPS structure.
  1103. // -------------------------------------------------------------
  1104. // This loop executes once for each enumeration. Each enumeration
  1105. // gets multiple STATPROPSTGs, so within this loop an inner loop
  1106. // will process each property. This two-level looping mechanism is
  1107. // used in order to reduce the number of ReadMultiples.
  1108. // Use the IEnum to load the first set of STATPROPSTGs.
  1109. hr = lpEnum->lpVtbl->Next (lpEnum, DEFAULT_IPROPERTY_COUNT, rgstatpropstg, &cEnumerated);
  1110. if (FAILED(hr))
  1111. {
  1112. AssertSz (0, TEXT("Couldn't get next StatPropStg"));
  1113. goto Exit;
  1114. }
  1115. Assert (cEnumerated <= DEFAULT_IPROPERTY_COUNT);
  1116. // If the last IEnum returned properties, process them here.
  1117. // At the end of this while loop, we re-call the IEnum, thus continuing
  1118. // until no properties are left to be enumerated.
  1119. while (cEnumerated)
  1120. {
  1121. // ------------------------------
  1122. // Read this batch of properties.
  1123. // ------------------------------
  1124. for (ulIndex = 0; ulIndex < cEnumerated; ulIndex++)
  1125. {
  1126. rgpropspec[ ulIndex ].ulKind = PRSPEC_PROPID;
  1127. rgpropspec[ ulIndex ].propid = rgstatpropstg[ ulIndex ].propid;
  1128. }
  1129. // Read the properties.
  1130. hr = lpPropertyStorage->lpVtbl->ReadMultiple(
  1131. lpPropertyStorage,
  1132. cEnumerated,
  1133. rgpropspec,
  1134. rgpropvar );
  1135. if (FAILED(hr))
  1136. {
  1137. AssertSz (0, TEXT("Couldn't read from property set"));
  1138. goto Exit;
  1139. }
  1140. // ------------------------------------------------------
  1141. // Loop through the properties, adding them to the UDOBJ.
  1142. // ------------------------------------------------------
  1143. for (ulIndex = 0; ulIndex < cEnumerated; ulIndex++)
  1144. {
  1145. // Convert string PropVariants to the right code page.
  1146. // We won't worry about Variants which are strings, because
  1147. // this is not a legal type for the UD properties.
  1148. if (PROPVAR_STRING_CONVERSION_REQUIRED (
  1149. &rgpropvar[ ulIndex ],
  1150. *puCodePage))
  1151. {
  1152. // Convert the string in the PropVariant, putting the
  1153. // result in a temporary PropVariant.
  1154. PROPVARIANT propvarConvert;
  1155. PropVariantInit (&propvarConvert);
  1156. if (!FPropVarConvertString (&propvarConvert,
  1157. &rgpropvar[ulIndex],
  1158. *puCodePage))
  1159. {
  1160. AssertSz (0, TEXT("Couldn't convert string"));
  1161. goto Exit;
  1162. }
  1163. // Free the old PropVariant, and load in the converted
  1164. // one.
  1165. PropVariantClear (&rgpropvar[ ulIndex ]);
  1166. rgpropvar[ ulIndex ] = propvarConvert;
  1167. }
  1168. // Allocate a new UDPROP structure, which will be added to the
  1169. // linked-list.
  1170. lpudprop = LpudpropCreate();
  1171. if (lpudprop == NULL)
  1172. {
  1173. goto Exit;
  1174. }
  1175. // Add this UDPROP to the linked-list. On success, this will assume
  1176. // responsibility for the PropVariant and STATPROPSTG buffers, and
  1177. // will NULL out our pointers accordingly.
  1178. if (!FAddPropToList (lpUDObj,
  1179. &rgpropvar[ ulIndex ],
  1180. &rgstatpropstg[ ulIndex ],
  1181. lpudprop))
  1182. {
  1183. goto Exit;
  1184. }
  1185. lpudprop = NULL;
  1186. } // for (ulIndex = 0; ulIndex < cEnumerated; ulIndex++)
  1187. // ---------------------
  1188. // Get a new enumeration
  1189. // ---------------------
  1190. // We've processed all the properties in the last enumeration, let's get
  1191. // a new set (if there are any). If there are no more, cEnumerated, will be
  1192. // zero, and we'll break out of the outer while loop.
  1193. FreePropVariantArray( cEnumerated, rgpropvar );
  1194. hr = lpEnum->lpVtbl->Next (lpEnum, DEFAULT_IPROPERTY_COUNT, rgstatpropstg, &cEnumerated);
  1195. if (FAILED(hr))
  1196. {
  1197. AssertSz (0, TEXT("Couldn't get next StatPropStg"));
  1198. goto Exit;
  1199. }
  1200. } // while (cEnumerated)
  1201. // ----
  1202. // Exit
  1203. // ----
  1204. fSuccess = TRUE;
  1205. Exit:
  1206. // Free any properties with buffers. This will only happen
  1207. // if there was an error.
  1208. if (cEnumerated > 0)
  1209. {
  1210. FreePropVariantArray (cEnumerated, rgpropvar);
  1211. }
  1212. // Again if there was an error, we must free the UDPROP object.
  1213. if (lpudprop)
  1214. {
  1215. VUdpropFree (&lpudprop);
  1216. }
  1217. // Free any name buffers we still have from the enumerations.
  1218. // Once again, this is only necessary if there was an error.
  1219. for (ulIndex = 0; ulIndex < cEnumerated; ulIndex++)
  1220. {
  1221. if (rgstatpropstg[ ulIndex ].lpwstrName != NULL)
  1222. {
  1223. CoTaskMemFree (rgstatpropstg[ ulIndex ].lpwstrName);
  1224. }
  1225. }
  1226. // Release the Property Storage and Enumeration interfaces.
  1227. RELEASEINTERFACE (lpEnum);
  1228. RELEASEINTERFACE (lpPropertyStorage);
  1229. return fSuccess;
  1230. } // FLoadUserDef
  1231. ////////////////////////////////////////////////////////////////////////////////
  1232. //
  1233. // FSaveUserDef
  1234. //
  1235. // Purpose:
  1236. // Save the User-Defined properties to the second section of
  1237. // the DocumentSummaryInformation property set.
  1238. //
  1239. // Inputs:
  1240. // LPUDOBJ - All UD data (including the properties)
  1241. // It's m_lpData must point to a valid UDINFO structure.
  1242. // LPPROPERTYSETSTORAGE - The Property Set Storage
  1243. // UINT - The code page in which strings should be
  1244. // written. If Unicode, all strings are
  1245. // written as LPWSTRs, otherwise all strings
  1246. // are written as LPSTRs.
  1247. // DWORD - Flags from the STGM enumeration to use when
  1248. // opening the property storage.
  1249. //
  1250. // Outputs:
  1251. // TRUE if successful.
  1252. //
  1253. // Pre-conditions:
  1254. // The properties to be written are all from the UDTYPES
  1255. // enumeration.
  1256. //
  1257. // Implementation:
  1258. // Properties which are links to application data require special
  1259. // handling. First, the property value is written (along with its
  1260. // name). Then, the application-defined link name is
  1261. // written (e.g. the Bookmark name in Word). The link name
  1262. // is written using the same PID as was the link value, except that
  1263. // the PID_LINKMASK is ORed in. The link name property has no name
  1264. // in the property set dictionary.
  1265. //
  1266. ////////////////////////////////////////////////////////////////////////////////
  1267. static
  1268. BOOL PASCAL FSaveUserDef (
  1269. LPUDOBJ lpUDObj,
  1270. LPPROPERTYSETSTORAGE lpPropertySetStorage,
  1271. UINT uCodePage,
  1272. DWORD grfStgMode)
  1273. {
  1274. // ------
  1275. // Locals
  1276. // ------
  1277. BOOL fSuccess = FALSE; // What to return to the caller.
  1278. HRESULT hr; // OLE result codes.
  1279. BOOL fLink, fLinkInvalid;
  1280. // The UD Property Storage
  1281. LPPROPERTYSTORAGE lpPropertyStorage = NULL;
  1282. LPUDITER lpudi = NULL; // Iterates the linked-list of UDPROPs
  1283. LPPROPVARIANT lppropvar = NULL; // A property from the linked-list
  1284. ULONG ulIndex; // Generic index into arrays
  1285. PROPID propid; // The PID to assign to the next property
  1286. // Arrays to be used in the WriteMultiple. The array of BOOLs
  1287. // indicate which elements of the PropVariant array must be freed.
  1288. ULONG ulPropIndex = 0;
  1289. PROPSPEC rgpropspec[ DEFAULT_IPROPERTY_COUNT ];
  1290. PROPVARIANT rgpropvar[ DEFAULT_IPROPERTY_COUNT ];
  1291. BOOL rgfFreePropVar[ DEFAULT_IPROPERTY_COUNT ];
  1292. // Arrays to be used in the WritePropertyNames.
  1293. ULONG ulNameIndex = 0;
  1294. PROPID rgpropidName[ DEFAULT_IPROPERTY_COUNT ];
  1295. LPWSTR rglpwstrName[ DEFAULT_IPROPERTY_COUNT ];
  1296. // ----------
  1297. // Initialize
  1298. // ----------
  1299. Assert (lpUDObj != NULL && GETUDINFO(lpUDObj) != NULL);
  1300. Assert (lpPropertySetStorage != NULL && lpPropertySetStorage->lpVtbl != NULL);
  1301. // Initialize the necessary arrays, so that we don't unnecessarily
  1302. // free something in the Error path.
  1303. ZeroMemory(rgpropvar, sizeof(rgpropvar));
  1304. ZeroMemory(rgfFreePropVar, sizeof(rgfFreePropVar));
  1305. ZeroMemory(rglpwstrName, sizeof(rglpwstrName));
  1306. // Delete the existing property set and create a new empty one.
  1307. // We must do this because we don't know which of the
  1308. // existing properties need to be deleted, we only know what
  1309. // the current set of properties should be.
  1310. hr = lpPropertySetStorage->lpVtbl->Delete(
  1311. lpPropertySetStorage,
  1312. &FMTID_UserDefinedProperties );
  1313. if (FAILED(hr))
  1314. {
  1315. if (hr != STG_E_FILENOTFOUND)
  1316. {
  1317. AssertSz (0, TEXT("Couldn't remove old properties"));
  1318. goto Exit;
  1319. }
  1320. }
  1321. hr = _CreatePropertyStorage( lpPropertySetStorage,
  1322. &FMTID_UserDefinedProperties,
  1323. &GETUDINFO(lpUDObj)->clsid,
  1324. grfStgMode,
  1325. &uCodePage,
  1326. &lpPropertyStorage );
  1327. if (FAILED(hr))
  1328. {
  1329. AssertSz (0, TEXT("Couldn't open User-Defined property set"));
  1330. goto Exit;
  1331. }
  1332. // Create an iterator which we use to enumerate the properties
  1333. // (UDPROPs) in the linked-list.
  1334. lpudi = LpudiUserDefCreateIterator (lpUDObj);
  1335. // ------------------------------------------------------------------
  1336. // Loop through the properties and write them to the UD property set.
  1337. // ------------------------------------------------------------------
  1338. // We use a two-layer loop. The inner loop batches a group of properties
  1339. // in a PropVariant array, and then writes them to the Property Storage.
  1340. // The outer loop repeats this process until there are no more properties.
  1341. // This two-layer mechanism is desirable so that we reduce the number
  1342. // of WriteMultiple calls.
  1343. propid = PID_UDFIRST;
  1344. fLink = FALSE;
  1345. while (TRUE)
  1346. {
  1347. // ------------------------------------------
  1348. // Batch up a set of properties to be written
  1349. // ------------------------------------------
  1350. ulPropIndex = ulNameIndex = 0;
  1351. // We will break out of this loop when we have no more properties
  1352. // or if we have enough for a WriteMultiple.
  1353. while (FUserDefIteratorValid (lpudi))
  1354. {
  1355. Assert (lpudi->lpudp != NULL);
  1356. // ----------------------------------------------------------------------
  1357. // Create entries in the arrays for WriteMultiple and WritePropertyNames.
  1358. // ----------------------------------------------------------------------
  1359. // If fLink is TRUE, it means that we've written out the
  1360. // property, and now we need to write out the link name
  1361. // (with the PID_LINKMASK ORed into the propid).
  1362. if (!fLink)
  1363. {
  1364. // We aren't writing a link. So let's get the
  1365. // property from the linked-list (we know it exists because
  1366. // FUserDefIteratorValid was true).
  1367. lppropvar
  1368. = LppropvarUserDefGetIteratorVal (lpudi, NULL, NULL);
  1369. if (lppropvar == NULL)
  1370. {
  1371. AssertSz (0, TEXT("Invalid PropVariant in iterator"));
  1372. goto Exit;
  1373. }
  1374. // Copy this propvariant into the array which will be used for
  1375. // the WriteMultiple. Note that we do not copy any referenced
  1376. // buffer (e.g. we don't copy the string buffer if this is a string).
  1377. rgpropvar[ ulPropIndex ] = *lppropvar;
  1378. // If this property has a name, prepare to write it.
  1379. if (lpudi->lpudp->lpstzName != NULL)
  1380. {
  1381. // Add this name to rglpwstrName & rgpropidName.
  1382. // Add this name to the list of those to be written.
  1383. rglpwstrName[ ulNameIndex ] = lpudi->lpudp->lpstzName;
  1384. // Add this propid to the list of those with names.
  1385. rgpropidName[ ulNameIndex ] = propid;
  1386. } // if (lpudi->lpudp->lpstzName != NULL)
  1387. } // if (!fLink)
  1388. else
  1389. {
  1390. // We are processing a link name. I.e., we've written the
  1391. // property value, now we need to write the name of the link,
  1392. // as a property, with the PID_LINKSMASK bit set in the PID.
  1393. Assert (lpudi->lpudp->lpstzLink != NULL);
  1394. // Create a entry in the PropVariant.
  1395. rgpropvar[ ulPropIndex ].vt = VT_LPTSTR;
  1396. (LPTSTR) rgpropvar[ ulPropIndex ].pszVal = lpudi->lpudp->lpstzLink;
  1397. }
  1398. // rgpropvar[ulPropIndex] now holds the property to be written,
  1399. // whether it is a real property or a link name.
  1400. // ------------------------------------
  1401. // Convert strings to the proper format.
  1402. // -------------------------------------
  1403. // (This could also convert the type from LPWSTR to LPSTR, or vice-versa).
  1404. // We don't have to worry about strings in vectors or in
  1405. // variant vectors, because these are illegal types for this
  1406. // property set.
  1407. if (rgpropvar[ ulPropIndex ].vt == VT_LPTSTR)
  1408. {
  1409. // If this string needs to be converted do so, putting the converted
  1410. // string in a new buffer. So,
  1411. // the caller's PropVariant still points to the old buffer,
  1412. // and our rgpropvar points to the new buffer.
  1413. if (PROPVAR_STRING_CONVERSION_REQUIRED (
  1414. &rgpropvar[ ulPropIndex ],
  1415. uCodePage))
  1416. {
  1417. // Convert the string into a temporary PropVariant.
  1418. PROPVARIANT propvarConvert;
  1419. PropVariantInit (&propvarConvert);
  1420. if (!FPropVarConvertString (&propvarConvert,
  1421. &rgpropvar[ ulPropIndex ],
  1422. uCodePage ))
  1423. {
  1424. AssertSz (0, TEXT("Couldn't convert string"));
  1425. goto Exit;
  1426. }
  1427. // Load this new PropVariant into rgpropvar, but don't
  1428. // delete the old buffer (so that we leave the linked-list
  1429. // of UDPROPs intact).
  1430. rgpropvar[ ulPropIndex ] = propvarConvert;
  1431. // Since we just created a new buffer, we must remember to free it.
  1432. rgfFreePropVar[ ulPropIndex ] = TRUE;
  1433. } // if (PROPVAR_STRING_CONVERSION_REQUIRED ( ...
  1434. } // if (rgpropvar[ ulPropIndex ].vt == VT_LPTSTR)
  1435. // --------------------------
  1436. // Finish this loop iteration
  1437. // --------------------------
  1438. // Set up the PropSpec.
  1439. rgpropspec[ ulPropIndex ].ulKind = PRSPEC_PROPID;
  1440. rgpropspec[ ulPropIndex ].propid = propid;
  1441. // If this is a link name, set the bit in the PID.
  1442. if (fLink)
  1443. {
  1444. rgpropspec[ ulPropIndex ].propid |= PID_LINKMASK;
  1445. }
  1446. // Advance the property index. And if we set a name, advance
  1447. // the name index.
  1448. ulPropIndex++;
  1449. if (rglpwstrName[ ulNameIndex ] != NULL)
  1450. {
  1451. ulNameIndex++;
  1452. }
  1453. // If we've just processed a link, or this is a property
  1454. // which is not linked to application content, then move on to the next property
  1455. // in the iterator. If we've just processed a property value that
  1456. // is linked, set fLink so that on the next pass through
  1457. // this loop, we'll write out the link name.
  1458. if (fLink || !FUserDefIteratorIsLink (lpudi))
  1459. {
  1460. fLink = FALSE;
  1461. propid++;
  1462. FUserDefIteratorNext (lpudi);
  1463. }
  1464. else
  1465. {
  1466. fLink = TRUE;
  1467. }
  1468. // If there's no more room in the WriteMultiple arrays,
  1469. // then write out the properties. We'll return to this
  1470. // inner loop when that's complete.
  1471. if (ulPropIndex >= DEFAULT_IPROPERTY_COUNT)
  1472. {
  1473. break;
  1474. }
  1475. } // while (FUserDefIteratorValid (lpudi))
  1476. // If broke out of the previous loop becuase there were no
  1477. // more properties, then we can break out of the outer loop
  1478. // as well -- we're done.
  1479. if (ulPropIndex == 0)
  1480. {
  1481. break;
  1482. }
  1483. // ---------------------
  1484. // Write the properties.
  1485. // ---------------------
  1486. hr = lpPropertyStorage->lpVtbl->WriteMultiple (
  1487. lpPropertyStorage, // 'this' pointer
  1488. ulPropIndex, // Number of properties
  1489. rgpropspec, // Property specifiers
  1490. rgpropvar, // The properties
  1491. PID_UDFIRST); // Not used.
  1492. if (FAILED(hr))
  1493. {
  1494. AssertSz (0, TEXT("Couldn't write properties"));
  1495. goto Exit;
  1496. }
  1497. // If we created any new buffers during string conversion,
  1498. // free them now.
  1499. for (ulIndex = 0; ulIndex < ulPropIndex; ulIndex++)
  1500. {
  1501. if (rgfFreePropVar[ ulIndex ])
  1502. {
  1503. PropVariantClear (&rgpropvar[ ulIndex ]);
  1504. rgfFreePropVar[ ulIndex ] = FALSE;
  1505. }
  1506. }
  1507. // ----------------
  1508. // Write the Names.
  1509. // ----------------
  1510. if (ulNameIndex != 0)
  1511. {
  1512. hr = lpPropertyStorage->lpVtbl->WritePropertyNames (
  1513. lpPropertyStorage, // 'this' pointer
  1514. ulNameIndex, // Number of names
  1515. rgpropidName, // PIDs for these names
  1516. rglpwstrName ); // The names
  1517. if (FAILED(hr))
  1518. {
  1519. AssertSz (0, TEXT("Couldn't write property names"));
  1520. goto Exit;
  1521. }
  1522. } // if (ulNameIndex != 0)
  1523. // Clear the names array.
  1524. for (ulIndex = 0; ulIndex < ulNameIndex; ulIndex++)
  1525. {
  1526. rglpwstrName[ ulIndex ] = NULL;
  1527. }
  1528. }
  1529. // ----
  1530. // Exit
  1531. // ----
  1532. fSuccess = TRUE;
  1533. Exit:
  1534. // Free the iterator
  1535. if (lpudi)
  1536. {
  1537. FUserDefDestroyIterator (&lpudi);
  1538. }
  1539. // Free any memory that was allocated for PropVariants.
  1540. for (ulIndex = 0; ulIndex < ulPropIndex; ulIndex++)
  1541. {
  1542. if (rgfFreePropVar[ ulIndex ])
  1543. {
  1544. PropVariantClear (&rgpropvar[ ulIndex ]);
  1545. }
  1546. }
  1547. // Release the UD Property Storage.
  1548. RELEASEINTERFACE (lpPropertyStorage);
  1549. return (fSuccess);
  1550. } // FSaveUserDef