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.

1476 lines
43 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1994.
  5. //
  6. // File: privstm.cpp
  7. //
  8. // Contents: Handles all reading/writing of the \1CompObj stream
  9. //
  10. // Functions: Implements:
  11. //
  12. // INTERNAL ReadCompObjStm
  13. // INTERNAL WriteCompObjStm
  14. // INTERNAL ClipfmtToStm
  15. // INTERNAL StmToClipfmt
  16. // INTERNAL GetUNICODEUserType
  17. // INTERNAL GetUNICODEProgID
  18. // INTERNAL GetUNICODEClipFormat
  19. // INTERNAL PutUNICODEUserType
  20. // INTERNAL PutUNICODEProgID
  21. // INTERNAL PutUNICODEClipFormat
  22. // INTERNAL UtGetUNICODEData
  23. // INTERNAL ANSIStrToStm
  24. // INTERNAL ANSIStmToStr
  25. //
  26. // STDAPI WriteFmtUserTypeStg
  27. // STDAPI ReadFmtUserTypeStg
  28. // STDAPI ReadFmtProgIdStg
  29. //
  30. // History: dd-mmm-yy Author Comment
  31. // 08-Feb-94 davepl Created
  32. //
  33. //
  34. // Notes: The CompObj stream (in 16-bit OLE) contained fields for
  35. // the ClassID, UserType, Clipboard format, and (in later
  36. // versions only) ProgID. These were always written in ANSI
  37. // format.
  38. //
  39. // The file format has been extended such that ANSI data is
  40. // written to the stream in much the same way as before. The
  41. // key difference is that in the event the internal UNICODE
  42. // versions of this data cannot be losslessly converted to
  43. // ANSI, the ANSI version is written as a NULL string, and
  44. // the UNICODE version follows at the end of the stream. This
  45. // way, 16-bit apps see as much of what they expect as possible,
  46. // and 32-bit apps can write UNICODE transparently in a
  47. // backwards-compatible way.
  48. //
  49. // The file format of the stream is:
  50. //
  51. // (A) WORD Byte Order
  52. // WORD Format Version
  53. // DWORD Original OS ver Always Windows 3.1
  54. // DWORD -1
  55. // CLSID Class ID
  56. // ULONG Length of UserType
  57. // <var> User Type string ANSI
  58. // <var> Clipformat ANSI (when using string tag)
  59. // ----------------------------
  60. // (B) ULONG Length of Prog ID
  61. // <var> Prog ID ANSI (not always present)
  62. // ----------------------------
  63. // (C) ULONG Magic Number Signified UNICODE data present
  64. // ULONG Length of UserType
  65. // ULONG User Type string UNICODE
  66. // <var> Clipformat UNICODE (when tag is string)
  67. // ULONG Length of Prog ID
  68. // <var> Prog ID UNICODE
  69. //
  70. // Section (A) is always present. Section (B) is present when
  71. // stream has been written by a later 16-bit app or by a
  72. // 32-bit app. Section (C) is present when written by a
  73. // 32-bit app.
  74. //
  75. // If a string is present in UNICODE, the ANSI version will be
  76. // NULL (a zero for length and _no_ <var> data). When the
  77. // UNICODE section is present, strings that were not needed
  78. // because the ANSI conversion was successful are written
  79. // as NULL (again, zero len and no <var> data).
  80. //
  81. // A NULL clipboard format is written as a 0 tag.
  82. //
  83. // In order to read any field, the entire string is read into
  84. // an internal object, and the fields are extracted individually.
  85. // In order to write an fields, the stream is read into the
  86. // object (if possible), the fields updated, and then rewritten
  87. // as an atomic object.
  88. //
  89. //--------------------------------------------------------------------------
  90. #include <le2int.h>
  91. static const ULONG COMP_OBJ_MAGIC_NUMBER = 0x71B239F4;
  92. #define MAX_CFNAME 400 // Maximum size of a clipformat name
  93. // (my choice, none documented)
  94. const DWORD gdwFirstDword = (DWORD)MAKELONG(COMPOBJ_STREAM_VERSION,
  95. BYTE_ORDER_INDICATOR);
  96. enum TXTTYPE
  97. {
  98. TT_UNICODE = 0, TT_ANSI = 1
  99. };
  100. // This is the data object into which the stream is read prior to
  101. // extracting fields.
  102. struct CompObjHdr // The leading data in the CompObj stream
  103. {
  104. DWORD m_dwFirstDword; // First DWORD, byte order and format ver
  105. DWORD m_dwOSVer; // Originating OS Ver (eg: Win31)
  106. DWORD m_unused; // Always a -1L in the stream
  107. CLSID m_clsClass; // Class ID of this object
  108. };
  109. class CompObjStmData : public CPrivAlloc
  110. {
  111. public:
  112. CompObjHdr m_hdr;
  113. ULONG m_cchUserType; // Number of CHARACTERS in UserType
  114. ULONG m_cchProgID; // Number of CHARACTERS in ProgID
  115. DWORD m_dwFormatTag; // Clipformat type (none, string, clip, etc)
  116. ULONG m_ulFormatID; // If tag is std clipformat, what type?
  117. LPOLESTR m_pszOUserType; // Pointer to OLESTR UserType
  118. LPOLESTR m_pszOProgID; // Pointer to OLESTR ProgID
  119. LPSTR m_pszAUserType; // Pointer to ANSI UserType
  120. LPSTR m_pszAProgID; // Pointer to ANSI ProgID
  121. TXTTYPE ttClipString; // Format needed for the clipformat string
  122. CompObjStmData(void)
  123. {
  124. memset(this, 0, sizeof(CompObjStmData));
  125. ttClipString = TT_ANSI; // By default, use ANSI Clipformat
  126. };
  127. ~CompObjStmData(void)
  128. {
  129. PubMemFree(m_pszOUserType);
  130. PubMemFree(m_pszOProgID);
  131. PubMemFree(m_pszAUserType);
  132. PubMemFree(m_pszAProgID);
  133. };
  134. };
  135. // Prototypes for fns declared in this file
  136. INTERNAL ReadCompObjStm (IStorage *, CompObjStmData *);
  137. INTERNAL WriteCompObjStm (IStorage *, CompObjStmData *);
  138. INTERNAL ClipfmtToStm (CStmBufWrite &, ULONG, ULONG, TXTTYPE);
  139. INTERNAL StmToClipfmt (CStmBufRead &, DWORD *, DWORD *, TXTTYPE);
  140. INTERNAL GetUNICODEUserType (CompObjStmData *, LPOLESTR *);
  141. INTERNAL GetUNICODEProgID (CompObjStmData *, LPOLESTR *);
  142. INTERNAL GetClipFormat (CompObjStmData *, DWORD *, DWORD *);
  143. INTERNAL PutUNICODEUserType (CompObjStmData *, LPOLESTR);
  144. INTERNAL PutUNICODEProgID (CompObjStmData *, LPOLESTR);
  145. INTERNAL PutClipFormat (CompObjStmData *, DWORD, DWORD);
  146. INTERNAL ANSIStrToStm (CStmBufWrite &, LPCSTR);
  147. INTERNAL ANSIStmToStr (CStmBufRead & StmRead, LPSTR * pstr, ULONG *);
  148. STDAPI WriteFmtUserTypeStg (IStorage *, CLIPFORMAT, LPOLESTR);
  149. STDAPI ReadFmtUserTypeStg (IStorage *, CLIPFORMAT *, LPOLESTR *);
  150. STDAPI ReadFmtProgIdStg (IStorage *, LPOLESTR *);
  151. //+-------------------------------------------------------------------------
  152. //
  153. // Function: ReadCompObjStm, PRIVATE INTERNAL
  154. //
  155. // Synopsis: Reads the \1CompObj stream into an internal data structure
  156. // that will contain the best-case representation of that
  157. // stream (ie: ANSI where possible, UNICODE where needed).
  158. //
  159. // Effects: Reads ANSI data where available. At end of standard ANSI
  160. // data, looks for ANSI ProgID field. If found, looks for
  161. // MagicNumber indicating UNICODE data is to follow. If this
  162. // matches, UNICODE strings are pulled from the stream. They
  163. // should only be found where the ANSI version was NULL
  164. // (because it could not be converted from UNICODE).
  165. //
  166. // Capable of reading 3 stream formats seamlessly:
  167. // - Original ANSI sans ProgID field
  168. // - Extended OLE 2.01 version with ProgID
  169. // - Extended OLE 2/32 version with ProgID and UNICODE extensions
  170. //
  171. // Arguments: [pstg] -- ptr to IStorage to read from
  172. // [pcod] -- ptr to already-allocated CompObjData object
  173. //
  174. // Returns: NOERROR on success
  175. // INVALIDARG on missing pcod
  176. // Various I/O on stream missing, read errors, etc
  177. // E_OUTOFMEMORY on any allocation failure
  178. //
  179. // History: dd-mmm-yy Author Comment
  180. // 08-Mar-94 davepl Created
  181. //
  182. // Notes: Any memory allocated herein will be allocated on
  183. // pointers in the pcod object, which will be freed by its
  184. // destructor when it exits scope or is deleted explicitly.
  185. //
  186. //--------------------------------------------------------------------------
  187. INTERNAL ReadCompObjStm(IStorage * pstg, CompObjStmData * pcod)
  188. {
  189. VDATEHEAP();
  190. HRESULT hr; // Result code
  191. const ULONG RESERVED = 0; // For reserved parameters
  192. ULONG ulSize = 0; // Holder for length of ProgID string
  193. BOOL fExtStm = 1; // Could this be ext with UNICODE?
  194. CStmBufRead StmRead;
  195. Win4Assert(pcod);
  196. // Validate the pstg interface
  197. VDATEIFACE(pstg);
  198. // Open the CompObj stream
  199. if (FAILED(hr = StmRead.OpenStream(pstg, COMPOBJ_STREAM))) // L"\1CompObj"
  200. {
  201. goto errRtn;
  202. }
  203. // Read the header from the CompObj stream:
  204. //
  205. // WORD Byte Order Indicator 02 bytes
  206. // WORD Format version 02 bytes
  207. // DWORD Originating OS version 04 bytes
  208. // DWORD -1 04 bytes
  209. // CLSID Class ID 16 bytes
  210. // --------
  211. // 28 bytes == sizeof(dwBuf)
  212. Win4Assert(sizeof(CompObjHdr) == 28 &&
  213. "Warning: possible packing error in CompObjHdr struct");
  214. hr = StmRead.Read(&pcod->m_hdr, sizeof(CompObjHdr));
  215. if (FAILED(hr))
  216. {
  217. goto errRtn;
  218. }
  219. // NB: There used to be a check against the OS version here,
  220. // but since the version number has been forced to always
  221. // be written as Win3.1, checking it would be redundant.
  222. // Win4Assert(pcod->m_hdr.m_dwOSVer == 0x00000a03);
  223. #if DBG==1
  224. if (pcod->m_hdr.m_dwOSVer != 0x00000a03)
  225. {
  226. LEDebugOut((DEB_WARN, "ReadCompObjStm found unexpected OSVer %lx",
  227. pcod->m_hdr.m_dwOSVer));
  228. }
  229. #endif
  230. // Get the User type string from the stream (ANSI FORMAT!)
  231. if (FAILED(hr = ANSIStmToStr(StmRead, &pcod->m_pszAUserType,
  232. &pcod->m_cchUserType)))
  233. {
  234. goto errRtn;
  235. }
  236. // Get the clipboard format data from the stream
  237. if (FAILED(hr = StmToClipfmt(StmRead, // Stream to read from
  238. &pcod->m_dwFormatTag, // DWORD clip format
  239. &pcod->m_ulFormatID, // DWORD clip type
  240. TT_ANSI))) // Use ANSI
  241. {
  242. goto errRtn;
  243. }
  244. // We have to special-case the ProgID field, because it may not
  245. // be present in objects written by early (pre-2.01) versions
  246. // of OLE. We only continue when ProgID can be found, but
  247. // its absence is not an error, so return what we have so far.
  248. hr = StmRead.Read(&ulSize, sizeof(ULONG));
  249. if (FAILED(hr))
  250. {
  251. // We were unable to read the size field; make sure ulSize is 0
  252. ulSize = 0;
  253. }
  254. // The ProgID can be no longer than 39 chars plus a NULL. Other
  255. // numbers likely indicate garbage.
  256. if (ulSize > 40 || 0 == ulSize)
  257. {
  258. #if DBG==1
  259. if (ulSize > 40)
  260. {
  261. LEDebugOut((DEB_WARN,"ReadCompObjStm: ulSize > 40 for ProgID\n"));
  262. }
  263. #endif
  264. fExtStm = 0; // No ProgID implies no UNICODE to follow
  265. }
  266. // If it looks like we have a hope of findind the ProgID and maybe
  267. // even UNICODE, try to fetch the ProdID
  268. if (fExtStm)
  269. {
  270. // Allocate memory for string on our ProgID pointer
  271. pcod->m_pszAProgID = (char *) PubMemAlloc(ulSize);
  272. if (NULL == pcod->m_pszAProgID)
  273. {
  274. hr = ResultFromScode(E_OUTOFMEMORY);
  275. goto errRtn;
  276. }
  277. if (FAILED(hr = StmRead.Read(pcod->m_pszAProgID, ulSize)))
  278. {
  279. // OK, we give up on ProgID and the UNICODE, but that's
  280. // _not_ reason to fail, since ProgID could just be missing
  281. pcod->m_cchProgID = 0;
  282. PubMemFree(pcod->m_pszAProgID);
  283. pcod->m_pszAProgID = NULL;
  284. fExtStm = 0;
  285. }
  286. else
  287. {
  288. // We managed to get ProgID from the stream, so set the
  289. // length in pcod and go looking for the UNICODE...
  290. pcod->m_cchProgID = ulSize;
  291. }
  292. }
  293. // See if we can find the Magic number
  294. DWORD dwMagic;
  295. if (fExtStm)
  296. {
  297. if (FAILED(StmRead.Read(&dwMagic, sizeof(dwMagic))))
  298. {
  299. fExtStm = 0;
  300. }
  301. }
  302. if (fExtStm && dwMagic != COMP_OBJ_MAGIC_NUMBER)
  303. {
  304. fExtStm = 0;
  305. }
  306. // If fExtStm is still TRUE, we go ahead and read the UNICODE
  307. if (fExtStm)
  308. {
  309. // Get the UNICODE version of the user type
  310. if (FAILED(hr = ReadStringStream(StmRead, &pcod->m_pszOUserType)))
  311. {
  312. goto errRtn;
  313. }
  314. // Get the clipboard format (UNICODE)
  315. DWORD dwFormatTag;
  316. ULONG ulFormatID;
  317. if (FAILED(hr = StmToClipfmt(StmRead, // Stream to read from
  318. &dwFormatTag, // DWORD clip format
  319. &ulFormatID, // DWORD clip type
  320. TT_UNICODE))) // Use UNICODE
  321. {
  322. goto errRtn;
  323. }
  324. // If we found some form of clipboard format, that implies the ANSI
  325. // was missing, so set up all of the fields based on this data.
  326. if (dwFormatTag)
  327. {
  328. pcod->m_dwFormatTag = dwFormatTag;
  329. pcod->m_ulFormatID = ulFormatID;
  330. }
  331. // Get the UNICODE version of the ProgID. If there was any UNICODE at
  332. // all, we know for sure there is a UNICODE ProgID, so no special casing
  333. // as was needed for the ANSI version
  334. if (FAILED(hr = ReadStringStream(StmRead, &pcod->m_pszOProgID)))
  335. {
  336. goto errRtn;
  337. }
  338. if (pcod->m_pszOProgID)
  339. {
  340. pcod->m_cchProgID = _xstrlen(pcod->m_pszOProgID) + 1;
  341. }
  342. }
  343. // We successfully read the CompObj stream
  344. hr = NOERROR;
  345. errRtn:
  346. StmRead.Release();
  347. return(hr);
  348. }
  349. //+-------------------------------------------------------------------------
  350. //
  351. // Function: StmToClipfmt, PRIVATE INTERNAL
  352. //
  353. // Synopsis: Reads the clipboard format from the given stream. Caller
  354. // specifies whether or not the string format description,
  355. // if present, should be expected in ANSI or UNICODE format.
  356. //
  357. // Effects: If the clipboard format is a length followed by a
  358. // string, then the string is read and registered as a
  359. // clipboard format (and the new format number is returned).
  360. //
  361. // Arguments: [lpstream] -- pointer to the stream
  362. // [lpdwCf] -- where to put the clipboard format
  363. // [lpdTag] -- format type (string, clip, etc)
  364. // [ttType] -- text type TT_ANSI or TT_UNICODE
  365. //
  366. // Returns: hr
  367. //
  368. // Algorithm: the format of the stream must be one of the following:
  369. //
  370. // 0 No clipboard format
  371. // -1 DWORD predefined windows clipboard format in
  372. // the second dword.
  373. // -2 DWORD predefined mac clipboard format in the
  374. // second dword. This may be obsolete or
  375. // irrelevant for us. REVIEW32
  376. // num STRING clipboard format name string (prefaced
  377. // by length of string).
  378. //
  379. // History: dd-mmm-yy Author Comment
  380. // 08-Mar-94 davepl Created
  381. //
  382. //--------------------------------------------------------------------------
  383. INTERNAL StmToClipfmt
  384. (CStmBufRead & StmRead,
  385. DWORD * lpdTag,
  386. DWORD * lpdwCf,
  387. TXTTYPE ttText)
  388. {
  389. VDATEHEAP();
  390. HRESULT hr;
  391. DWORD dwValue;
  392. VDATEPTROUT(lpdwCf, DWORD);
  393. Win4Assert (lpdwCf); // These ptrs are always required
  394. Win4Assert (lpdTag);
  395. // Read the format type tag from the stream
  396. if (FAILED(hr = StmRead.Read(&dwValue, sizeof(DWORD))))
  397. {
  398. return hr;
  399. }
  400. *lpdTag = dwValue;
  401. // If the tag is zero, there is no clipboard format info
  402. if (dwValue == 0)
  403. {
  404. *lpdwCf = 0; // NULL cf value
  405. }
  406. // If it is -1, then it is a standard Windows clipboard format
  407. else if (dwValue == -1L)
  408. {
  409. // Then this is a NON-NULL predefined windows clipformat.
  410. // The clipformat values follows
  411. if (FAILED(hr = StmRead.Read(&dwValue, sizeof(DWORD))))
  412. {
  413. return hr;
  414. }
  415. *lpdwCf = dwValue;
  416. }
  417. // If it is -2, it is a MAC format
  418. else if (dwValue == -2L)
  419. {
  420. // Then this is a NON-NULL MAC clipboard format.
  421. // The clipformat value follows. For MAC the CLIPFORMAT
  422. // is 4 bytes
  423. if (FAILED(hr = StmRead.Read(&dwValue, sizeof(DWORD))))
  424. {
  425. return hr;
  426. }
  427. *lpdwCf = dwValue;
  428. return ResultFromScode(OLE_S_MAC_CLIPFORMAT);
  429. }
  430. // Anything but a 0, -1, or -2 indicates a string is to follow, and the
  431. // DWORD we already read is the length of the that string
  432. else
  433. {
  434. // Allocate enough memory for whatever type of string it is
  435. // we expect to find, and read the string
  436. if (dwValue > MAX_CFNAME)
  437. {
  438. return ResultFromScode(DV_E_CLIPFORMAT);
  439. }
  440. if (TT_ANSI == ttText) // READ ANSI
  441. {
  442. char szCf[MAX_CFNAME];
  443. if (FAILED(hr = StmRead.Read(szCf, dwValue)))
  444. {
  445. return hr;
  446. }
  447. // Try to register the clipboard format and return the result
  448. // (Note: must explicitly call ANSI version)
  449. if (((*lpdwCf = (DWORD) SSRegisterClipboardFormatA(szCf))) == 0)
  450. {
  451. return ResultFromScode(DV_E_CLIPFORMAT);
  452. }
  453. }
  454. else // READ UNICODE
  455. {
  456. OLECHAR wszCf[MAX_CFNAME];
  457. Win4Assert(dwValue < MAX_CFNAME);
  458. if (FAILED(hr=StmRead.Read(wszCf, dwValue * sizeof(OLECHAR))))
  459. {
  460. return hr;
  461. }
  462. // Try to register the clipboard format and return the result
  463. if (((*lpdwCf = (DWORD) RegisterClipboardFormat(wszCf))) == 0)
  464. {
  465. return ResultFromScode(DV_E_CLIPFORMAT);
  466. }
  467. }
  468. }
  469. return NOERROR;
  470. }
  471. //+-------------------------------------------------------------------------
  472. //
  473. // Function: GetUNICODEUserType, PRIVATE INTERNAL
  474. //
  475. // Synopsis: Given a CompObjStmData object, returns the User Type
  476. // in UNICODE format, converting the ANSI rep as required.
  477. //
  478. // Effects: Allocates memory on the caller's ptr to hold the string
  479. //
  480. // Arguments: [pcod] -- The CompObjStmData object
  481. // [pstr] -- Pointer to allocate resultant string on
  482. //
  483. // Returns: NOERROR on success
  484. // E_OUTOFMEMORY on allocation failure
  485. //
  486. // History: dd-mmm-yy Author Comment
  487. // 08-Mar-94 davepl Created
  488. //
  489. //--------------------------------------------------------------------------
  490. INTERNAL GetUNICODEUserType
  491. ( CompObjStmData * pcod,
  492. LPOLESTR * pstr )
  493. {
  494. VDATEHEAP();
  495. HRESULT hr = NOERROR;
  496. // Validate and NULL the OUT parameter, or return if none given
  497. if (pstr)
  498. {
  499. VDATEPTROUT(pstr, LPOLESTR);
  500. *pstr = NULL;
  501. }
  502. else
  503. {
  504. return(NOERROR);
  505. }
  506. // Either get the UNICODE string, or convert the ANSI version and
  507. // get it as UNICODE.
  508. if (pcod->m_cchUserType)
  509. {
  510. hr = UtGetUNICODEData( pcod->m_cchUserType,
  511. pcod->m_pszAUserType,
  512. pcod->m_pszOUserType,
  513. pstr );
  514. }
  515. return hr;
  516. }
  517. //+-------------------------------------------------------------------------
  518. //
  519. // Function: GetUNICODEProgID, PRIVATE INTERNAL
  520. //
  521. // Synopsis: Given a CompObjStmData object, returns the ProgID string
  522. // in UNICODE format, converting the ANSI rep as required.
  523. //
  524. // Effects: Allocates memory on the caller's ptr to hold the string
  525. //
  526. // Arguments: [pcod] -- The CompObjStmData object
  527. // [pstr] -- Pointer to allocate resultant string on
  528. //
  529. // Returns: NOERROR on success
  530. // E_OUTOFMEMORY on allocation failure
  531. //
  532. // History: dd-mmm-yy Author Comment
  533. // 08-Mar-94 davepl Created
  534. //
  535. //--------------------------------------------------------------------------
  536. INTERNAL GetUNICODEProgID
  537. ( CompObjStmData * pcod,
  538. LPOLESTR * pstr )
  539. {
  540. VDATEHEAP();
  541. HRESULT hr = NOERROR;
  542. // Validate and NULL the OUT parameter, or return if none given
  543. if (pstr)
  544. {
  545. VDATEPTROUT(pstr, LPOLESTR);
  546. *pstr = NULL;
  547. }
  548. else
  549. {
  550. return(NOERROR);
  551. }
  552. // Either get the UNICODE string, or convert the ANSI version and
  553. // get it as UNICODE.
  554. if (pcod->m_cchProgID)
  555. {
  556. hr = UtGetUNICODEData( pcod->m_cchProgID,
  557. pcod->m_pszAProgID,
  558. pcod->m_pszOProgID,
  559. pstr );
  560. }
  561. return hr;
  562. }
  563. //+-------------------------------------------------------------------------
  564. //
  565. // Function: GetClipFormat, PRIVATE INTERNAL
  566. //
  567. // Synopsis: Given a CompObjStmData object, extracts the clipboard format
  568. // type (none, standard, string).
  569. //
  570. // Effects: If string type, memory is allocated on the caller's ptr
  571. //
  572. // Arguments: [pcod] -- The CompObjStmData object to extract from
  573. // [pdwFormatID] -- Tag type OUT parameter
  574. // [pdwFormatTag] -- Tag OUT parameter
  575. //
  576. // Returns: NOERROR on success
  577. // E_OUTOFMEMORY on allocation failures
  578. // OLE_S_MAC_CLIPFORMAT as a warning that a MAC fmt is returned
  579. //
  580. // History: dd-mmm-yy Author Comment
  581. // 08-Mar-94 davepl Created
  582. //
  583. //--------------------------------------------------------------------------
  584. INTERNAL GetClipFormat
  585. ( CompObjStmData * pcod,
  586. DWORD * pdwFormatID,
  587. DWORD * pdwFormatTag )
  588. {
  589. VDATEHEAP();
  590. *pdwFormatTag = (DWORD) pcod->m_dwFormatTag;
  591. *pdwFormatID = pcod->m_ulFormatID;
  592. return NOERROR;
  593. }
  594. //+-------------------------------------------------------------------------
  595. //
  596. // Function: PutUNICODEUserType, PRIVATE INTERNAL
  597. //
  598. // Synopsis: Given a UNICODE string, stores it in the CompObjDataStm
  599. // object in ANSI if possible. If the UNICODE -> ANSI
  600. // conversion is not possible, it is stored in the object
  601. // in UNICODE.
  602. //
  603. // Notes: Input string is duplicated, so it adds no references
  604. // to the string passed in.
  605. //
  606. // Arguments: [pcod] -- The CompObjDataStm object
  607. // [szUser] -- The UNICODE UserType string
  608. //
  609. // Returns: NOERROR on success
  610. // E_OUTOFMEMORY on allocation failure
  611. //
  612. // History: dd-mmm-yy Author Comment
  613. // 08-Mar-94 davepl Created
  614. //
  615. //--------------------------------------------------------------------------
  616. INTERNAL PutUNICODEUserType(CompObjStmData * pcod, LPOLESTR szUser)
  617. {
  618. VDATEHEAP();
  619. HRESULT hr;
  620. // If no string supplied, clear UserType fields, otherwise
  621. // if it can be converted to ANSI, store it an ANSI. Last
  622. // resort, store it as UNICODE.
  623. if (NULL == szUser)
  624. {
  625. pcod->m_cchUserType = 0;
  626. PubMemFree(pcod->m_pszAUserType);
  627. PubMemFree(pcod->m_pszOUserType);
  628. pcod->m_pszAUserType = NULL;
  629. pcod->m_pszOUserType = NULL;
  630. }
  631. else
  632. {
  633. if (FAILED(hr = UtPutUNICODEData( _xstrlen(szUser)+1,
  634. szUser,
  635. &pcod->m_pszAUserType,
  636. &pcod->m_pszOUserType,
  637. &pcod->m_cchUserType )))
  638. {
  639. return(hr);
  640. }
  641. }
  642. return(NOERROR);
  643. }
  644. //+-------------------------------------------------------------------------
  645. //
  646. // Function: PutUNICODEProgID, PRIVATE INTERNAL
  647. //
  648. // Synopsis: Given a UNICODE string, stores it in the CompObjDataStm
  649. // object in ANSI if possible. If the UNICODE -> ANSI
  650. // conversion is not possible, it is stored in the object
  651. // in UNICODE.
  652. //
  653. // Notes: Input string is duplicated, so it adds no references
  654. // to the string passed in.
  655. //
  656. // Arguments: [pcod] -- The CompObjDataStm object
  657. // [szProg] -- The UNICODE ProgID string
  658. //
  659. // Returns: NOERROR on success
  660. // E_OUTOFMEMORY on allocation failure
  661. //
  662. // History: dd-mmm-yy Author Comment
  663. // 08-Mar-94 davepl Created
  664. //
  665. //--------------------------------------------------------------------------
  666. INTERNAL PutUNICODEProgID(CompObjStmData * pcod, LPOLESTR szProg)
  667. {
  668. VDATEHEAP();
  669. HRESULT hr;
  670. // If no string supplied, clear ProgID fields, otherwise
  671. // if it can be converted to ANSI, store it an ANSI. Last
  672. // resort, store it as UNICODE.
  673. if (NULL == szProg)
  674. {
  675. pcod->m_cchProgID = 0;
  676. PubMemFree(pcod->m_pszAProgID);
  677. PubMemFree(pcod->m_pszOProgID);
  678. pcod->m_pszAProgID = NULL;
  679. pcod->m_pszOProgID = NULL;
  680. }
  681. else
  682. {
  683. if (FAILED(hr = UtPutUNICODEData( _xstrlen(szProg)+1,
  684. szProg,
  685. &pcod->m_pszAProgID,
  686. &pcod->m_pszOProgID,
  687. &pcod->m_cchProgID )))
  688. {
  689. return(hr);
  690. }
  691. }
  692. return(NOERROR);
  693. }
  694. //+-------------------------------------------------------------------------
  695. //
  696. // Function: PutClipFormat
  697. //
  698. // Synopsis: Stores the clipformat in the internal data structure
  699. //
  700. // Effects: Input string is duplicated as required, so no references are
  701. // kept by this function.
  702. //
  703. // Arguments: [pcod] -- The CompObjStmData object
  704. // [dwFormatTag] -- Format tag (string, clipboard, none)
  705. // [ulFormatID] -- If format tag is clipboard, what format
  706. //
  707. // Returns: NOERROR on success
  708. //
  709. // History: dd-mmm-yy Author Comment
  710. // 08-Mar-94 davepl Created
  711. //
  712. //--------------------------------------------------------------------------
  713. INTERNAL PutClipFormat
  714. ( CompObjStmData * pcod,
  715. DWORD dwFormatTag,
  716. ULONG ulFormatID
  717. )
  718. {
  719. VDATEHEAP();
  720. pcod->m_dwFormatTag = (ULONG) dwFormatTag;
  721. pcod->m_ulFormatID = ulFormatID;
  722. return NOERROR;
  723. }
  724. //+-------------------------------------------------------------------------
  725. //
  726. // Function: WriteCompObjStm, PRIVATE INTERNAL
  727. //
  728. // Synopsis: Writes CompObjStmData object to the CompObj stream in
  729. // the IStorage provided.
  730. //
  731. // First the ANSI fields are written (including the ProgID),
  732. // followed by a MagicNumber, followed by whatever OLESTR
  733. // versions were required because ANSI fields could not be
  734. // converted.
  735. //
  736. // Destroys any existing CompObj stream!
  737. //
  738. // Arguments: [pstg] -- The IStorage to write the stream to
  739. // [pcod] -- The CompObjStmData object to write out
  740. //
  741. // Returns: NOERROR on success
  742. // E_OUTOFMEMORY on allocation failure
  743. // Various I/O on stream failures
  744. //
  745. // History: dd-mmm-yy Author Comment
  746. // 08-Mar-94 davepl Created
  747. //
  748. //--------------------------------------------------------------------------
  749. INTERNAL WriteCompObjStm(IStorage * pstg, CompObjStmData * pcod)
  750. {
  751. VDATEHEAP();
  752. HRESULT hr = NOERROR;
  753. const ULONG RESERVED = 0;
  754. const ULONG ulMagic = COMP_OBJ_MAGIC_NUMBER;
  755. CStmBufWrite StmWrite;
  756. // The CompObjStmData parameter must be supplied
  757. if (NULL == pcod)
  758. {
  759. return ResultFromScode(E_INVALIDARG);
  760. }
  761. VDATEIFACE(pstg);
  762. // Open the CompObj stm for writing (and overwrite it if
  763. // if already exists, which is why we _don't_ specify the
  764. // STGM_FAILIFTHERE flag)
  765. if (FAILED(hr = StmWrite.CreateStream(pstg, COMPOBJ_STREAM)))
  766. {
  767. goto errRtn;
  768. }
  769. // Set up the header
  770. pcod->m_hdr.m_dwFirstDword = gdwFirstDword;
  771. // The OSVer _must_ be Win 3.10 (0a03), since the old DLL will bail if
  772. // it finds anything else.
  773. pcod->m_hdr.m_dwOSVer = 0x00000a03; // gdwOrgOSVersion;
  774. pcod->m_hdr.m_unused = (DWORD) -1;
  775. if (ReadClassStg(pstg, &pcod->m_hdr.m_clsClass) != NOERROR)
  776. {
  777. pcod->m_hdr.m_clsClass = CLSID_NULL;
  778. }
  779. // Write the CompObj stream header
  780. Win4Assert(sizeof(CompObjHdr) == 28 &&
  781. "Warning: possible packing error in CompObjHdr struct");
  782. if (FAILED(hr = StmWrite.Write(pcod, sizeof(CompObjHdr))))
  783. {
  784. goto errRtn;
  785. }
  786. // Write the ANSI UserType
  787. if (FAILED(hr = ANSIStrToStm(StmWrite, pcod->m_pszAUserType)))
  788. {
  789. goto errRtn;
  790. }
  791. if (TT_ANSI == pcod->ttClipString)
  792. {
  793. if (FAILED(hr = ClipfmtToStm(StmWrite, // the stream
  794. pcod->m_dwFormatTag, // format tag
  795. pcod->m_ulFormatID, // format ID
  796. TT_ANSI)))// TRUE==use ANSI
  797. {
  798. goto errRtn;
  799. }
  800. }
  801. else
  802. {
  803. const ULONG ulDummy = 0;
  804. if (FAILED(hr = StmWrite.Write(&ulDummy, sizeof(ULONG))))
  805. {
  806. goto errRtn;
  807. }
  808. }
  809. // Write the ANSI ProgID
  810. if (FAILED(hr = ANSIStrToStm(StmWrite, pcod->m_pszAProgID)))
  811. {
  812. goto errRtn;
  813. }
  814. // Write the Magic Number
  815. if (FAILED(hr = StmWrite.Write(&ulMagic, sizeof(ULONG))))
  816. {
  817. goto errRtn;
  818. }
  819. // Write the OLESTR version of UserType
  820. if (FAILED(hr = WriteStringStream(StmWrite, pcod->m_pszOUserType)))
  821. {
  822. goto errRtn;
  823. }
  824. // If we have to write a UNICODE clipformat string, do it now. If
  825. // ANSI was sufficient, just write a 0 to the stream here.
  826. if (TT_UNICODE == pcod->ttClipString)
  827. {
  828. if (FAILED(hr = ClipfmtToStm(StmWrite, // the stream
  829. pcod->m_dwFormatTag, // format tag
  830. pcod->m_ulFormatID, // format ID
  831. TT_UNICODE))) // FALSE==use UNICODE
  832. {
  833. goto errRtn;
  834. }
  835. }
  836. else
  837. {
  838. const ULONG ulDummy = 0;
  839. if (FAILED(hr = StmWrite.Write(&ulDummy, sizeof(ULONG))))
  840. {
  841. goto errRtn;
  842. }
  843. }
  844. // Write the OLESTR version of ProgID
  845. if (FAILED(hr = WriteStringStream(StmWrite, pcod->m_pszOProgID)))
  846. {
  847. goto errRtn;
  848. }
  849. hr = StmWrite.Flush();
  850. // That's it.. clean up and exit
  851. errRtn:
  852. StmWrite.Release();
  853. return hr;
  854. }
  855. //+-------------------------------------------------------------------------
  856. //
  857. // Function: ClipFtmToStm, PRIVATE INTERNAL
  858. //
  859. // Synopsis: Writes out the clipboard format information at the
  860. // current point in the stream. A flag is available
  861. // to specify whether or not the string format desc
  862. // (if present) is in ANSI or UNICODE format.
  863. //
  864. // Arguments: [pstm] -- the stream to write to
  865. // [dwFormatTag] -- format tag (string, clipfmt, etc)
  866. // [ulFormatID] -- if clipfmt, which one
  867. // [szClipFormat] -- if string format, the string itself
  868. // [ttText] -- text type: TT_ANSI or TT_UNICODE
  869. //
  870. // Returns: NOERROR on success
  871. // E_OUTOFMEMORY on allocation failure
  872. //
  873. // History: dd-mmm-yy Author Comment
  874. // 08-Mar-94 davepl Created
  875. //
  876. //--------------------------------------------------------------------------
  877. INTERNAL ClipfmtToStm
  878. ( CStmBufWrite & StmWrite,
  879. DWORD dwFormatTag,
  880. ULONG ulFormatID,
  881. TXTTYPE ttText )
  882. {
  883. VDATEHEAP();
  884. HRESULT hr;
  885. const ULONG ulDummy = 0;
  886. switch((DWORD)dwFormatTag)
  887. {
  888. // If the tag is 0, there is no clipboard format info.
  889. case 0:
  890. if (FAILED(hr = StmWrite.Write(&ulDummy, sizeof(ULONG))))
  891. {
  892. return(hr);
  893. }
  894. return(NOERROR);
  895. // In the -1 and -2 cases (yes, I wish there were constants too) all we
  896. // need to write is the format ID
  897. case -1:
  898. case -2:
  899. // Write the format tag to the stream
  900. if (FAILED(hr = StmWrite.Write(&dwFormatTag, sizeof(dwFormatTag))))
  901. {
  902. return hr;
  903. }
  904. return(StmWrite.Write(&ulFormatID, sizeof(ulFormatID)));
  905. // In all other cases, we need to write the string raw with termination
  906. // (ie: the format tag we've already written was the length).
  907. default:
  908. if (TT_ANSI == ttText)
  909. {
  910. char szClipName[MAX_CFNAME];
  911. int cbLen = SSGetClipboardFormatNameA(ulFormatID, szClipName, MAX_CFNAME);
  912. if (cbLen == 0)
  913. {
  914. return HRESULT_FROM_WIN32(GetLastError());
  915. }
  916. cbLen++; // Account for NULL terminator
  917. szClipName[cbLen] = '\0';
  918. // Write the format tag to the stream
  919. if (FAILED(hr = StmWrite.Write(&cbLen, sizeof(cbLen))))
  920. {
  921. return hr;
  922. }
  923. return (StmWrite.Write(szClipName, cbLen));
  924. }
  925. else
  926. {
  927. OLECHAR wszClipName[MAX_CFNAME];
  928. int ccLen = GetClipboardFormatName(ulFormatID, wszClipName, MAX_CFNAME);
  929. if (ccLen == 0)
  930. {
  931. return HRESULT_FROM_WIN32(GetLastError());
  932. }
  933. ccLen++; // Account for NULL terminator
  934. wszClipName[ccLen] = OLESTR('\0');
  935. // Write the format tag to the stream
  936. if (FAILED(hr = StmWrite.Write(&ccLen, sizeof(ccLen))))
  937. {
  938. return hr;
  939. }
  940. return (StmWrite.Write(wszClipName, ccLen*sizeof(OLECHAR)));
  941. }
  942. } // end switch()
  943. }
  944. //+-------------------------------------------------------------------------
  945. //
  946. // Function: ANSIStrToStm, PRIVATE INTERNAL
  947. //
  948. // Synopsis: Writes an ANSI string out to a stream, preceded by a ULONG
  949. // indicating its length (INCLUDING TERMINATOR). If the
  950. // string is 0-length, or a NULL ptr is passed in, just
  951. // the length (0) is written, and no blank string is stored
  952. // in the stream.
  953. //
  954. // Arguments: [pstm] -- the stream to write to
  955. // [str] -- the string to write
  956. //
  957. //
  958. //
  959. //
  960. // Returns: NOERROR on success
  961. // E_OUTOFMEMORY on allocation failure
  962. //
  963. // History: dd-mmm-yy Author Comment
  964. // 08-Mar-94 davepl Created
  965. //
  966. //--------------------------------------------------------------------------
  967. INTERNAL ANSIStrToStm(CStmBufWrite & StmWrite, LPCSTR str)
  968. {
  969. VDATEHEAP();
  970. HRESULT hr;
  971. ULONG ulDummy = 0;
  972. ULONG ulLen;
  973. // If the pointer is NULL or if it is valid but points to
  974. // a 0-length string, _just_ write the 0-length, but no
  975. // string.
  976. if (NULL == str || (ulLen = strlen(str) + 1) == 1)
  977. {
  978. return(StmWrite.Write(&ulDummy, sizeof(ulDummy)));
  979. }
  980. if (FAILED(hr = StmWrite.Write(&ulLen, sizeof(ulLen))))
  981. {
  982. return(hr);
  983. }
  984. return StmWrite.Write(str, ulLen);
  985. }
  986. //+-------------------------------------------------------------------------
  987. //
  988. // Function: ANSIStmToStr, PRIVATE INTERNAL
  989. //
  990. // Synopsis: Reads a string from a stream, which is preceded by a ULONG
  991. // giving its length. If the string OUT parameter is NULL,
  992. // the string is read but not returned. If the parameter is
  993. // a valid pointer, memory is allocated on it to hold the str.
  994. //
  995. // Arguments: [pstm] -- the stream to write to
  996. // [pstr] -- the caller's string pointer
  997. //
  998. // Returns: NOERROR on success
  999. // E_OUTOFMEMORY on allocation failure
  1000. //
  1001. // History: dd-mmm-yy Author Comment
  1002. // 08-Mar-94 davepl Created
  1003. //
  1004. //--------------------------------------------------------------------------
  1005. INTERNAL ANSIStmToStr(CStmBufRead & StmRead, LPSTR * pstr, ULONG * pulLen)
  1006. {
  1007. VDATEHEAP();
  1008. LPSTR szTmp = NULL;
  1009. ULONG ulTmp;
  1010. HRESULT hr;
  1011. if (pstr)
  1012. {
  1013. VDATEPTROUT(pstr, LPSTR);
  1014. *pstr = NULL;
  1015. }
  1016. if (pulLen)
  1017. {
  1018. VDATEPTROUT(pulLen, ULONG *);
  1019. *pulLen = 0;
  1020. }
  1021. // Find out how many bytes are to follow as a string
  1022. if (FAILED(hr = StmRead.Read(&ulTmp, sizeof(ulTmp))))
  1023. {
  1024. return(hr);
  1025. }
  1026. // If none, we can just return now
  1027. if (0 == ulTmp)
  1028. {
  1029. return(NOERROR);
  1030. }
  1031. if (pulLen)
  1032. {
  1033. *pulLen = ulTmp;
  1034. }
  1035. // Allocate a buffer to read the string into
  1036. szTmp = (LPSTR) PubMemAlloc(ulTmp);
  1037. if (NULL == szTmp)
  1038. {
  1039. return ResultFromScode(E_OUTOFMEMORY);
  1040. }
  1041. if (FAILED(hr = StmRead.Read(szTmp, ulTmp)))
  1042. {
  1043. PubMemFree(szTmp);
  1044. return(hr);
  1045. }
  1046. // If the caller wanted the string, assign it over, otherwise
  1047. // just free it now.
  1048. if (pstr)
  1049. {
  1050. *pstr = szTmp;
  1051. }
  1052. else
  1053. {
  1054. PubMemFree(szTmp);
  1055. }
  1056. return(NOERROR);
  1057. }
  1058. //+-------------------------------------------------------------------------
  1059. //
  1060. // Function: ReadFmtUserTypeStg
  1061. //
  1062. // Synopsis: Read ClipFormat, UserType from CompObj stream
  1063. //
  1064. // Arguments: [pstg] -- storage containing CompObj stream
  1065. // [pcf] -- place holder for clip format, may be NULL
  1066. // [ppszUserType] -- place holder for User Type, may be NULL
  1067. //
  1068. // Returns: If NOERROR, *pcf is clip format and *ppszUserType is User Type
  1069. // If ERROR, *pcf is 0 and *ppszUserType is NULL
  1070. //
  1071. // Modifies:
  1072. //
  1073. // Algorithm:
  1074. //
  1075. // History: ??-???-?? ? Ported
  1076. // 15-Jul-94 AlexT Make sure *pcf & *pszUserType are clear
  1077. // on error
  1078. //
  1079. // Notes:
  1080. //
  1081. //--------------------------------------------------------------------------
  1082. STDAPI ReadFmtUserTypeStg
  1083. ( IStorage * pstg,
  1084. CLIPFORMAT * pcf,
  1085. LPOLESTR * ppszUserType )
  1086. {
  1087. OLETRACEOUTEX((API_ReadFmtUserTypeStg,
  1088. PARAMFMT("pstg= %p, pcf= %p, ppszUserType= %p"),
  1089. pstg, pcf, ppszUserType));
  1090. VDATEHEAP();
  1091. CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IStorage,(IUnknown **)&pstg);
  1092. HRESULT hr;
  1093. CompObjStmData cod;
  1094. do
  1095. {
  1096. // Read the CompObj stream
  1097. hr = ReadCompObjStm(pstg, &cod);
  1098. if (FAILED(hr))
  1099. {
  1100. // clean up and return
  1101. break;
  1102. }
  1103. // Extract the clipboard format
  1104. if (NULL != pcf)
  1105. {
  1106. ULONG ulFormatID = 0;
  1107. DWORD dwFormatTag = 0;
  1108. if (FAILED(hr = GetClipFormat(&cod, &ulFormatID, &dwFormatTag))
  1109. && GetScode(hr) != OLE_S_MAC_CLIPFORMAT)
  1110. {
  1111. // clean up and return
  1112. break;
  1113. }
  1114. *pcf = (CLIPFORMAT) ulFormatID;
  1115. }
  1116. // Extract the User Type
  1117. if (NULL != ppszUserType)
  1118. {
  1119. if (FAILED(hr = GetUNICODEUserType(&cod, ppszUserType)))
  1120. {
  1121. // clean up and return
  1122. break;
  1123. }
  1124. }
  1125. hr = S_OK;
  1126. } while (FALSE);
  1127. if (FAILED(hr))
  1128. {
  1129. // Make sure the out parameters are zeroed out in the failure case
  1130. if (NULL != pcf)
  1131. {
  1132. *pcf = 0;
  1133. }
  1134. if (NULL != ppszUserType)
  1135. {
  1136. *ppszUserType = NULL;
  1137. }
  1138. }
  1139. OLETRACEOUT((API_ReadFmtUserTypeStg, hr));
  1140. return(hr);
  1141. }
  1142. STDAPI ReadFmtProgIdStg
  1143. ( IStorage * pstg,
  1144. LPOLESTR * pszProgID )
  1145. {
  1146. VDATEHEAP();
  1147. HRESULT hr;
  1148. CompObjStmData cod;
  1149. // Read the CompObj stream
  1150. if (FAILED(hr = ReadCompObjStm(pstg, &cod)))
  1151. {
  1152. return(hr);
  1153. }
  1154. // Extract the User Type
  1155. if (pszProgID)
  1156. {
  1157. if (FAILED(hr = GetUNICODEProgID(&cod, pszProgID)))
  1158. {
  1159. return(hr);
  1160. }
  1161. }
  1162. return(NOERROR);
  1163. }
  1164. STDAPI WriteFmtUserTypeStg
  1165. ( LPSTORAGE pstg,
  1166. CLIPFORMAT cf,
  1167. LPOLESTR szUserType)
  1168. {
  1169. OLETRACEIN((API_WriteFmtUserTypeStg, PARAMFMT("pstg= %p, cf= %x, szUserType= %ws"),
  1170. pstg, cf, szUserType));
  1171. VDATEHEAP();
  1172. HRESULT hr;
  1173. CompObjStmData cod;
  1174. CLSID clsid;
  1175. LPOLESTR szProgID = NULL;
  1176. VDATEIFACE_LABEL(pstg, errRtn, hr);
  1177. CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IStorage,(IUnknown **)&pstg);
  1178. // Read the CompObj stream. If it's not there, we don't care,
  1179. // we'll build a new one. Some errors, such as E_OUTOFMEMORY, cannot
  1180. // be overlooked, so we must return them.
  1181. if (FAILED(hr = ReadCompObjStm(pstg, &cod)))
  1182. {
  1183. if (hr == ResultFromScode(E_OUTOFMEMORY))
  1184. {
  1185. goto errRtn;
  1186. }
  1187. }
  1188. // Set the User Type in the Object.
  1189. if (szUserType)
  1190. {
  1191. if (FAILED(hr = PutUNICODEUserType(&cod, szUserType)))
  1192. {
  1193. goto errRtn;
  1194. }
  1195. }
  1196. // Set the ProgID field
  1197. if (ReadClassStg(pstg, &clsid) != NOERROR)
  1198. {
  1199. clsid = CLSID_NULL;
  1200. }
  1201. if (SUCCEEDED(ProgIDFromCLSID (clsid, &szProgID)))
  1202. {
  1203. PutUNICODEProgID(&cod, szProgID);
  1204. }
  1205. if (szProgID)
  1206. {
  1207. PubMemFree(szProgID);
  1208. }
  1209. // Set the clipboard format. 0xC000 is a magical constant which
  1210. // bounds the standard clipboard format type IDs
  1211. if (cf < 0xC000)
  1212. {
  1213. if (0 == cf)
  1214. {
  1215. PutClipFormat(&cod, 0, 0); // NULL format
  1216. }
  1217. else
  1218. {
  1219. PutClipFormat(&cod, (DWORD)-1, cf); // Standard format
  1220. }
  1221. }
  1222. else
  1223. {
  1224. PutClipFormat(&cod, MAX_CFNAME, cf); // Custom format
  1225. }
  1226. // Now we have all the info in the CompObjData object.
  1227. // Now we can write it out to the stream as a big atomic object.
  1228. if (FAILED(hr = WriteCompObjStm(pstg, &cod)))
  1229. {
  1230. if (hr == ResultFromScode(E_OUTOFMEMORY))
  1231. {
  1232. goto errRtn;
  1233. }
  1234. }
  1235. hr = NOERROR;
  1236. errRtn:
  1237. OLETRACEOUT((API_WriteFmtUserTypeStg, hr));
  1238. return hr;
  1239. }