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.

2255 lines
74 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. #ifdef UNICODE
  161. swprintf(lpsz, TEXT("%g"), *(double *) lpdbl);
  162. #else
  163. sprintf(lpsz, TEXT("%g"), *(double *) lpdbl);
  164. #endif
  165. return TRUE;
  166. }
  167. BOOL OFC_CALLBACK FUpdateStats(HWND hwndParent, LPSIOBJ lpSIObj, LPDSIOBJ lpDSIObj)
  168. {
  169. return TRUE;
  170. }
  171. const void *rglpfnProp[] = {
  172. (void *) FCPConvert,
  173. (void *) FSzToNum,
  174. (void *) FNumToSz,
  175. (void *) FUpdateStats
  176. };
  177. ////////////////////////////////////////////////////////////////////////////////
  178. //
  179. // FOfficeCreateAndInitObjects
  180. //
  181. // Purpose:
  182. // Creates and initializes all non-null args.
  183. //
  184. ////////////////////////////////////////////////////////////////////////////////
  185. DLLFUNC BOOL OFC_CALLTYPE FOfficeCreateAndInitObjects(LPSIOBJ *lplpSIObj, LPDSIOBJ *lplpDSIObj, LPUDOBJ *lplpUDObj)
  186. {
  187. #ifdef _ABBREVIATED_DOCPROP_
  188. if (!FUserDefCreate (lplpUDObj, rglpfnProp))
  189. #else //_ABBREVIATED_DOCPROP_
  190. if (!FSumInfoCreate (lplpSIObj, rglpfnProp) ||
  191. !FDocSumCreate (lplpDSIObj, rglpfnProp) ||
  192. !FUserDefCreate (lplpUDObj, rglpfnProp))
  193. #endif //_ABBREVIATED_DOCPROP_
  194. {
  195. FOfficeDestroyObjects(lplpSIObj, lplpDSIObj, lplpUDObj);
  196. return FALSE;
  197. }
  198. return TRUE;
  199. } // FOfficeCreateAndInitObjects
  200. ////////////////////////////////////////////////////////////////////////////////
  201. //
  202. // FOfficeClearObjects
  203. //
  204. // Purpose:
  205. // Clear any non-null objects
  206. //
  207. ////////////////////////////////////////////////////////////////////////////////
  208. DLLFUNC BOOL OFC_CALLTYPE FOfficeClearObjects (
  209. LPSIOBJ lpSIObj,
  210. LPDSIOBJ lpDSIObj,
  211. LPUDOBJ lpUDObj)
  212. {
  213. #ifndef _ABBREVIATED_DOCPROP_
  214. FSumInfoClear (lpSIObj);
  215. FDocSumClear (lpDSIObj);
  216. #endif //_ABBREVIATED_DOCPROP_
  217. FUserDefClear (lpUDObj);
  218. return TRUE;
  219. } // FOfficeClearObjects
  220. #ifdef DEBUG
  221. int CmpXOpro(XOPRO *pxopro1, XOPRO *pxopro2)
  222. {
  223. if(pxopro1->typ==pxopro2->typ)
  224. {
  225. switch(pxopro1->typ)
  226. {
  227. case typSI:
  228. if(pxopro1->lpSIObj==pxopro2->lpSIObj)
  229. return(sgnEQ);
  230. break;
  231. case typDSI:
  232. if(pxopro1->lpDSIObj==pxopro2->lpDSIObj)
  233. return(sgnEQ);
  234. break;
  235. case typUD:
  236. if(pxopro1->lpUDObj==pxopro2->lpUDObj)
  237. return(sgnEQ);
  238. break;
  239. default:
  240. Assert(fFalse);
  241. break;
  242. }
  243. }
  244. return(sgnNE);
  245. }
  246. #endif
  247. ////////////////////////////////////////////////////////////////////////////////
  248. //
  249. // FOfficeDestroyObjects
  250. //
  251. // Purpose:
  252. // Destroy any non-null objects
  253. //
  254. ////////////////////////////////////////////////////////////////////////////////
  255. DLLFUNC BOOL OFC_CALLTYPE FOfficeDestroyObjects (
  256. LPSIOBJ *lplpSIObj,
  257. LPDSIOBJ *lplpDSIObj,
  258. LPUDOBJ *lplpUDObj)
  259. {
  260. #ifndef _ABBREVIATED_DOCPROP_
  261. FSumInfoDestroy (lplpSIObj); // We don't care what these guys return
  262. FDocSumDestroy (lplpDSIObj);
  263. #endif //_ABBREVIATED_DOCPROP_
  264. FUserDefDestroy (lplpUDObj);
  265. return TRUE;
  266. } // FOfficeDestroyObjects
  267. ////////////////////////////////////////////////////////////////////////////////
  268. //
  269. // DwOfficeLoadProperties
  270. //
  271. // Purpose:
  272. // Populate the objects with data. lpStg is the root stream.
  273. //
  274. ////////////////////////////////////////////////////////////////////////////////
  275. UINT gdwFileCP = CP_ACP;
  276. DLLFUNC DWORD OFC_CALLTYPE DwOfficeLoadProperties (
  277. LPSTORAGE lpStg, // Pointer to root storage
  278. LPSIOBJ lpSIObj, // Pointer to Summary Obj
  279. LPDSIOBJ lpDSIObj, // Pointer to Document Summary obj
  280. LPUDOBJ lpUDObj, // Pointer to User-defined Obj
  281. DWORD dwFlags, // Flags
  282. DWORD grfStgMode) // STGM flags with which to open the property set
  283. {
  284. HRESULT hr = E_FAIL;
  285. BOOL fSuccess = FALSE;
  286. LPPROPERTYSETSTORAGE lpPropertySetStorage = NULL;
  287. // Validate the inputs.
  288. if (lpStg == NULL)
  289. goto Exit;
  290. // Get the IPropertySetStorage from the IStorage.
  291. hr = lpStg->lpVtbl->QueryInterface( lpStg,
  292. &IID_IPropertySetStorage,
  293. &lpPropertySetStorage );
  294. if (FAILED (hr))
  295. {
  296. AssertSz (0, TEXT("Couldn't query for IPropertySetStorage"));
  297. goto Exit;
  298. }
  299. #ifndef _ABBREVIATED_DOCPROP_
  300. if (lpSIObj != NULL)
  301. {
  302. // Make sure we start with an empty object.
  303. FSumInfoClear (lpSIObj); // This will set the save flag
  304. OfficeDirtySIObj(lpSIObj, FALSE); // so clear it. Bug 1068
  305. // Load the properties into an array of PropVariants
  306. if (MSO_IO_SUCCESS != DwLoadPropSetRange (lpPropertySetStorage,
  307. &FMTID_SummaryInformation,
  308. &gdwFileCP,
  309. PID_SIFIRST,
  310. PID_SILAST,
  311. GETSINFO(lpSIObj)->rgpropvar,
  312. grfStgMode))
  313. {
  314. AssertSz (0, TEXT("Could not load SummaryInformation property set"));
  315. goto Exit;
  316. }
  317. if (GETSINFO(lpSIObj)->rgpropvar[ PVSI_THUMBNAIL ].vt == VT_CF)
  318. {
  319. GETSINFO(lpSIObj)->fSaveSINail = TRUE;
  320. #ifdef SHELL
  321. // We don't need the thumbnail, we just want to know if it exists.
  322. // So, we can free the memory.
  323. PropVariantClear( &GETSINFO(lpSIObj)->rgpropvar[ PVSI_THUMBNAIL ] );
  324. #endif
  325. }
  326. OfficeDirtySIObj (lpSIObj, FALSE);
  327. }
  328. #endif //_ABBREVIATED_DOCPROP_
  329. #ifndef _ABBREVIATED_DOCPROP_
  330. if (lpDSIObj != NULL)
  331. {
  332. // Make sure we start with an empty object.
  333. FDocSumClear (lpDSIObj);
  334. OfficeDirtyDSIObj(lpDSIObj, FALSE);
  335. if (MSO_IO_SUCCESS != DwLoadPropSetRange (lpPropertySetStorage,
  336. &FMTID_DocumentSummaryInformation,
  337. &gdwFileCP,
  338. PID_DSIFIRST,
  339. PID_DSILAST,
  340. GETDSINFO(lpDSIObj)->rgpropvar,
  341. grfStgMode))
  342. {
  343. AssertSz (0, TEXT("Could not load DocSumInfo"));
  344. goto Exit;
  345. }
  346. OfficeDirtyDSIObj (lpDSIObj, FALSE);
  347. }
  348. #endif // _ABBREVIATED_DOCPROP_
  349. if (lpUDObj != NULL)
  350. {
  351. // Make sure we start with an empty object.
  352. FUserDefClear (lpUDObj);
  353. OfficeDirtyUDObj(lpUDObj, FALSE);
  354. // Load the properties into a linked-list.
  355. if (!FLoadUserDef (lpUDObj,
  356. lpPropertySetStorage,
  357. &gdwFileCP,
  358. FALSE, // Not integers only.
  359. grfStgMode))
  360. {
  361. MESSAGE (TEXT("Could not load User-Defined properties"));
  362. goto Exit;
  363. }
  364. OfficeDirtyUDObj (lpUDObj, FALSE);
  365. }
  366. // If none of the property sets had a code-page property, set it to
  367. // the current system default.
  368. if (gdwFileCP == CP_ACP)
  369. gdwFileCP = GetACP();
  370. fSuccess = TRUE;
  371. Exit:
  372. RELEASEINTERFACE( lpPropertySetStorage );
  373. if (fSuccess)
  374. {
  375. return (MSO_IO_SUCCESS);
  376. }
  377. else
  378. {
  379. DebugHr (hr);
  380. FOfficeClearObjects (lpSIObj, lpDSIObj, lpUDObj);
  381. #ifndef _ABBREVIATED_DOCPROP_
  382. OfficeDirtySIObj (lpSIObj, FALSE);
  383. OfficeDirtyDSIObj (lpDSIObj, FALSE);
  384. #endif //_ABBREVIATED_DOCPROP_
  385. OfficeDirtyUDObj (lpUDObj, FALSE);
  386. return (MSO_IO_ERROR);
  387. }
  388. } // DwOfficeLoadProperties
  389. ////////////////////////////////////////////////////////////////////////////////
  390. //
  391. // DwOfficeLoadIntProperties
  392. //
  393. // Purpose:
  394. // Populate the objects with integer data. lpStg is the root stream.
  395. //
  396. ////////////////////////////////////////////////////////////////////////////////
  397. #ifdef UNUSED
  398. DLLFUNC DWORD OFC_CALLTYPE DwOfficeLoadIntProperties (
  399. LPSTORAGE lpStg, // Pointer to root storage
  400. LPSIOBJ lpSIObj, // Pointer to Summary Obj
  401. LPDSIOBJ lpDSIObj, // Pointer to Document Summary obj
  402. LPUDOBJ lpUDObj, // Pointer to User-defined Obj
  403. DWORD dwFlags) // Flags
  404. {
  405. DWORD dwLoad1 = MSO_IO_ERROR;
  406. DWORD dwLoad2 = MSO_IO_ERROR;
  407. if (lpStg == NULL)
  408. {
  409. return FALSE;
  410. }
  411. #ifndef _ABBREVIATED_DOCPROP_
  412. if (lpSIObj != NULL)
  413. {
  414. dwLoad1 = DwOfficeLoadSumInfo (lpSIObj, lpStg, dwFlags, TRUE);
  415. if (dwLoad1 == MSO_IO_ERROR)
  416. {
  417. return(MSO_IO_ERROR);
  418. }
  419. }
  420. #endif //_ABBREVIATED_DOCPROP_
  421. #ifndef _ABBREVIATED_DOCPROP_
  422. if (lpDSIObj != NULL && lpUDObj != NULL)
  423. {
  424. dwLoad2 = DwLoadDocAndUser (lpDSIObj, lpUDObj, lpStg, dwFlags, TRUE);
  425. if (dwLoad2 == MSO_IO_ERROR)
  426. {
  427. if (lpSIObj != NULL)
  428. {
  429. FSumInfoClear(lpSIObj);
  430. }
  431. return(MSO_IO_ERROR);
  432. }
  433. }
  434. #endif //_ABBREVIATED_DOCPROP_
  435. return(max(dwLoad1, dwLoad2));
  436. } // DwOfficeLoadIntProperties
  437. #endif
  438. ////////////////////////////////////////////////////////////////////////////////
  439. //
  440. // DwOfficeSaveProperties
  441. //
  442. // Purpose:
  443. // Write the data in the given objects. lpStg is the root stream.
  444. //
  445. ////////////////////////////////////////////////////////////////////////////////
  446. DLLFUNC DWORD OFC_CALLTYPE DwOfficeSaveProperties (
  447. LPSTORAGE lpStg, // Pointer to root storage
  448. LPSIOBJ lpSIObj, // Pointer to Summary Obj
  449. LPDSIOBJ lpDSIObj, // Pointer to Document Summary obj
  450. LPUDOBJ lpUDObj, // Pointer to User-defined Obj
  451. DWORD dwFlags, // Flags
  452. DWORD grfStgMode) // STGM flags with which to open the property set
  453. {
  454. // ------
  455. // Locals
  456. // ------
  457. HRESULT hr = E_FAIL;
  458. BOOL fSuccess = FALSE;
  459. LPPROPERTYSETSTORAGE lpPropertySetStorage = NULL;
  460. // Validate the inputs.
  461. if (lpStg == NULL)
  462. {
  463. AssertSz (0, TEXT("Invalid inputs to DwOfficeSaveProperties"));
  464. goto Exit;
  465. }
  466. // Get the IPropertySetStorage from the IStorage.
  467. hr = lpStg->lpVtbl->QueryInterface( lpStg,
  468. &IID_IPropertySetStorage,
  469. &lpPropertySetStorage );
  470. if (FAILED (hr))
  471. {
  472. AssertSz (0, TEXT("Couldn't query for IPropertySetStorage"));
  473. goto Exit;
  474. }
  475. #ifndef _ABBREVIATED_DOCPROP_
  476. // ---------------------------------------
  477. // Save the SummaryInformation properties.
  478. // ---------------------------------------
  479. if (lpSIObj != NULL)
  480. {
  481. // Only save if the user didn't specify the change only flag,
  482. // or if they did, only save if we need to.
  483. if ( !(dwFlags & OIO_SAVEIFCHANGEONLY) || FSumInfoShouldSave(lpSIObj) )
  484. {
  485. if (MSO_IO_SUCCESS != DwSavePropSetRange (
  486. lpPropertySetStorage, // The property set
  487. GetACP(), // The code page
  488. &FMTID_SummaryInformation, // The format ID
  489. PID_SIFIRST, // The first PID to use
  490. PID_SILAST, // The last PID to use
  491. GETSINFO(lpSIObj)->rgpropvar, // The properties
  492. // Skip the thumbnail if not saving
  493. GETSINFO(lpSIObj)->fSaveSINail ? 0 : PID_THUMBNAIL,
  494. grfStgMode)) // STGM flags
  495. {
  496. AssertSz (0, TEXT("Could not save SummaryInformation property set"));
  497. goto Exit;
  498. }
  499. }
  500. }
  501. #endif //_ABBREVIATED_DOCPROP_
  502. #ifndef _ABBREVIATED_DOCPROP_
  503. // -----------------------------------------------
  504. // Save the DocumentSummaryInformation properties.
  505. // -----------------------------------------------
  506. if (lpDSIObj != NULL)
  507. {
  508. if (((dwFlags & OIO_SAVEIFCHANGEONLY) && (FDocSumShouldSave (lpDSIObj))) ||
  509. !(dwFlags & OIO_SAVEIFCHANGEONLY))
  510. {
  511. if (MSO_IO_SUCCESS != DwSavePropSetRange (lpPropertySetStorage, // The property set
  512. GetACP(), // The code page
  513. // The format ID
  514. &FMTID_DocumentSummaryInformation,
  515. PID_DSIFIRST, // The first PID to use
  516. PID_DSILAST, // The last PID to use
  517. // The properties
  518. GETDSINFO(lpDSIObj)->rgpropvar,
  519. 0, // Don't skip any properties
  520. grfStgMode)) // STGM flags
  521. {
  522. AssertSz (0, TEXT("Could not save DocSumInfo"));
  523. goto Exit;
  524. }
  525. }
  526. }
  527. #endif _ABBREVIATED_DOCPROP_
  528. // ---------------------------------
  529. // Save the User-Defined properties.
  530. // ---------------------------------
  531. if (lpUDObj != NULL)
  532. {
  533. if (((dwFlags & OIO_SAVEIFCHANGEONLY) && (FUserDefShouldSave (lpUDObj))) ||
  534. !(dwFlags & OIO_SAVEIFCHANGEONLY))
  535. {
  536. if (!FSaveUserDef (lpUDObj,
  537. lpPropertySetStorage,
  538. GetACP(),
  539. grfStgMode))
  540. {
  541. AssertSz (0, TEXT("Could not save UserDefined properties"));
  542. goto Exit;
  543. }
  544. }
  545. }
  546. //
  547. // Exit
  548. //
  549. fSuccess = TRUE;
  550. Exit:
  551. RELEASEINTERFACE( lpPropertySetStorage );
  552. if (fSuccess)
  553. {
  554. #ifndef _ABBREVIATED_DOCPROP_
  555. OfficeDirtySIObj (lpSIObj, FALSE);
  556. OfficeDirtyDSIObj (lpDSIObj, FALSE);
  557. #endif //_ABBREVIATED_DOCPROP_
  558. OfficeDirtyUDObj (lpUDObj, FALSE);
  559. return (TRUE);
  560. }
  561. else
  562. {
  563. DebugHr (hr);
  564. return (FALSE);
  565. }
  566. } // DwOfficeSaveProperties
  567. ///////////////////////////////////////////////////////
  568. //
  569. // DwLoadPropSetRange
  570. //
  571. // Purpose:
  572. // Load a range of properties (specified by the first and
  573. // last property ID) from a given PropertySetStorage. All
  574. // strings are converted to the appropriate system format
  575. // (LPTSTRs).
  576. //
  577. // Inputs:
  578. // LPPROPERTYSETSTORAGE - The set of property storage objects.
  579. // REFFMTID - The Format ID of the desired property set
  580. // UINT * - A location to put the PID_CODEPAGE. This
  581. // should be initialized by the caller to a
  582. // valid default, in case the PID_CODEPAGE
  583. // does not exist.
  584. // PROPID - The first property in the range.
  585. // PROPID - The last property in the range.
  586. // PROPVARIANT[] - An array of PropVariants, large enough
  587. // for the (pidLast-pidFirst+1) properties.
  588. // DWORD - Flags from the STGM enumeration to use when
  589. // opening the property storage.
  590. //
  591. // Output:
  592. // An MSO error code.
  593. //
  594. // Note:
  595. // When strings are converted to the system format, their
  596. // VarTypes are converted too. E.g., if an ANSI VT_LPSTR is
  597. // read from a property set, the string will be converted
  598. // to Unicode, and the VarType will be changed to VT_LPWSTR.
  599. //
  600. ////////////////////////////////////////////////////////////////////////////////
  601. static DWORD PASCAL DwLoadPropSetRange (
  602. LPPROPERTYSETSTORAGE lpPropertySetStorage,
  603. REFFMTID pfmtid,
  604. UINT FAR * lpuCodePage,
  605. PROPID propidFirst,
  606. PROPID propidLast,
  607. PROPVARIANT rgpropvar[],
  608. DWORD grfStgMode)
  609. {
  610. // ------
  611. // Locals
  612. // ------
  613. DWORD dwResult = MSO_IO_ERROR; // The return code.
  614. HRESULT hr; // OLE errors
  615. ULONG ulIndex; // Index into the rgpropvar
  616. // The requested IPropertyStorage
  617. LPPROPERTYSTORAGE lpPropertyStorage;
  618. PROPSPEC FAR * rgpropspec; // The PropSpecs for the ReadMultiple
  619. PROPVARIANT propvarCodePage; // A PropVariant with which to read the PID_CODEPAGE
  620. // The total number of properties to read.
  621. ULONG cProps = propidLast - propidFirst + 1;
  622. // ----------
  623. // Initialize
  624. // ----------
  625. Assert (lpPropertySetStorage != NULL);
  626. Assert (lpPropertySetStorage->lpVtbl != NULL);
  627. Assert (propidLast >= propidFirst);
  628. lpPropertyStorage = NULL;
  629. PropVariantInit( &propvarCodePage );
  630. // Initialize the PropVariants, so that if we
  631. // early-exit, we'll return VT_EMPTY for all the properties.
  632. for (ulIndex = 0; ulIndex < cProps; ulIndex++)
  633. PropVariantInit (&rgpropvar[ulIndex]);
  634. // Allocate an array of PropSpecs.
  635. rgpropspec = PvMemAlloc ( cProps * sizeof (*rgpropspec));
  636. if (rgpropspec == NULL)
  637. {
  638. AssertSz (0, TEXT("Couldn't alloc rgpropspec"));
  639. goto Exit;
  640. }
  641. // ----------------------
  642. // Open the property set.
  643. // ----------------------
  644. hr = lpPropertySetStorage->lpVtbl->Open(
  645. lpPropertySetStorage, // this pointer
  646. pfmtid, // Identifies propset
  647. grfStgMode, // STGM_ flags
  648. &lpPropertyStorage ); // Result
  649. if (FAILED(hr))
  650. {
  651. // We couldn't open the property set.
  652. if( hr == STG_E_FILENOTFOUND )
  653. {
  654. // No problem, it just didn't exist.
  655. dwResult = MSO_IO_SUCCESS;
  656. goto Exit;
  657. }
  658. else
  659. {
  660. AssertSz (0, TEXT("Couldn't open property set"));
  661. goto Exit;
  662. }
  663. }
  664. // -------------------
  665. // Read the properties
  666. // -------------------
  667. // Initialize the local PropSpec array in preparation for a ReadMultiple.
  668. // The PROPIDs range from propidFirst to propidLast.
  669. for (ulIndex = 0; ulIndex < cProps; ulIndex++)
  670. {
  671. rgpropspec[ ulIndex ].ulKind = PRSPEC_PROPID;
  672. rgpropspec[ ulIndex ].propid = ulIndex + propidFirst;
  673. }
  674. // Read in the properties
  675. hr = lpPropertyStorage->lpVtbl->ReadMultiple (
  676. lpPropertyStorage, // 'this' pointer
  677. cProps, // count
  678. rgpropspec, // Props to read
  679. rgpropvar); // Buffers for props
  680. // Did we fail to read anything?
  681. if (hr != S_OK)
  682. {
  683. // If S_FALSE, no problem; none of the properties existed.
  684. if (hr == S_FALSE)
  685. {
  686. dwResult = MSO_IO_SUCCESS;
  687. goto Exit;
  688. }
  689. else
  690. {
  691. // Otherwise, we have a problem.
  692. AssertSz (0, TEXT("Couldn't read from property set"));
  693. goto Exit;
  694. }
  695. }
  696. // -----------------
  697. // Get the Code-Page
  698. // -----------------
  699. rgpropspec[0].ulKind = PRSPEC_PROPID;
  700. rgpropspec[0].propid = PID_CODEPAGE;
  701. hr = lpPropertyStorage->lpVtbl->ReadMultiple (
  702. lpPropertyStorage, // 'this' pointer
  703. 1, // count
  704. rgpropspec, // Props to read
  705. &propvarCodePage); // Buffer for prop
  706. // We only set the code page if we actually read it.
  707. if (hr == S_OK
  708. &&
  709. propvarCodePage.vt == VT_I2)
  710. {
  711. *lpuCodePage = propvarCodePage.iVal;
  712. }
  713. //*lpuCodePage = GetACP() ;
  714. // ---------------------------
  715. // Correct the string formats.
  716. // ---------------------------
  717. // E.g., if this is a Unicode system, convert LPSTRs to LPWSTRs.
  718. for (ulIndex = 0; ulIndex < cProps; ulIndex++)
  719. {
  720. // Is this is vector of Variants?
  721. if (rgpropvar[ ulIndex ].vt == (VT_VARIANT | VT_VECTOR))
  722. {
  723. // Loop through each element of the vector, converting
  724. // any elements which are strings.
  725. ULONG ulVectorIndex;
  726. for (ulVectorIndex = 0;
  727. ulVectorIndex < rgpropvar[ ulIndex ].capropvar.cElems;
  728. ulVectorIndex++)
  729. {
  730. if (PROPVAR_STRING_CONVERSION_REQUIRED (
  731. &rgpropvar[ulIndex].capropvar.pElems[ulVectorIndex],
  732. *lpuCodePage
  733. ))
  734. {
  735. // Convert the PropVariant string, putting it in a new
  736. // PropVariant.
  737. PROPVARIANT propvarConvert;
  738. PropVariantInit (&propvarConvert);
  739. if (!FPropVarConvertString (&propvarConvert,
  740. &rgpropvar[ulIndex].capropvar.pElems[ulVectorIndex],
  741. *lpuCodePage ))
  742. {
  743. AssertSz (0, TEXT("Couldn't convert string"));
  744. goto Exit;
  745. }
  746. // Clear the old PropVar, and copy in the new one.
  747. PropVariantClear (&rgpropvar[ulIndex].capropvar.pElems[ulVectorIndex]);
  748. rgpropvar[ulIndex].capropvar.pElems[ulVectorIndex] = propvarConvert;
  749. }
  750. } // for (ulVectorIndex = 0; ...
  751. } // if ((rgpropvar[ ulIndex ].vt == (VT_VARIANT | VT_VECTOR))
  752. // This isn't a Variant Vector, but is it a string
  753. // of some kind which requires conversion?
  754. else if (PROPVAR_STRING_CONVERSION_REQUIRED (
  755. &rgpropvar[ ulIndex ],
  756. *lpuCodePage))
  757. {
  758. // Convert the PropVariant string into a new PropVariant
  759. // buffer. The string may be a singleton, or a vector.
  760. PROPVARIANT propvarConvert;
  761. PropVariantInit (&propvarConvert);
  762. if (!FPropVarConvertString (&propvarConvert,
  763. &rgpropvar[ ulIndex ],
  764. *lpuCodePage ))
  765. {
  766. AssertSz (0, TEXT("Couldn't convert string"));
  767. goto Exit;
  768. }
  769. // Free the old PropVar and load the new one.
  770. PropVariantClear (&rgpropvar[ ulIndex ]);
  771. rgpropvar[ ulIndex ] = propvarConvert;
  772. } // else if (PROPVAR_STRING_CONVERSION_REQUIRED ( ...
  773. } // for (ulIndex = 0; ulIndex < cProps; ulIndex++)
  774. // ----
  775. // Exit
  776. // ----
  777. dwResult = MSO_IO_SUCCESS;
  778. Exit:
  779. // Release the code-page just in case somebody put the wrong type
  780. // there (like a blob).
  781. PropVariantClear (&propvarCodePage);
  782. // Release the PropSpecs and the IPropertyStorage
  783. if (rgpropspec != NULL)
  784. {
  785. VFreeMemP (rgpropspec, cProps * sizeof (*rgpropspec));
  786. }
  787. RELEASEINTERFACE (lpPropertyStorage);
  788. // If we failed, free the PropVariants.
  789. if (dwResult != MSO_IO_SUCCESS)
  790. {
  791. FreePropVariantArray( cProps, rgpropvar );
  792. DebugHr( hr );
  793. }
  794. return (dwResult);
  795. } // DwLoadPropSetRange
  796. ////////////////////////////////////////////////////////////////////////////////
  797. //
  798. // Wrap of IPropertySetStorage::Create
  799. //
  800. // Each new ANSI property set created by docprop must set PID_CODEPAGE to CP_UTF8
  801. // to avoid ansi<->unicode roundtripping issues.
  802. //
  803. HRESULT _CreatePropertyStorage(
  804. LPPROPERTYSETSTORAGE psetstg,
  805. REFFMTID rfmtid,
  806. CLSID* pclsid,
  807. DWORD grfMode,
  808. UINT* /*IN OUT*/ puCodePage,
  809. IPropertyStorage** ppstg )
  810. {
  811. DWORD grfFlags = (CP_WINUNICODE == (*puCodePage)) ?
  812. PROPSETFLAG_DEFAULT : PROPSETFLAG_ANSI;
  813. HRESULT hr = psetstg->lpVtbl->Create( psetstg, rfmtid, pclsid, grfFlags, grfMode, ppstg );
  814. if( SUCCEEDED( hr ) )
  815. {
  816. if( PROPSETFLAG_ANSI == grfFlags )
  817. {
  818. PROPSPEC propspec = { PRSPEC_PROPID, PID_CODEPAGE };
  819. PROPVARIANT varCP;
  820. varCP.vt = VT_I2;
  821. varCP.iVal = (SHORT)CP_UTF8;
  822. if( SUCCEEDED( (*ppstg)->lpVtbl->WriteMultiple( *ppstg, 1, &propspec, &varCP, PID_UDFIRST ) ) )
  823. *puCodePage = (UINT)MAKELONG(varCP.iVal, 0);
  824. }
  825. }
  826. return hr;
  827. }
  828. ///////////////////////////////////////////////////////
  829. //
  830. // DwSavePropSetRange
  831. //
  832. // Purpose:
  833. // Save a range of properties to a Property Set Storage.
  834. // The properties to be saved are provided in an
  835. // array of PropVariants, and their property IDs are
  836. // specified by the first and last PID for the range.
  837. // The caller may also specify that a property be
  838. // "skipped", i.e., not written.
  839. //
  840. // Inputs:
  841. // LPPROPERTYSETSTORAGE - The Property Set Storage
  842. // UINT - The code page with which the strings
  843. // should be written.
  844. // REFFMTID - The GUID identifying the Property Storage
  845. // within the Property Set Storage.
  846. // PROPID - The PID to assign to the first property.
  847. // PROPID - The PID to assign to the last property
  848. // PROPVARIANT [] - The propeties to write. All strings
  849. // are assumed to be in the system format
  850. // (e.g. VT_LPWSTRs for NT). This array
  851. // is returned un-modified to the caller.
  852. // PROPID - If non-zero, identifies a property
  853. // which should not be written, even if
  854. // it is non-empty. If the property exists
  855. // in the property set, it will be deleted.
  856. // (This was added to provide a way to skip
  857. // the PID_THUMBNAIL.)
  858. // DWORD - Flags from the STGM enumeration to use when
  859. // opening the property storage.
  860. //
  861. // Output:
  862. // An MSO error code.
  863. //
  864. // Notes:
  865. // - If the code page is Unicode, all strings are written as LPWSTRs,
  866. // otherwise, they are written as LPSTRs.
  867. // - Only non-empty properties are written.
  868. //
  869. // Implementation:
  870. // This routine creates a new PropVariant array which is the
  871. // subset of the caller's PropVariant array which must actually
  872. // be written (i.e, it doesn't include the VT_EMPTY properties
  873. // or the 'propidSkip').
  874. //
  875. // We allocate as little extra memory as possible. For example,
  876. // if we have to write a string, we'll copy the pointer to the
  877. // string into the subset PropVariant array. Thus we'll have
  878. // two pointers to the string.
  879. //
  880. // If a string to be written must be converted first (to another
  881. // code-page), then the original PropVariant array will continue
  882. // pointing to the original string, and the subset PropVariant
  883. // array will point to the converted string (and must consequently
  884. // be freed).
  885. //
  886. ////////////////////////////////////////////////////////////////////////////////
  887. static DWORD PASCAL DwSavePropSetRange (
  888. LPPROPERTYSETSTORAGE lpPropertySetStorage,
  889. UINT uCodePage,
  890. REFFMTID pfmtid,
  891. PROPID propidFirst,
  892. PROPID propidLast,
  893. PROPVARIANT rgpropvarOriginal[],
  894. PROPID propidSkip,
  895. DWORD grfStgMode)
  896. {
  897. // ------
  898. // Locals
  899. // ------
  900. DWORD dwResult = MSO_IO_ERROR; // The functions return code.
  901. HRESULT hr; // OLE results.
  902. // The Property Storage to write to
  903. LPPROPERTYSTORAGE lpPropertyStorage = NULL;
  904. ULONG cOriginal; // The size of rgpropvarOriginal,
  905. ULONG cNew; // and the number which must actually be written.
  906. ULONG ulIndex; // Index into rgpropvarOriginal
  907. PROPSPEC FAR * rgpropspecNew = NULL;// PropSpecs for the WriteMultiple
  908. LPPROPVARIANT rgpropvarNew = NULL; // The sub-set of rgpropvarOrigianl we must write.
  909. // The following array has an entry for each entry in rgpropvarNew.
  910. // Each entry identifies the corresponding entry in rgpropvarOriginal.
  911. // E.g. rgMapNewToOriginal[0] is the index in rgpropvarOriginal of
  912. // the first property to be written.
  913. ULONG *rgMapNewToOriginal = NULL;
  914. // ----------
  915. // Initialize
  916. // ----------
  917. cOriginal = propidLast - propidFirst + 1;
  918. cNew = 0;
  919. Assert (cOriginal <= max(NUM_SI_PROPERTIES, NUM_DSI_PROPERTIES));
  920. Assert (lpPropertySetStorage != NULL);
  921. Assert (lpPropertySetStorage->lpVtbl != NULL);
  922. Assert (propidLast >= propidFirst);
  923. Assert (rgpropvarOriginal != NULL);
  924. // Allocate an array of PropSpecs for the WriteMultiple.
  925. rgpropspecNew = PvMemAlloc ( cOriginal * sizeof (*rgpropspecNew));
  926. if (rgpropspecNew == NULL)
  927. {
  928. AssertSz (0, TEXT("Couldn't alloc rgpropspecNew"));
  929. goto Exit;
  930. }
  931. // Allocate an array of PropVariants which will hold the subset
  932. // of the caller's properties which must be written.
  933. // Initialize to zeros so that we don't think we have memory
  934. // to free in the error path.
  935. rgpropvarNew = PvMemAlloc ( cOriginal * sizeof (*rgpropvarNew));
  936. if (rgpropvarNew == NULL)
  937. {
  938. AssertSz (0, TEXT("Couldn't alloc rgpropvarNew"));
  939. goto Exit;
  940. }
  941. FillBuf (rgpropvarNew, 0, cOriginal * sizeof (*rgpropvarNew));
  942. // Allocate the look-up-table which maps entries in rgpropvarNew
  943. // to rgpropvarOriginal
  944. rgMapNewToOriginal = PvMemAlloc (cOriginal * sizeof(*rgMapNewToOriginal));
  945. if (rgMapNewToOriginal == NULL)
  946. {
  947. AssertSz (0, TEXT("Couldn't alloc rgMapNewToOriginal"));
  948. goto Exit;
  949. }
  950. // -------------------------
  951. // Open the Property Storage
  952. // -------------------------
  953. hr = lpPropertySetStorage->lpVtbl->Open(
  954. lpPropertySetStorage, // this pointer
  955. pfmtid,
  956. grfStgMode,
  957. &lpPropertyStorage );
  958. // If it didn't exist, create it.
  959. if( hr == STG_E_FILENOTFOUND )
  960. {
  961. hr = _CreatePropertyStorage( lpPropertySetStorage,
  962. pfmtid,
  963. NULL,
  964. STGM_DIRECT | STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
  965. &uCodePage,
  966. &lpPropertyStorage );
  967. }
  968. // Check the result of the open/create.
  969. if (FAILED(hr))
  970. {
  971. AssertSz (0, TEXT("Couldn't open property set"));
  972. goto Exit;
  973. }
  974. // ---------------------------------------------------
  975. // Copy the properties to be written into rgpropvarNew
  976. // ---------------------------------------------------
  977. // Loop through all the properties in rgpropvarOriginal
  978. for (ulIndex = 0; ulIndex < cOriginal; ulIndex++)
  979. {
  980. // Is this property extant and not the one to skip?
  981. if (rgpropvarOriginal[ ulIndex ].vt != VT_EMPTY
  982. &&
  983. ( propidSkip == 0
  984. ||
  985. propidSkip != propidFirst + ulIndex )
  986. )
  987. {
  988. // We have a property which must be written.
  989. BOOL fVector;
  990. VARTYPE vt;
  991. // Record a mapping from the new index to the original.
  992. rgMapNewToOriginal[ cNew ] = ulIndex;
  993. // Add an entry to the PropSpec array.
  994. rgpropspecNew[ cNew ].ulKind = PRSPEC_PROPID;
  995. rgpropspecNew[ cNew ].propid = propidFirst + ulIndex;
  996. // Get the underlying VarType.
  997. fVector = (rgpropvarOriginal[ ulIndex ].vt & VT_VECTOR) ? TRUE : FALSE;
  998. vt = rgpropvarOriginal[ ulIndex ].vt & ~VT_VECTOR;
  999. // If this property is a vector of variants, some of those
  1000. // elements may be strings which need to be converted.
  1001. if ((vt == VT_VARIANT) && fVector)
  1002. {
  1003. ULONG ulVectorIndex;
  1004. // We'll inintialize the capropvar.pElems in rgpropvarNew
  1005. // so that it points to the one in rgpropvarOriginal. We'll
  1006. // only allocate if a conversion is necessary. I.e., we handle
  1007. // pElems as a copy-on-write.
  1008. rgpropvarNew[ cNew ] = rgpropvarOriginal[ ulIndex ];
  1009. // Loop through the elements of the vector.
  1010. for (ulVectorIndex = 0;
  1011. ulVectorIndex < rgpropvarNew[ cNew ].capropvar.cElems;
  1012. ulVectorIndex++)
  1013. {
  1014. // Is this a string requiring a code-page conversion?
  1015. if (PROPVAR_STRING_CONVERSION_REQUIRED(
  1016. &rgpropvarOriginal[ulIndex].capropvar.pElems[ulVectorIndex],
  1017. uCodePage ))
  1018. {
  1019. // We must convert this string. Have we allocated a pElems yet?
  1020. if (rgpropvarNew[cNew].capropvar.pElems
  1021. == rgpropvarOriginal[ulIndex].capropvar.pElems)
  1022. {
  1023. // Allocate a new pElems for rgpropvarNew
  1024. rgpropvarNew[cNew].capropvar.pElems
  1025. = CoTaskMemAlloc (rgpropvarNew[cNew].capropvar.cElems
  1026. * sizeof(*rgpropvarNew[cNew].capropvar.pElems));
  1027. if (rgpropvarNew[cNew].capropvar.pElems == NULL)
  1028. {
  1029. AssertSz (0, TEXT("Couldn't allocate pElems"));
  1030. goto Exit;
  1031. }
  1032. // Initialize it to match that in rgpropvarOriginal
  1033. PbMemCopy (rgpropvarNew[cNew].capropvar.pElems,
  1034. rgpropvarOriginal[ulIndex].capropvar.pElems,
  1035. rgpropvarNew[cNew].capropvar.cElems
  1036. * sizeof(*rgpropvarNew[cNew].capropvar.pElems));
  1037. }
  1038. // Now, we can convert this string from rgpropvarOriginal into
  1039. // rgpropvarNew.
  1040. PropVariantInit (&rgpropvarNew[cNew].capropvar.pElems[ulVectorIndex]);
  1041. if (!FPropVarConvertString(&rgpropvarNew[cNew].capropvar.pElems[ulVectorIndex],
  1042. &rgpropvarOriginal[ulIndex].capropvar.pElems[ulVectorIndex],
  1043. uCodePage))
  1044. {
  1045. AssertSz(0, TEXT("Couldn't convert code page of string"));
  1046. goto Exit;
  1047. }
  1048. } // if (PROPVAR_STRING_CONVERSION_REQUIRED( ...
  1049. } // for (ulVectorIndex = 0; ...
  1050. } // if (vt == VT_VARIANT && fVector)
  1051. // This isn't a variant vector, but is it some type of string
  1052. // property for which we must make a conversion?
  1053. else if (PROPVAR_STRING_CONVERSION_REQUIRED (
  1054. &rgpropvarOriginal[ ulIndex ],
  1055. uCodePage))
  1056. {
  1057. PropVariantInit (&rgpropvarNew[cNew]);
  1058. if (!FPropVarConvertString (&rgpropvarNew[cNew],
  1059. &rgpropvarOriginal[ulIndex],
  1060. uCodePage))
  1061. {
  1062. AssertSz (0, TEXT("Couldn't convert string"));
  1063. goto Exit;
  1064. }
  1065. } // else if (PROPVAR_STRING_CONVERSION_REQUIRED ( ...
  1066. // If neither of the above special-cases were triggered,
  1067. // then simply copy the PropVariant structure (but not
  1068. // any referred-to data). We save memory by not duplicating
  1069. // the referred-to data, but we must be careful in the exit
  1070. // not to free it.
  1071. else
  1072. {
  1073. rgpropvarNew[cNew] = rgpropvarOriginal[ulIndex];
  1074. } // if ((vt == VT_VARIANT) && fVector) ... else
  1075. // We're done copying/converting this property from rgpropvarOriginal
  1076. // into rgpropvarNew.
  1077. cNew++;
  1078. } // if (rgpropvarOriginal[ ulIndex ].vt != VT_EMPTY ...
  1079. } // for (ulIndex = 0; ulIndex < cProps; ulIndex++)
  1080. // ------------------------
  1081. // Write out the properties
  1082. // ------------------------
  1083. // Write out properties if we found any.
  1084. if (cNew > 0)
  1085. {
  1086. hr = lpPropertyStorage->lpVtbl->WriteMultiple (
  1087. lpPropertyStorage, // 'this' pointer
  1088. cNew, // Count
  1089. rgpropspecNew, // Props to write
  1090. rgpropvarNew, // The props
  1091. PID_UDFIRST);
  1092. if (FAILED(hr))
  1093. {
  1094. AssertSz (0, TEXT("Couldn't write properties"));
  1095. goto Exit;
  1096. }
  1097. } // if (cNew > 0)
  1098. // ---------------------
  1099. // Delete the propidSkip
  1100. // ---------------------
  1101. // If the caller specified a PID to skip, then it should
  1102. // be deleted from the property set as well.
  1103. if (propidSkip != 0)
  1104. {
  1105. rgpropspecNew[0].ulKind = PRSPEC_PROPID;
  1106. rgpropspecNew[0].propid = propidSkip;
  1107. hr = lpPropertyStorage->lpVtbl->DeleteMultiple (
  1108. lpPropertyStorage, // this pointer
  1109. 1, // Delete one property
  1110. rgpropspecNew ); // The prop to delete
  1111. if (FAILED(hr))
  1112. {
  1113. AssertSz (0, TEXT("Couldn't delete the propidSkip"));
  1114. goto Exit;
  1115. }
  1116. }
  1117. // ----
  1118. // Exit
  1119. // ----
  1120. dwResult = MSO_IO_SUCCESS;
  1121. Exit:
  1122. // Clear any of the properties in rgpropvarNew for which new
  1123. // buffers were allocated. Then free the rgpropvarNew array itself.
  1124. // We know that buffers were allocated for rgpropvarNew if it's contents
  1125. // don't match rgpropvarOriginal.
  1126. if (rgpropvarNew != NULL)
  1127. {
  1128. // Loop through rgpropvarNew
  1129. for (ulIndex = 0; ulIndex < cNew; ulIndex++)
  1130. {
  1131. // Was memory allocated for this rgpropvarNew?
  1132. if (memcmp (&rgpropvarNew[ ulIndex ],
  1133. &rgpropvarOriginal[ rgMapNewToOriginal[ulIndex] ],
  1134. sizeof(rgpropvarNew[ ulIndex ])))
  1135. {
  1136. // Is this a variant vector?
  1137. if (rgpropvarNew[ulIndex].vt == (VT_VECTOR | VT_VARIANT))
  1138. {
  1139. ULONG ulVectIndex;
  1140. // Loop through the variant vector and free any PropVariants
  1141. // that were allocated. We follow the same principle, if the
  1142. // entry in rgpropvarNew doesn't match the entry in
  1143. // rgpropvarOriginal, we must have allocated new memory.
  1144. for (ulVectIndex = 0;
  1145. ulVectIndex < rgpropvarNew[ulIndex].capropvar.cElems;
  1146. ulVectIndex++)
  1147. {
  1148. if (memcmp(&rgpropvarNew[ ulIndex ].capropvar.pElems[ ulVectIndex ],
  1149. &rgpropvarOriginal[ rgMapNewToOriginal[ulIndex] ].capropvar.pElems[ ulVectIndex ],
  1150. sizeof(rgpropvarNew[ ulIndex ].capropvar.pElems[ ulVectIndex ])))
  1151. {
  1152. PropVariantClear (&rgpropvarNew[ulIndex].capropvar.pElems[ulVectIndex]);
  1153. }
  1154. }
  1155. // Unconditionally free the pElems buffer.
  1156. CoTaskMemFree (rgpropvarNew[ulIndex].capropvar.pElems);
  1157. } // if (rgpropvarNew[ulIndex].vt == (VT_VECTOR | VT_VARIANT))
  1158. // This isn't a variant vector
  1159. else
  1160. {
  1161. // But does the rgpropvarNew have private memory (i.e.
  1162. // a converted string buffer)?
  1163. if (memcmp (&rgpropvarNew[ ulIndex ],
  1164. &rgpropvarOriginal[ rgMapNewToOriginal[ulIndex] ],
  1165. sizeof(rgpropvarNew[ ulIndex ])))
  1166. {
  1167. PropVariantClear (&rgpropvarNew[ulIndex]);
  1168. }
  1169. } // if (rgpropvarNew[ulIndex].vt == (VT_VECTOR | VT_VARIANT)) ... else
  1170. } // if (rgpropvarNew[ulIndex] ...
  1171. } // for (ulIndex = 0; ulIndex < cNew; ulIndex++)
  1172. // Free the rgpropvarNew array itself.
  1173. VFreeMemP (rgpropvarNew, cOriginal * sizeof (*rgpropvarNew));
  1174. } // if (rgpropvarNew != NULL)
  1175. // Free the remaining arrays and release the Property Storage interface.
  1176. if (rgpropspecNew != NULL)
  1177. {
  1178. VFreeMemP (rgpropspecNew, cOriginal * sizeof (*rgpropspecNew));
  1179. }
  1180. if (rgMapNewToOriginal != NULL)
  1181. {
  1182. VFreeMemP (rgMapNewToOriginal, cOriginal * sizeof(*rgMapNewToOriginal));
  1183. }
  1184. RELEASEINTERFACE (lpPropertyStorage);
  1185. // And we're done.
  1186. return (dwResult);
  1187. } // DwSavePropSetRange
  1188. ////////////////////////////////////////////////////////////////////////////////
  1189. //
  1190. // FLoadUserDef
  1191. //
  1192. // Purpose:
  1193. // Load the User-Defined properties (those in the second section of
  1194. // the DocumentSummaryInformation property set). There can be any number
  1195. // of these properties, and the user specifies they're name, value, and
  1196. // type (from a limited subset of the VarTypes). Since this is
  1197. // variable-sized, the properties are loaded into a linked-list.
  1198. //
  1199. // Inputs:
  1200. // LPUDOBJ - All User-Defined data (including the properties).
  1201. // Its m_lpData must point to a valid UDINFO structure.
  1202. // LPPROPERTYSETSTORAGE - The Property Set Storage in which we'll find the
  1203. // UD property storage.
  1204. // UINT* - The PID_CODEPAGE, if it exists. Left unmodified
  1205. // if it doesn't exist. All string properties will
  1206. // converted to this format. This must be intialized
  1207. // by the caller to a valid default.
  1208. // BOOL - Only load integer values.
  1209. // DWORD - Flags from the STGM enumeration to use when opening
  1210. // the property storage.
  1211. //
  1212. ////////////////////////////////////////////////////////////////////////////////
  1213. static BOOL PASCAL FLoadUserDef (
  1214. LPUDOBJ lpUDObj,
  1215. LPPROPERTYSETSTORAGE lpPropertySetStorage,
  1216. UINT *puCodePage,
  1217. BOOL fIntOnly, // Load Int Properties only?
  1218. DWORD grfStgMode)
  1219. {
  1220. // ------
  1221. // Locals
  1222. // ------
  1223. BOOL fSuccess = FALSE; // Return code to the caller.
  1224. HRESULT hr; // Error codes for OLE calls.
  1225. LPPROPERTYSTORAGE lpPropertyStorage = NULL; // The UD property storage
  1226. LPENUMSTATPROPSTG lpEnum = NULL; // Enumerates the UD property storage
  1227. STATPROPSETSTG statpropsetstg; // Holds the ClassID from the property storage
  1228. // Used in ReadMultiple call.
  1229. PROPSPEC rgpropspec[ DEFAULT_IPROPERTY_COUNT ];
  1230. // A subset of the UD properties
  1231. PROPVARIANT rgpropvar[ DEFAULT_IPROPERTY_COUNT ];
  1232. // Stats on a subset of the UD properties
  1233. STATPROPSTG rgstatpropstg[ DEFAULT_IPROPERTY_COUNT ];
  1234. ULONG ulIndex; // Index into the above arrays.
  1235. PROPSPEC propspec; // PropSpec for reading the code-page
  1236. LPUDPROP lpudprop = NULL; // A single UD property (points to the PropVariant)
  1237. ULONG cEnumerated = 0; // Number of properties found in an enumeration
  1238. // --------------
  1239. // Initialization
  1240. // --------------
  1241. Assert (!fIntOnly); // No longer used.
  1242. Assert (lpUDObj != NULL && GETUDINFO(lpUDObj) != NULL);
  1243. Assert (puCodePage != NULL);
  1244. // We need to zero-out the PropVariant and StatPropStg
  1245. // arrays so that we don't think they need to be freed
  1246. // in the Exit block.
  1247. FillBuf (rgpropvar, 0, sizeof (rgpropvar));
  1248. FillBuf (rgstatpropstg, 0, sizeof (rgstatpropstg));
  1249. // -----------------------------------------
  1250. // Get the PropertyStorage and an Enumerator
  1251. // -----------------------------------------
  1252. // Open the IPropertyStorage and check for errors.
  1253. hr = lpPropertySetStorage->lpVtbl->Open(
  1254. lpPropertySetStorage, // this pointer
  1255. &FMTID_UserDefinedProperties,
  1256. grfStgMode,
  1257. &lpPropertyStorage );
  1258. if (FAILED(hr))
  1259. {
  1260. // We couldn't open the property set.
  1261. if( hr == STG_E_FILENOTFOUND )
  1262. {
  1263. // No problem, it just didn't exist.
  1264. fSuccess = TRUE;
  1265. goto Exit;
  1266. }
  1267. else
  1268. {
  1269. AssertSz (0, TEXT("Couldn't open property set"));
  1270. goto Exit;
  1271. }
  1272. }
  1273. // Save the property storage's class ID (identifying the application
  1274. // which is primarily responsible for it). We do this because
  1275. // we may later delete the existing property set.
  1276. hr = lpPropertyStorage->lpVtbl->Stat (lpPropertyStorage, &statpropsetstg);
  1277. if (FAILED(hr))
  1278. {
  1279. AssertSz (0, TEXT("Couldn't Stat the Property Storage"));
  1280. goto Exit;
  1281. }
  1282. GETUDINFO(lpUDObj)->clsid = statpropsetstg.clsid;
  1283. // Get the IEnum interface and check for errors.
  1284. hr = lpPropertyStorage->lpVtbl->Enum(
  1285. lpPropertyStorage,
  1286. &lpEnum );
  1287. if (FAILED(hr))
  1288. {
  1289. AssertSz (0, TEXT("Couldn't enumerate the PropertyStorage"));
  1290. goto Exit;
  1291. }
  1292. // ------------------
  1293. // Read the Code Page
  1294. // ------------------
  1295. propspec.ulKind = PRSPEC_PROPID;
  1296. propspec.propid = PID_CODEPAGE;
  1297. hr = lpPropertyStorage->lpVtbl->ReadMultiple (lpPropertyStorage, 1, &propspec, &rgpropvar[0]);
  1298. if (FAILED(hr))
  1299. {
  1300. AssertSz (0, TEXT("Couldn't get property set"));
  1301. }
  1302. // If this is a valid PID_CODEPAGE, give it to the caller.
  1303. if (hr == S_OK && rgpropvar[0].vt == VT_I2)
  1304. {
  1305. *puCodePage = (UINT)MAKELONG(rgpropvar[0].iVal, 0);
  1306. }
  1307. PropVariantClear (&rgpropvar[0]);
  1308. // -------------------------------------------------------------
  1309. // Loop through the properties and add to the UDPROPS structure.
  1310. // -------------------------------------------------------------
  1311. // This loop executes once for each enumeration. Each enumeration
  1312. // gets multiple STATPROPSTGs, so within this loop an inner loop
  1313. // will process each property. This two-level looping mechanism is
  1314. // used in order to reduce the number of ReadMultiples.
  1315. // Use the IEnum to load the first set of STATPROPSTGs.
  1316. hr = lpEnum->lpVtbl->Next (lpEnum, DEFAULT_IPROPERTY_COUNT, rgstatpropstg, &cEnumerated);
  1317. if (FAILED(hr))
  1318. {
  1319. AssertSz (0, TEXT("Couldn't get next StatPropStg"));
  1320. goto Exit;
  1321. }
  1322. Assert (cEnumerated <= DEFAULT_IPROPERTY_COUNT);
  1323. // If the last IEnum returned properties, process them here.
  1324. // At the end of this while loop, we re-call the IEnum, thus continuing
  1325. // until no properties are left to be enumerated.
  1326. while (cEnumerated)
  1327. {
  1328. // ------------------------------
  1329. // Read this batch of properties.
  1330. // ------------------------------
  1331. for (ulIndex = 0; ulIndex < cEnumerated; ulIndex++)
  1332. {
  1333. rgpropspec[ ulIndex ].ulKind = PRSPEC_PROPID;
  1334. rgpropspec[ ulIndex ].propid = rgstatpropstg[ ulIndex ].propid;
  1335. }
  1336. // Read the properties.
  1337. hr = lpPropertyStorage->lpVtbl->ReadMultiple(
  1338. lpPropertyStorage,
  1339. cEnumerated,
  1340. rgpropspec,
  1341. rgpropvar );
  1342. if (FAILED(hr))
  1343. {
  1344. AssertSz (0, TEXT("Couldn't read from property set"));
  1345. goto Exit;
  1346. }
  1347. // ------------------------------------------------------
  1348. // Loop through the properties, adding them to the UDOBJ.
  1349. // ------------------------------------------------------
  1350. for (ulIndex = 0; ulIndex < cEnumerated; ulIndex++)
  1351. {
  1352. // Convert string PropVariants to the right code page.
  1353. // We won't worry about Variants which are strings, because
  1354. // this is not a legal type for the UD properties.
  1355. if (PROPVAR_STRING_CONVERSION_REQUIRED (
  1356. &rgpropvar[ ulIndex ],
  1357. *puCodePage))
  1358. {
  1359. // Convert the string in the PropVariant, putting the
  1360. // result in a temporary PropVariant.
  1361. PROPVARIANT propvarConvert;
  1362. PropVariantInit (&propvarConvert);
  1363. if (!FPropVarConvertString (&propvarConvert,
  1364. &rgpropvar[ulIndex],
  1365. *puCodePage))
  1366. {
  1367. AssertSz (0, TEXT("Couldn't convert string"));
  1368. goto Exit;
  1369. }
  1370. // Free the old PropVariant, and load in the converted
  1371. // one.
  1372. PropVariantClear (&rgpropvar[ ulIndex ]);
  1373. rgpropvar[ ulIndex ] = propvarConvert;
  1374. }
  1375. #ifndef UNICODE
  1376. // Convert the property name to the right code page.
  1377. if (rgstatpropstg[ ulIndex ].lpwstrName != NULL)
  1378. {
  1379. LPSTR lpsz;
  1380. if (!FCoWStrToStr (&lpsz, rgstatpropstg[ ulIndex ].lpwstrName,
  1381. *puCodePage))
  1382. {
  1383. goto Exit;
  1384. }
  1385. CoTaskMemFree (rgstatpropstg[ ulIndex ].lpwstrName);
  1386. // [scotthan] HACK ALERT: watch this wacky cast;
  1387. // it'll bite you if you don't reciprocate when reading
  1388. // the string back out! (see userdef.c, FAddPropToList() impl).
  1389. rgstatpropstg[ ulIndex ].lpwstrName =(LPWSTR)lpsz;
  1390. }
  1391. #endif
  1392. // Allocate a new UDPROP structure, which will be added to the
  1393. // linked-list.
  1394. lpudprop = LpudpropCreate();
  1395. if (lpudprop == NULL)
  1396. {
  1397. goto Exit;
  1398. }
  1399. // Add this UDPROP to the linked-list. On success, this will assume
  1400. // responsibility for the PropVariant and STATPROPSTG buffers, and
  1401. // will NULL out our pointers accordingly.
  1402. if (!FAddPropToList (lpUDObj,
  1403. &rgpropvar[ ulIndex ],
  1404. &rgstatpropstg[ ulIndex ],
  1405. lpudprop))
  1406. {
  1407. goto Exit;
  1408. }
  1409. lpudprop = NULL;
  1410. } // for (ulIndex = 0; ulIndex < cEnumerated; ulIndex++)
  1411. // ---------------------
  1412. // Get a new enumeration
  1413. // ---------------------
  1414. // We've processed all the properties in the last enumeration, let's get
  1415. // a new set (if there are any). If there are no more, cEnumerated, will be
  1416. // zero, and we'll break out of the outer while loop.
  1417. FreePropVariantArray( cEnumerated, rgpropvar );
  1418. hr = lpEnum->lpVtbl->Next (lpEnum, DEFAULT_IPROPERTY_COUNT, rgstatpropstg, &cEnumerated);
  1419. if (FAILED(hr))
  1420. {
  1421. AssertSz (0, TEXT("Couldn't get next StatPropStg"));
  1422. goto Exit;
  1423. }
  1424. } // while (cEnumerated)
  1425. // ----
  1426. // Exit
  1427. // ----
  1428. fSuccess = TRUE;
  1429. Exit:
  1430. // Free any properties with buffers. This will only happen
  1431. // if there was an error.
  1432. if (cEnumerated > 0)
  1433. {
  1434. FreePropVariantArray (cEnumerated, rgpropvar);
  1435. }
  1436. // Again if there was an error, we must free the UDPROP object.
  1437. if (lpudprop)
  1438. {
  1439. VUdpropFree (&lpudprop);
  1440. }
  1441. // Free any name buffers we still have from the enumerations.
  1442. // Once again, this is only necessary if there was an error.
  1443. for (ulIndex = 0; ulIndex < cEnumerated; ulIndex++)
  1444. {
  1445. if (rgstatpropstg[ ulIndex ].lpwstrName != NULL)
  1446. {
  1447. CoTaskMemFree (rgstatpropstg[ ulIndex ].lpwstrName);
  1448. }
  1449. }
  1450. // Release the Property Storage and Enumeration interfaces.
  1451. RELEASEINTERFACE (lpEnum);
  1452. RELEASEINTERFACE (lpPropertyStorage);
  1453. return fSuccess;
  1454. } // FLoadUserDef
  1455. ////////////////////////////////////////////////////////////////////////////////
  1456. //
  1457. // FSaveUserDef
  1458. //
  1459. // Purpose:
  1460. // Save the User-Defined properties to the second section of
  1461. // the DocumentSummaryInformation property set.
  1462. //
  1463. // Inputs:
  1464. // LPUDOBJ - All UD data (including the properties)
  1465. // It's m_lpData must point to a valid UDINFO structure.
  1466. // LPPROPERTYSETSTORAGE - The Property Set Storage
  1467. // UINT - The code page in which strings should be
  1468. // written. If Unicode, all strings are
  1469. // written as LPWSTRs, otherwise all strings
  1470. // are written as LPSTRs.
  1471. // DWORD - Flags from the STGM enumeration to use when
  1472. // opening the property storage.
  1473. //
  1474. // Outputs:
  1475. // TRUE if successful.
  1476. //
  1477. // Pre-conditions:
  1478. // The properties to be written are all from the UDTYPES
  1479. // enumeration.
  1480. //
  1481. // Implementation:
  1482. // Properties which are links to application data require special
  1483. // handling. First, the property value is written (along with its
  1484. // name). Then, the application-defined link name is
  1485. // written (e.g. the Bookmark name in Word). The link name
  1486. // is written using the same PID as was the link value, except that
  1487. // the PID_LINKMASK is ORed in. The link name property has no name
  1488. // in the property set dictionary.
  1489. //
  1490. ////////////////////////////////////////////////////////////////////////////////
  1491. static
  1492. BOOL PASCAL FSaveUserDef (
  1493. LPUDOBJ lpUDObj,
  1494. LPPROPERTYSETSTORAGE lpPropertySetStorage,
  1495. UINT uCodePage,
  1496. DWORD grfStgMode)
  1497. {
  1498. // ------
  1499. // Locals
  1500. // ------
  1501. BOOL fSuccess = FALSE; // What to return to the caller.
  1502. HRESULT hr; // OLE result codes.
  1503. BOOL fLink, fLinkInvalid;
  1504. // The UD Property Storage
  1505. LPPROPERTYSTORAGE lpPropertyStorage = NULL;
  1506. LPUDITER lpudi = NULL; // Iterates the linked-list of UDPROPs
  1507. LPPROPVARIANT lppropvar = NULL; // A property from the linked-list
  1508. ULONG ulIndex; // Generic index into arrays
  1509. PROPID propid; // The PID to assign to the next property
  1510. // Arrays to be used in the WriteMultiple. The array of BOOLs
  1511. // indicate which elements of the PropVariant array must be freed.
  1512. ULONG ulPropIndex = 0;
  1513. PROPSPEC rgpropspec[ DEFAULT_IPROPERTY_COUNT ];
  1514. PROPVARIANT rgpropvar[ DEFAULT_IPROPERTY_COUNT ];
  1515. BOOL rgfFreePropVar[ DEFAULT_IPROPERTY_COUNT ];
  1516. // Arrays to be used in the WritePropertyNames.
  1517. ULONG ulNameIndex = 0;
  1518. PROPID rgpropidName[ DEFAULT_IPROPERTY_COUNT ];
  1519. LPWSTR rglpwstrName[ DEFAULT_IPROPERTY_COUNT ];
  1520. // ----------
  1521. // Initialize
  1522. // ----------
  1523. Assert (lpUDObj != NULL && GETUDINFO(lpUDObj) != NULL);
  1524. Assert (lpPropertySetStorage != NULL && lpPropertySetStorage->lpVtbl != NULL);
  1525. // Initialize the necessary arrays, so that we don't unnecessarily
  1526. // free something in the Error path.
  1527. FillBuf (rgpropvar, 0, sizeof(rgpropvar));
  1528. FillBuf (rgfFreePropVar, 0, sizeof(rgfFreePropVar));
  1529. FillBuf (rglpwstrName, 0, sizeof(rglpwstrName));
  1530. // Delete the existing property set and create a new empty one.
  1531. // We must do this because we don't know which of the
  1532. // existing properties need to be deleted, we only know what
  1533. // the current set of properties should be.
  1534. hr = lpPropertySetStorage->lpVtbl->Delete(
  1535. lpPropertySetStorage,
  1536. &FMTID_UserDefinedProperties );
  1537. if (FAILED(hr))
  1538. {
  1539. if (hr != STG_E_FILENOTFOUND)
  1540. {
  1541. AssertSz (0, TEXT("Couldn't remove old properties"));
  1542. goto Exit;
  1543. }
  1544. }
  1545. hr = _CreatePropertyStorage( lpPropertySetStorage,
  1546. &FMTID_UserDefinedProperties,
  1547. &GETUDINFO(lpUDObj)->clsid,
  1548. grfStgMode,
  1549. &uCodePage,
  1550. &lpPropertyStorage );
  1551. if (FAILED(hr))
  1552. {
  1553. AssertSz (0, TEXT("Couldn't open User-Defined property set"));
  1554. goto Exit;
  1555. }
  1556. // Create an iterator which we use to enumerate the properties
  1557. // (UDPROPs) in the linked-list.
  1558. lpudi = LpudiUserDefCreateIterator (lpUDObj);
  1559. // ------------------------------------------------------------------
  1560. // Loop through the properties and write them to the UD property set.
  1561. // ------------------------------------------------------------------
  1562. // We use a two-layer loop. The inner loop batches a group of properties
  1563. // in a PropVariant array, and then writes them to the Property Storage.
  1564. // The outer loop repeats this process until there are no more properties.
  1565. // This two-layer mechanism is desirable so that we reduce the number
  1566. // of WriteMultiple calls.
  1567. propid = PID_UDFIRST;
  1568. fLink = FALSE;
  1569. while (TRUE)
  1570. {
  1571. // ------------------------------------------
  1572. // Batch up a set of properties to be written
  1573. // ------------------------------------------
  1574. ulPropIndex = ulNameIndex = 0;
  1575. // We will break out of this loop when we have no more properties
  1576. // or if we have enough for a WriteMultiple.
  1577. while (FUserDefIteratorValid (lpudi))
  1578. {
  1579. Assert (lpudi->lpudp != NULL);
  1580. // ----------------------------------------------------------------------
  1581. // Create entries in the arrays for WriteMultiple and WritePropertyNames.
  1582. // ----------------------------------------------------------------------
  1583. // If fLink is TRUE, it means that we've written out the
  1584. // property, and now we need to write out the link name
  1585. // (with the PID_LINKMASK ORed into the propid).
  1586. if (!fLink)
  1587. {
  1588. // We aren't writing a link. So let's get the
  1589. // property from the linked-list (we know it exists because
  1590. // FUserDefIteratorValid was true).
  1591. lppropvar
  1592. = LppropvarUserDefGetIteratorVal (lpudi, NULL, NULL);
  1593. if (lppropvar == NULL)
  1594. {
  1595. AssertSz (0, TEXT("Invalid PropVariant in iterator"));
  1596. goto Exit;
  1597. }
  1598. // Copy this propvariant into the array which will be used for
  1599. // the WriteMultiple. Note that we do not copy any referenced
  1600. // buffer (e.g. we don't copy the string buffer if this is a string).
  1601. rgpropvar[ ulPropIndex ] = *lppropvar;
  1602. // If this property has a name, prepare to write it.
  1603. if (lpudi->lpudp->lpstzName != NULL)
  1604. {
  1605. // Add this name to rglpwstrName & rgpropidName.
  1606. #ifndef UNICODE
  1607. {
  1608. // Convert the ANSI name to Unicode (all OLE calls require
  1609. // Unicode strings).
  1610. if (!FCoStrToWStr (&rglpwstrName[ ulNameIndex ],
  1611. lpudi->lpudp->lpstzName,
  1612. uCodePage ))
  1613. {
  1614. AssertSz (0, TEXT("Couldn't convert name to Unicode"));
  1615. goto Exit;
  1616. }
  1617. }
  1618. #else
  1619. // Add this name to the list of those to be written.
  1620. rglpwstrName[ ulNameIndex ] = lpudi->lpudp->lpstzName;
  1621. #endif // UNICODE
  1622. // Add this propid to the list of those with names.
  1623. rgpropidName[ ulNameIndex ] = propid;
  1624. } // if (lpudi->lpudp->lpstzName != NULL)
  1625. } // if (!fLink)
  1626. else
  1627. {
  1628. // We are processing a link name. I.e., we've written the
  1629. // property value, now we need to write the name of the link,
  1630. // as a property, with the PID_LINKSMASK bit set in the PID.
  1631. Assert (lpudi->lpudp->lpstzLink != NULL);
  1632. // Create a entry in the PropVariant.
  1633. rgpropvar[ ulPropIndex ].vt = VT_LPTSTR;
  1634. (LPTSTR) rgpropvar[ ulPropIndex ].pszVal = lpudi->lpudp->lpstzLink;
  1635. }
  1636. // rgpropvar[ulPropIndex] now holds the property to be written,
  1637. // whether it is a real property or a link name.
  1638. // ------------------------------------
  1639. // Convert strings to the proper format.
  1640. // -------------------------------------
  1641. // (This could also convert the type from LPWSTR to LPSTR, or vice-versa).
  1642. // We don't have to worry about strings in vectors or in
  1643. // variant vectors, because these are illegal types for this
  1644. // property set.
  1645. if (rgpropvar[ ulPropIndex ].vt == VT_LPTSTR)
  1646. {
  1647. // If this string needs to be converted do so, putting the converted
  1648. // string in a new buffer. So,
  1649. // the caller's PropVariant still points to the old buffer,
  1650. // and our rgpropvar points to the new buffer.
  1651. if (PROPVAR_STRING_CONVERSION_REQUIRED (
  1652. &rgpropvar[ ulPropIndex ],
  1653. uCodePage))
  1654. {
  1655. // Convert the string into a temporary PropVariant.
  1656. PROPVARIANT propvarConvert;
  1657. PropVariantInit (&propvarConvert);
  1658. if (!FPropVarConvertString (&propvarConvert,
  1659. &rgpropvar[ ulPropIndex ],
  1660. uCodePage ))
  1661. {
  1662. AssertSz (0, TEXT("Couldn't convert string"));
  1663. goto Exit;
  1664. }
  1665. // Load this new PropVariant into rgpropvar, but don't
  1666. // delete the old buffer (so that we leave the linked-list
  1667. // of UDPROPs intact).
  1668. rgpropvar[ ulPropIndex ] = propvarConvert;
  1669. // Since we just created a new buffer, we must remember to free it.
  1670. rgfFreePropVar[ ulPropIndex ] = TRUE;
  1671. } // if (PROPVAR_STRING_CONVERSION_REQUIRED ( ...
  1672. } // if (rgpropvar[ ulPropIndex ].vt == VT_LPTSTR)
  1673. // --------------------------
  1674. // Finish this loop iteration
  1675. // --------------------------
  1676. // Set up the PropSpec.
  1677. rgpropspec[ ulPropIndex ].ulKind = PRSPEC_PROPID;
  1678. rgpropspec[ ulPropIndex ].propid = propid;
  1679. // If this is a link name, set the bit in the PID.
  1680. if (fLink)
  1681. {
  1682. rgpropspec[ ulPropIndex ].propid |= PID_LINKMASK;
  1683. }
  1684. // Advance the property index. And if we set a name, advance
  1685. // the name index.
  1686. ulPropIndex++;
  1687. if (rglpwstrName[ ulNameIndex ] != NULL)
  1688. {
  1689. ulNameIndex++;
  1690. }
  1691. // If we've just processed a link, or this is a property
  1692. // which is not linked to application content, then move on to the next property
  1693. // in the iterator. If we've just processed a property value that
  1694. // is linked, set fLink so that on the next pass through
  1695. // this loop, we'll write out the link name.
  1696. if (fLink || !FUserDefIteratorIsLink (lpudi))
  1697. {
  1698. fLink = FALSE;
  1699. propid++;
  1700. FUserDefIteratorNext (lpudi);
  1701. }
  1702. else
  1703. {
  1704. fLink = TRUE;
  1705. }
  1706. // If there's no more room in the WriteMultiple arrays,
  1707. // then write out the properties. We'll return to this
  1708. // inner loop when that's complete.
  1709. if (ulPropIndex >= DEFAULT_IPROPERTY_COUNT)
  1710. {
  1711. break;
  1712. }
  1713. } // while (FUserDefIteratorValid (lpudi))
  1714. // If broke out of the previous loop becuase there were no
  1715. // more properties, then we can break out of the outer loop
  1716. // as well -- we're done.
  1717. if (ulPropIndex == 0)
  1718. {
  1719. break;
  1720. }
  1721. // ---------------------
  1722. // Write the properties.
  1723. // ---------------------
  1724. hr = lpPropertyStorage->lpVtbl->WriteMultiple (
  1725. lpPropertyStorage, // 'this' pointer
  1726. ulPropIndex, // Number of properties
  1727. rgpropspec, // Property specifiers
  1728. rgpropvar, // The properties
  1729. PID_UDFIRST); // Not used.
  1730. if (FAILED(hr))
  1731. {
  1732. AssertSz (0, TEXT("Couldn't write properties"));
  1733. goto Exit;
  1734. }
  1735. // If we created any new buffers during string conversion,
  1736. // free them now.
  1737. for (ulIndex = 0; ulIndex < ulPropIndex; ulIndex++)
  1738. {
  1739. if (rgfFreePropVar[ ulIndex ])
  1740. {
  1741. PropVariantClear (&rgpropvar[ ulIndex ]);
  1742. rgfFreePropVar[ ulIndex ] = FALSE;
  1743. }
  1744. }
  1745. // ----------------
  1746. // Write the Names.
  1747. // ----------------
  1748. if (ulNameIndex != 0)
  1749. {
  1750. hr = lpPropertyStorage->lpVtbl->WritePropertyNames (
  1751. lpPropertyStorage, // 'this' pointer
  1752. ulNameIndex, // Number of names
  1753. rgpropidName, // PIDs for these names
  1754. rglpwstrName ); // The names
  1755. if (FAILED(hr))
  1756. {
  1757. AssertSz (0, TEXT("Couldn't write property names"));
  1758. goto Exit;
  1759. }
  1760. } // if (ulNameIndex != 0)
  1761. // Clear the names array.
  1762. for (ulIndex = 0; ulIndex < ulNameIndex; ulIndex++)
  1763. {
  1764. #ifndef UNICODE
  1765. // Free the memory which was allocated for this name.
  1766. CoTaskMemFree (rglpwstrName[ ulIndex ]);
  1767. #endif
  1768. rglpwstrName[ ulIndex ] = NULL;
  1769. } // for (ulIndex = 0; ulIndex < ulNameIndex; ulIndex++)
  1770. } // while (TRUE)
  1771. // ----
  1772. // Exit
  1773. // ----
  1774. fSuccess = TRUE;
  1775. Exit:
  1776. // Free the iterator
  1777. if (lpudi)
  1778. {
  1779. FUserDefDestroyIterator (&lpudi);
  1780. }
  1781. // Free any memory that was allocated for PropVariants.
  1782. for (ulIndex = 0; ulIndex < ulPropIndex; ulIndex++)
  1783. {
  1784. if (rgfFreePropVar[ ulIndex ])
  1785. {
  1786. PropVariantClear (&rgpropvar[ ulIndex ]);
  1787. }
  1788. }
  1789. #ifndef UNICODE
  1790. // Free any memory that was allocated for name.
  1791. for (ulIndex = 0; ulIndex < ulNameIndex; ulIndex++)
  1792. {
  1793. CoTaskMemFree (rglpwstrName[ ulIndex ]);
  1794. }
  1795. #endif
  1796. // Release the UD Property Storage.
  1797. RELEASEINTERFACE (lpPropertyStorage);
  1798. return (fSuccess);
  1799. } // FSaveUserDef