//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1994. // // File: privstm.cpp // // Contents: Handles all reading/writing of the \1CompObj stream // // Functions: Implements: // // INTERNAL ReadCompObjStm // INTERNAL WriteCompObjStm // INTERNAL ClipfmtToStm // INTERNAL StmToClipfmt // INTERNAL GetUNICODEUserType // INTERNAL GetUNICODEProgID // INTERNAL GetUNICODEClipFormat // INTERNAL PutUNICODEUserType // INTERNAL PutUNICODEProgID // INTERNAL PutUNICODEClipFormat // INTERNAL UtGetUNICODEData // INTERNAL ANSIStrToStm // INTERNAL ANSIStmToStr // // STDAPI WriteFmtUserTypeStg // STDAPI ReadFmtUserTypeStg // STDAPI ReadFmtProgIdStg // // History: dd-mmm-yy Author Comment // 08-Feb-94 davepl Created // // // Notes: The CompObj stream (in 16-bit OLE) contained fields for // the ClassID, UserType, Clipboard format, and (in later // versions only) ProgID. These were always written in ANSI // format. // // The file format has been extended such that ANSI data is // written to the stream in much the same way as before. The // key difference is that in the event the internal UNICODE // versions of this data cannot be losslessly converted to // ANSI, the ANSI version is written as a NULL string, and // the UNICODE version follows at the end of the stream. This // way, 16-bit apps see as much of what they expect as possible, // and 32-bit apps can write UNICODE transparently in a // backwards-compatible way. // // The file format of the stream is: // // (A) WORD Byte Order // WORD Format Version // DWORD Original OS ver Always Windows 3.1 // DWORD -1 // CLSID Class ID // ULONG Length of UserType // User Type string ANSI // Clipformat ANSI (when using string tag) // ---------------------------- // (B) ULONG Length of Prog ID // Prog ID ANSI (not always present) // ---------------------------- // (C) ULONG Magic Number Signified UNICODE data present // ULONG Length of UserType // ULONG User Type string UNICODE // Clipformat UNICODE (when tag is string) // ULONG Length of Prog ID // Prog ID UNICODE // // Section (A) is always present. Section (B) is present when // stream has been written by a later 16-bit app or by a // 32-bit app. Section (C) is present when written by a // 32-bit app. // // If a string is present in UNICODE, the ANSI version will be // NULL (a zero for length and _no_ data). When the // UNICODE section is present, strings that were not needed // because the ANSI conversion was successful are written // as NULL (again, zero len and no data). // // A NULL clipboard format is written as a 0 tag. // // In order to read any field, the entire string is read into // an internal object, and the fields are extracted individually. // In order to write an fields, the stream is read into the // object (if possible), the fields updated, and then rewritten // as an atomic object. // //-------------------------------------------------------------------------- #include static const ULONG COMP_OBJ_MAGIC_NUMBER = 0x71B239F4; #define MAX_CFNAME 400 // Maximum size of a clipformat name // (my choice, none documented) const DWORD gdwFirstDword = (DWORD)MAKELONG(COMPOBJ_STREAM_VERSION, BYTE_ORDER_INDICATOR); enum TXTTYPE { TT_UNICODE = 0, TT_ANSI = 1 }; // This is the data object into which the stream is read prior to // extracting fields. struct CompObjHdr // The leading data in the CompObj stream { DWORD m_dwFirstDword; // First DWORD, byte order and format ver DWORD m_dwOSVer; // Originating OS Ver (eg: Win31) DWORD m_unused; // Always a -1L in the stream CLSID m_clsClass; // Class ID of this object }; class CompObjStmData : public CPrivAlloc { public: CompObjHdr m_hdr; ULONG m_cchUserType; // Number of CHARACTERS in UserType ULONG m_cchProgID; // Number of CHARACTERS in ProgID DWORD m_dwFormatTag; // Clipformat type (none, string, clip, etc) ULONG m_ulFormatID; // If tag is std clipformat, what type? LPOLESTR m_pszOUserType; // Pointer to OLESTR UserType LPOLESTR m_pszOProgID; // Pointer to OLESTR ProgID LPSTR m_pszAUserType; // Pointer to ANSI UserType LPSTR m_pszAProgID; // Pointer to ANSI ProgID TXTTYPE ttClipString; // Format needed for the clipformat string CompObjStmData(void) { memset(this, 0, sizeof(CompObjStmData)); ttClipString = TT_ANSI; // By default, use ANSI Clipformat }; ~CompObjStmData(void) { PubMemFree(m_pszOUserType); PubMemFree(m_pszOProgID); PubMemFree(m_pszAUserType); PubMemFree(m_pszAProgID); }; }; // Prototypes for fns declared in this file INTERNAL ReadCompObjStm (IStorage *, CompObjStmData *); INTERNAL WriteCompObjStm (IStorage *, CompObjStmData *); INTERNAL ClipfmtToStm (CStmBufWrite &, ULONG, ULONG, TXTTYPE); INTERNAL StmToClipfmt (CStmBufRead &, DWORD *, DWORD *, TXTTYPE); INTERNAL GetUNICODEUserType (CompObjStmData *, LPOLESTR *); INTERNAL GetUNICODEProgID (CompObjStmData *, LPOLESTR *); INTERNAL GetClipFormat (CompObjStmData *, DWORD *, DWORD *); INTERNAL PutUNICODEUserType (CompObjStmData *, LPOLESTR); INTERNAL PutUNICODEProgID (CompObjStmData *, LPOLESTR); INTERNAL PutClipFormat (CompObjStmData *, DWORD, DWORD); INTERNAL ANSIStrToStm (CStmBufWrite &, LPCSTR); INTERNAL ANSIStmToStr (CStmBufRead & StmRead, LPSTR * pstr, ULONG *); STDAPI WriteFmtUserTypeStg (IStorage *, CLIPFORMAT, LPOLESTR); STDAPI ReadFmtUserTypeStg (IStorage *, CLIPFORMAT *, LPOLESTR *); STDAPI ReadFmtProgIdStg (IStorage *, LPOLESTR *); //+------------------------------------------------------------------------- // // Function: ReadCompObjStm, PRIVATE INTERNAL // // Synopsis: Reads the \1CompObj stream into an internal data structure // that will contain the best-case representation of that // stream (ie: ANSI where possible, UNICODE where needed). // // Effects: Reads ANSI data where available. At end of standard ANSI // data, looks for ANSI ProgID field. If found, looks for // MagicNumber indicating UNICODE data is to follow. If this // matches, UNICODE strings are pulled from the stream. They // should only be found where the ANSI version was NULL // (because it could not be converted from UNICODE). // // Capable of reading 3 stream formats seamlessly: // - Original ANSI sans ProgID field // - Extended OLE 2.01 version with ProgID // - Extended OLE 2/32 version with ProgID and UNICODE extensions // // Arguments: [pstg] -- ptr to IStorage to read from // [pcod] -- ptr to already-allocated CompObjData object // // Returns: NOERROR on success // INVALIDARG on missing pcod // Various I/O on stream missing, read errors, etc // E_OUTOFMEMORY on any allocation failure // // History: dd-mmm-yy Author Comment // 08-Mar-94 davepl Created // // Notes: Any memory allocated herein will be allocated on // pointers in the pcod object, which will be freed by its // destructor when it exits scope or is deleted explicitly. // //-------------------------------------------------------------------------- INTERNAL ReadCompObjStm(IStorage * pstg, CompObjStmData * pcod) { VDATEHEAP(); HRESULT hr; // Result code const ULONG RESERVED = 0; // For reserved parameters ULONG ulSize = 0; // Holder for length of ProgID string BOOL fExtStm = 1; // Could this be ext with UNICODE? CStmBufRead StmRead; Win4Assert(pcod); // Validate the pstg interface VDATEIFACE(pstg); // Open the CompObj stream if (FAILED(hr = StmRead.OpenStream(pstg, COMPOBJ_STREAM))) // L"\1CompObj" { goto errRtn; } // Read the header from the CompObj stream: // // WORD Byte Order Indicator 02 bytes // WORD Format version 02 bytes // DWORD Originating OS version 04 bytes // DWORD -1 04 bytes // CLSID Class ID 16 bytes // -------- // 28 bytes == sizeof(dwBuf) Win4Assert(sizeof(CompObjHdr) == 28 && "Warning: possible packing error in CompObjHdr struct"); hr = StmRead.Read(&pcod->m_hdr, sizeof(CompObjHdr)); if (FAILED(hr)) { goto errRtn; } // NB: There used to be a check against the OS version here, // but since the version number has been forced to always // be written as Win3.1, checking it would be redundant. // Win4Assert(pcod->m_hdr.m_dwOSVer == 0x00000a03); #if DBG==1 if (pcod->m_hdr.m_dwOSVer != 0x00000a03) { LEDebugOut((DEB_WARN, "ReadCompObjStm found unexpected OSVer %lx", pcod->m_hdr.m_dwOSVer)); } #endif // Get the User type string from the stream (ANSI FORMAT!) if (FAILED(hr = ANSIStmToStr(StmRead, &pcod->m_pszAUserType, &pcod->m_cchUserType))) { goto errRtn; } // Get the clipboard format data from the stream if (FAILED(hr = StmToClipfmt(StmRead, // Stream to read from &pcod->m_dwFormatTag, // DWORD clip format &pcod->m_ulFormatID, // DWORD clip type TT_ANSI))) // Use ANSI { goto errRtn; } // We have to special-case the ProgID field, because it may not // be present in objects written by early (pre-2.01) versions // of OLE. We only continue when ProgID can be found, but // its absence is not an error, so return what we have so far. hr = StmRead.Read(&ulSize, sizeof(ULONG)); if (FAILED(hr)) { // We were unable to read the size field; make sure ulSize is 0 ulSize = 0; } // The ProgID can be no longer than 39 chars plus a NULL. Other // numbers likely indicate garbage. if (ulSize > 40 || 0 == ulSize) { #if DBG==1 if (ulSize > 40) { LEDebugOut((DEB_WARN,"ReadCompObjStm: ulSize > 40 for ProgID\n")); } #endif fExtStm = 0; // No ProgID implies no UNICODE to follow } // If it looks like we have a hope of findind the ProgID and maybe // even UNICODE, try to fetch the ProdID if (fExtStm) { // Allocate memory for string on our ProgID pointer pcod->m_pszAProgID = (char *) PubMemAlloc(ulSize); if (NULL == pcod->m_pszAProgID) { hr = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } if (FAILED(hr = StmRead.Read(pcod->m_pszAProgID, ulSize))) { // OK, we give up on ProgID and the UNICODE, but that's // _not_ reason to fail, since ProgID could just be missing pcod->m_cchProgID = 0; PubMemFree(pcod->m_pszAProgID); pcod->m_pszAProgID = NULL; fExtStm = 0; } else { // We managed to get ProgID from the stream, so set the // length in pcod and go looking for the UNICODE... pcod->m_cchProgID = ulSize; } } // See if we can find the Magic number DWORD dwMagic; if (fExtStm) { if (FAILED(StmRead.Read(&dwMagic, sizeof(dwMagic)))) { fExtStm = 0; } } if (fExtStm && dwMagic != COMP_OBJ_MAGIC_NUMBER) { fExtStm = 0; } // If fExtStm is still TRUE, we go ahead and read the UNICODE if (fExtStm) { // Get the UNICODE version of the user type if (FAILED(hr = ReadStringStream(StmRead, &pcod->m_pszOUserType))) { goto errRtn; } // Get the clipboard format (UNICODE) DWORD dwFormatTag; ULONG ulFormatID; if (FAILED(hr = StmToClipfmt(StmRead, // Stream to read from &dwFormatTag, // DWORD clip format &ulFormatID, // DWORD clip type TT_UNICODE))) // Use UNICODE { goto errRtn; } // If we found some form of clipboard format, that implies the ANSI // was missing, so set up all of the fields based on this data. if (dwFormatTag) { pcod->m_dwFormatTag = dwFormatTag; pcod->m_ulFormatID = ulFormatID; } // Get the UNICODE version of the ProgID. If there was any UNICODE at // all, we know for sure there is a UNICODE ProgID, so no special casing // as was needed for the ANSI version if (FAILED(hr = ReadStringStream(StmRead, &pcod->m_pszOProgID))) { goto errRtn; } if (pcod->m_pszOProgID) { pcod->m_cchProgID = _xstrlen(pcod->m_pszOProgID) + 1; } } // We successfully read the CompObj stream hr = NOERROR; errRtn: StmRead.Release(); return(hr); } //+------------------------------------------------------------------------- // // Function: StmToClipfmt, PRIVATE INTERNAL // // Synopsis: Reads the clipboard format from the given stream. Caller // specifies whether or not the string format description, // if present, should be expected in ANSI or UNICODE format. // // Effects: If the clipboard format is a length followed by a // string, then the string is read and registered as a // clipboard format (and the new format number is returned). // // Arguments: [lpstream] -- pointer to the stream // [lpdwCf] -- where to put the clipboard format // [lpdTag] -- format type (string, clip, etc) // [ttType] -- text type TT_ANSI or TT_UNICODE // // Returns: hr // // Algorithm: the format of the stream must be one of the following: // // 0 No clipboard format // -1 DWORD predefined windows clipboard format in // the second dword. // -2 DWORD predefined mac clipboard format in the // second dword. This may be obsolete or // irrelevant for us. REVIEW32 // num STRING clipboard format name string (prefaced // by length of string). // // History: dd-mmm-yy Author Comment // 08-Mar-94 davepl Created // //-------------------------------------------------------------------------- INTERNAL StmToClipfmt (CStmBufRead & StmRead, DWORD * lpdTag, DWORD * lpdwCf, TXTTYPE ttText) { VDATEHEAP(); HRESULT hr; DWORD dwValue; VDATEPTROUT(lpdwCf, DWORD); Win4Assert (lpdwCf); // These ptrs are always required Win4Assert (lpdTag); // Read the format type tag from the stream if (FAILED(hr = StmRead.Read(&dwValue, sizeof(DWORD)))) { return hr; } *lpdTag = dwValue; // If the tag is zero, there is no clipboard format info if (dwValue == 0) { *lpdwCf = 0; // NULL cf value } // If it is -1, then it is a standard Windows clipboard format else if (dwValue == -1L) { // Then this is a NON-NULL predefined windows clipformat. // The clipformat values follows if (FAILED(hr = StmRead.Read(&dwValue, sizeof(DWORD)))) { return hr; } *lpdwCf = dwValue; } // If it is -2, it is a MAC format else if (dwValue == -2L) { // Then this is a NON-NULL MAC clipboard format. // The clipformat value follows. For MAC the CLIPFORMAT // is 4 bytes if (FAILED(hr = StmRead.Read(&dwValue, sizeof(DWORD)))) { return hr; } *lpdwCf = dwValue; return ResultFromScode(OLE_S_MAC_CLIPFORMAT); } // Anything but a 0, -1, or -2 indicates a string is to follow, and the // DWORD we already read is the length of the that string else { // Allocate enough memory for whatever type of string it is // we expect to find, and read the string if (dwValue > MAX_CFNAME) { return ResultFromScode(DV_E_CLIPFORMAT); } if (TT_ANSI == ttText) // READ ANSI { char szCf[MAX_CFNAME]; if (FAILED(hr = StmRead.Read(szCf, dwValue))) { return hr; } // Try to register the clipboard format and return the result // (Note: must explicitly call ANSI version) if (((*lpdwCf = (DWORD) SSRegisterClipboardFormatA(szCf))) == 0) { return ResultFromScode(DV_E_CLIPFORMAT); } } else // READ UNICODE { OLECHAR wszCf[MAX_CFNAME]; Win4Assert(dwValue < MAX_CFNAME); if (FAILED(hr=StmRead.Read(wszCf, dwValue * sizeof(OLECHAR)))) { return hr; } // Try to register the clipboard format and return the result if (((*lpdwCf = (DWORD) RegisterClipboardFormat(wszCf))) == 0) { return ResultFromScode(DV_E_CLIPFORMAT); } } } return NOERROR; } //+------------------------------------------------------------------------- // // Function: GetUNICODEUserType, PRIVATE INTERNAL // // Synopsis: Given a CompObjStmData object, returns the User Type // in UNICODE format, converting the ANSI rep as required. // // Effects: Allocates memory on the caller's ptr to hold the string // // Arguments: [pcod] -- The CompObjStmData object // [pstr] -- Pointer to allocate resultant string on // // Returns: NOERROR on success // E_OUTOFMEMORY on allocation failure // // History: dd-mmm-yy Author Comment // 08-Mar-94 davepl Created // //-------------------------------------------------------------------------- INTERNAL GetUNICODEUserType ( CompObjStmData * pcod, LPOLESTR * pstr ) { VDATEHEAP(); HRESULT hr = NOERROR; // Validate and NULL the OUT parameter, or return if none given if (pstr) { VDATEPTROUT(pstr, LPOLESTR); *pstr = NULL; } else { return(NOERROR); } // Either get the UNICODE string, or convert the ANSI version and // get it as UNICODE. if (pcod->m_cchUserType) { hr = UtGetUNICODEData( pcod->m_cchUserType, pcod->m_pszAUserType, pcod->m_pszOUserType, pstr ); } return hr; } //+------------------------------------------------------------------------- // // Function: GetUNICODEProgID, PRIVATE INTERNAL // // Synopsis: Given a CompObjStmData object, returns the ProgID string // in UNICODE format, converting the ANSI rep as required. // // Effects: Allocates memory on the caller's ptr to hold the string // // Arguments: [pcod] -- The CompObjStmData object // [pstr] -- Pointer to allocate resultant string on // // Returns: NOERROR on success // E_OUTOFMEMORY on allocation failure // // History: dd-mmm-yy Author Comment // 08-Mar-94 davepl Created // //-------------------------------------------------------------------------- INTERNAL GetUNICODEProgID ( CompObjStmData * pcod, LPOLESTR * pstr ) { VDATEHEAP(); HRESULT hr = NOERROR; // Validate and NULL the OUT parameter, or return if none given if (pstr) { VDATEPTROUT(pstr, LPOLESTR); *pstr = NULL; } else { return(NOERROR); } // Either get the UNICODE string, or convert the ANSI version and // get it as UNICODE. if (pcod->m_cchProgID) { hr = UtGetUNICODEData( pcod->m_cchProgID, pcod->m_pszAProgID, pcod->m_pszOProgID, pstr ); } return hr; } //+------------------------------------------------------------------------- // // Function: GetClipFormat, PRIVATE INTERNAL // // Synopsis: Given a CompObjStmData object, extracts the clipboard format // type (none, standard, string). // // Effects: If string type, memory is allocated on the caller's ptr // // Arguments: [pcod] -- The CompObjStmData object to extract from // [pdwFormatID] -- Tag type OUT parameter // [pdwFormatTag] -- Tag OUT parameter // // Returns: NOERROR on success // E_OUTOFMEMORY on allocation failures // OLE_S_MAC_CLIPFORMAT as a warning that a MAC fmt is returned // // History: dd-mmm-yy Author Comment // 08-Mar-94 davepl Created // //-------------------------------------------------------------------------- INTERNAL GetClipFormat ( CompObjStmData * pcod, DWORD * pdwFormatID, DWORD * pdwFormatTag ) { VDATEHEAP(); *pdwFormatTag = (DWORD) pcod->m_dwFormatTag; *pdwFormatID = pcod->m_ulFormatID; return NOERROR; } //+------------------------------------------------------------------------- // // Function: PutUNICODEUserType, PRIVATE INTERNAL // // Synopsis: Given a UNICODE string, stores it in the CompObjDataStm // object in ANSI if possible. If the UNICODE -> ANSI // conversion is not possible, it is stored in the object // in UNICODE. // // Notes: Input string is duplicated, so it adds no references // to the string passed in. // // Arguments: [pcod] -- The CompObjDataStm object // [szUser] -- The UNICODE UserType string // // Returns: NOERROR on success // E_OUTOFMEMORY on allocation failure // // History: dd-mmm-yy Author Comment // 08-Mar-94 davepl Created // //-------------------------------------------------------------------------- INTERNAL PutUNICODEUserType(CompObjStmData * pcod, LPOLESTR szUser) { VDATEHEAP(); HRESULT hr; // If no string supplied, clear UserType fields, otherwise // if it can be converted to ANSI, store it an ANSI. Last // resort, store it as UNICODE. if (NULL == szUser) { pcod->m_cchUserType = 0; PubMemFree(pcod->m_pszAUserType); PubMemFree(pcod->m_pszOUserType); pcod->m_pszAUserType = NULL; pcod->m_pszOUserType = NULL; } else { if (FAILED(hr = UtPutUNICODEData( _xstrlen(szUser)+1, szUser, &pcod->m_pszAUserType, &pcod->m_pszOUserType, &pcod->m_cchUserType ))) { return(hr); } } return(NOERROR); } //+------------------------------------------------------------------------- // // Function: PutUNICODEProgID, PRIVATE INTERNAL // // Synopsis: Given a UNICODE string, stores it in the CompObjDataStm // object in ANSI if possible. If the UNICODE -> ANSI // conversion is not possible, it is stored in the object // in UNICODE. // // Notes: Input string is duplicated, so it adds no references // to the string passed in. // // Arguments: [pcod] -- The CompObjDataStm object // [szProg] -- The UNICODE ProgID string // // Returns: NOERROR on success // E_OUTOFMEMORY on allocation failure // // History: dd-mmm-yy Author Comment // 08-Mar-94 davepl Created // //-------------------------------------------------------------------------- INTERNAL PutUNICODEProgID(CompObjStmData * pcod, LPOLESTR szProg) { VDATEHEAP(); HRESULT hr; // If no string supplied, clear ProgID fields, otherwise // if it can be converted to ANSI, store it an ANSI. Last // resort, store it as UNICODE. if (NULL == szProg) { pcod->m_cchProgID = 0; PubMemFree(pcod->m_pszAProgID); PubMemFree(pcod->m_pszOProgID); pcod->m_pszAProgID = NULL; pcod->m_pszOProgID = NULL; } else { if (FAILED(hr = UtPutUNICODEData( _xstrlen(szProg)+1, szProg, &pcod->m_pszAProgID, &pcod->m_pszOProgID, &pcod->m_cchProgID ))) { return(hr); } } return(NOERROR); } //+------------------------------------------------------------------------- // // Function: PutClipFormat // // Synopsis: Stores the clipformat in the internal data structure // // Effects: Input string is duplicated as required, so no references are // kept by this function. // // Arguments: [pcod] -- The CompObjStmData object // [dwFormatTag] -- Format tag (string, clipboard, none) // [ulFormatID] -- If format tag is clipboard, what format // // Returns: NOERROR on success // // History: dd-mmm-yy Author Comment // 08-Mar-94 davepl Created // //-------------------------------------------------------------------------- INTERNAL PutClipFormat ( CompObjStmData * pcod, DWORD dwFormatTag, ULONG ulFormatID ) { VDATEHEAP(); pcod->m_dwFormatTag = (ULONG) dwFormatTag; pcod->m_ulFormatID = ulFormatID; return NOERROR; } //+------------------------------------------------------------------------- // // Function: WriteCompObjStm, PRIVATE INTERNAL // // Synopsis: Writes CompObjStmData object to the CompObj stream in // the IStorage provided. // // First the ANSI fields are written (including the ProgID), // followed by a MagicNumber, followed by whatever OLESTR // versions were required because ANSI fields could not be // converted. // // Destroys any existing CompObj stream! // // Arguments: [pstg] -- The IStorage to write the stream to // [pcod] -- The CompObjStmData object to write out // // Returns: NOERROR on success // E_OUTOFMEMORY on allocation failure // Various I/O on stream failures // // History: dd-mmm-yy Author Comment // 08-Mar-94 davepl Created // //-------------------------------------------------------------------------- INTERNAL WriteCompObjStm(IStorage * pstg, CompObjStmData * pcod) { VDATEHEAP(); HRESULT hr = NOERROR; const ULONG RESERVED = 0; const ULONG ulMagic = COMP_OBJ_MAGIC_NUMBER; CStmBufWrite StmWrite; // The CompObjStmData parameter must be supplied if (NULL == pcod) { return ResultFromScode(E_INVALIDARG); } VDATEIFACE(pstg); // Open the CompObj stm for writing (and overwrite it if // if already exists, which is why we _don't_ specify the // STGM_FAILIFTHERE flag) if (FAILED(hr = StmWrite.CreateStream(pstg, COMPOBJ_STREAM))) { goto errRtn; } // Set up the header pcod->m_hdr.m_dwFirstDword = gdwFirstDword; // The OSVer _must_ be Win 3.10 (0a03), since the old DLL will bail if // it finds anything else. pcod->m_hdr.m_dwOSVer = 0x00000a03; // gdwOrgOSVersion; pcod->m_hdr.m_unused = (DWORD) -1; if (ReadClassStg(pstg, &pcod->m_hdr.m_clsClass) != NOERROR) { pcod->m_hdr.m_clsClass = CLSID_NULL; } // Write the CompObj stream header Win4Assert(sizeof(CompObjHdr) == 28 && "Warning: possible packing error in CompObjHdr struct"); if (FAILED(hr = StmWrite.Write(pcod, sizeof(CompObjHdr)))) { goto errRtn; } // Write the ANSI UserType if (FAILED(hr = ANSIStrToStm(StmWrite, pcod->m_pszAUserType))) { goto errRtn; } if (TT_ANSI == pcod->ttClipString) { if (FAILED(hr = ClipfmtToStm(StmWrite, // the stream pcod->m_dwFormatTag, // format tag pcod->m_ulFormatID, // format ID TT_ANSI)))// TRUE==use ANSI { goto errRtn; } } else { const ULONG ulDummy = 0; if (FAILED(hr = StmWrite.Write(&ulDummy, sizeof(ULONG)))) { goto errRtn; } } // Write the ANSI ProgID if (FAILED(hr = ANSIStrToStm(StmWrite, pcod->m_pszAProgID))) { goto errRtn; } // Write the Magic Number if (FAILED(hr = StmWrite.Write(&ulMagic, sizeof(ULONG)))) { goto errRtn; } // Write the OLESTR version of UserType if (FAILED(hr = WriteStringStream(StmWrite, pcod->m_pszOUserType))) { goto errRtn; } // If we have to write a UNICODE clipformat string, do it now. If // ANSI was sufficient, just write a 0 to the stream here. if (TT_UNICODE == pcod->ttClipString) { if (FAILED(hr = ClipfmtToStm(StmWrite, // the stream pcod->m_dwFormatTag, // format tag pcod->m_ulFormatID, // format ID TT_UNICODE))) // FALSE==use UNICODE { goto errRtn; } } else { const ULONG ulDummy = 0; if (FAILED(hr = StmWrite.Write(&ulDummy, sizeof(ULONG)))) { goto errRtn; } } // Write the OLESTR version of ProgID if (FAILED(hr = WriteStringStream(StmWrite, pcod->m_pszOProgID))) { goto errRtn; } hr = StmWrite.Flush(); // That's it.. clean up and exit errRtn: StmWrite.Release(); return hr; } //+------------------------------------------------------------------------- // // Function: ClipFtmToStm, PRIVATE INTERNAL // // Synopsis: Writes out the clipboard format information at the // current point in the stream. A flag is available // to specify whether or not the string format desc // (if present) is in ANSI or UNICODE format. // // Arguments: [pstm] -- the stream to write to // [dwFormatTag] -- format tag (string, clipfmt, etc) // [ulFormatID] -- if clipfmt, which one // [szClipFormat] -- if string format, the string itself // [ttText] -- text type: TT_ANSI or TT_UNICODE // // Returns: NOERROR on success // E_OUTOFMEMORY on allocation failure // // History: dd-mmm-yy Author Comment // 08-Mar-94 davepl Created // //-------------------------------------------------------------------------- INTERNAL ClipfmtToStm ( CStmBufWrite & StmWrite, DWORD dwFormatTag, ULONG ulFormatID, TXTTYPE ttText ) { VDATEHEAP(); HRESULT hr; const ULONG ulDummy = 0; switch((DWORD)dwFormatTag) { // If the tag is 0, there is no clipboard format info. case 0: if (FAILED(hr = StmWrite.Write(&ulDummy, sizeof(ULONG)))) { return(hr); } return(NOERROR); // In the -1 and -2 cases (yes, I wish there were constants too) all we // need to write is the format ID case -1: case -2: // Write the format tag to the stream if (FAILED(hr = StmWrite.Write(&dwFormatTag, sizeof(dwFormatTag)))) { return hr; } return(StmWrite.Write(&ulFormatID, sizeof(ulFormatID))); // In all other cases, we need to write the string raw with termination // (ie: the format tag we've already written was the length). default: if (TT_ANSI == ttText) { char szClipName[MAX_CFNAME]; int cbLen = SSGetClipboardFormatNameA(ulFormatID, szClipName, MAX_CFNAME); if (cbLen == 0) { return HRESULT_FROM_WIN32(GetLastError()); } cbLen++; // Account for NULL terminator szClipName[cbLen] = '\0'; // Write the format tag to the stream if (FAILED(hr = StmWrite.Write(&cbLen, sizeof(cbLen)))) { return hr; } return (StmWrite.Write(szClipName, cbLen)); } else { OLECHAR wszClipName[MAX_CFNAME]; int ccLen = GetClipboardFormatName(ulFormatID, wszClipName, MAX_CFNAME); if (ccLen == 0) { return HRESULT_FROM_WIN32(GetLastError()); } ccLen++; // Account for NULL terminator wszClipName[ccLen] = OLESTR('\0'); // Write the format tag to the stream if (FAILED(hr = StmWrite.Write(&ccLen, sizeof(ccLen)))) { return hr; } return (StmWrite.Write(wszClipName, ccLen*sizeof(OLECHAR))); } } // end switch() } //+------------------------------------------------------------------------- // // Function: ANSIStrToStm, PRIVATE INTERNAL // // Synopsis: Writes an ANSI string out to a stream, preceded by a ULONG // indicating its length (INCLUDING TERMINATOR). If the // string is 0-length, or a NULL ptr is passed in, just // the length (0) is written, and no blank string is stored // in the stream. // // Arguments: [pstm] -- the stream to write to // [str] -- the string to write // // // // // Returns: NOERROR on success // E_OUTOFMEMORY on allocation failure // // History: dd-mmm-yy Author Comment // 08-Mar-94 davepl Created // //-------------------------------------------------------------------------- INTERNAL ANSIStrToStm(CStmBufWrite & StmWrite, LPCSTR str) { VDATEHEAP(); HRESULT hr; ULONG ulDummy = 0; ULONG ulLen; // If the pointer is NULL or if it is valid but points to // a 0-length string, _just_ write the 0-length, but no // string. if (NULL == str || (ulLen = strlen(str) + 1) == 1) { return(StmWrite.Write(&ulDummy, sizeof(ulDummy))); } if (FAILED(hr = StmWrite.Write(&ulLen, sizeof(ulLen)))) { return(hr); } return StmWrite.Write(str, ulLen); } //+------------------------------------------------------------------------- // // Function: ANSIStmToStr, PRIVATE INTERNAL // // Synopsis: Reads a string from a stream, which is preceded by a ULONG // giving its length. If the string OUT parameter is NULL, // the string is read but not returned. If the parameter is // a valid pointer, memory is allocated on it to hold the str. // // Arguments: [pstm] -- the stream to write to // [pstr] -- the caller's string pointer // // Returns: NOERROR on success // E_OUTOFMEMORY on allocation failure // // History: dd-mmm-yy Author Comment // 08-Mar-94 davepl Created // //-------------------------------------------------------------------------- INTERNAL ANSIStmToStr(CStmBufRead & StmRead, LPSTR * pstr, ULONG * pulLen) { VDATEHEAP(); LPSTR szTmp = NULL; ULONG ulTmp; HRESULT hr; if (pstr) { VDATEPTROUT(pstr, LPSTR); *pstr = NULL; } if (pulLen) { VDATEPTROUT(pulLen, ULONG *); *pulLen = 0; } // Find out how many bytes are to follow as a string if (FAILED(hr = StmRead.Read(&ulTmp, sizeof(ulTmp)))) { return(hr); } // If none, we can just return now if (0 == ulTmp) { return(NOERROR); } if (pulLen) { *pulLen = ulTmp; } // Allocate a buffer to read the string into szTmp = (LPSTR) PubMemAlloc(ulTmp); if (NULL == szTmp) { return ResultFromScode(E_OUTOFMEMORY); } if (FAILED(hr = StmRead.Read(szTmp, ulTmp))) { PubMemFree(szTmp); return(hr); } // If the caller wanted the string, assign it over, otherwise // just free it now. if (pstr) { *pstr = szTmp; } else { PubMemFree(szTmp); } return(NOERROR); } //+------------------------------------------------------------------------- // // Function: ReadFmtUserTypeStg // // Synopsis: Read ClipFormat, UserType from CompObj stream // // Arguments: [pstg] -- storage containing CompObj stream // [pcf] -- place holder for clip format, may be NULL // [ppszUserType] -- place holder for User Type, may be NULL // // Returns: If NOERROR, *pcf is clip format and *ppszUserType is User Type // If ERROR, *pcf is 0 and *ppszUserType is NULL // // Modifies: // // Algorithm: // // History: ??-???-?? ? Ported // 15-Jul-94 AlexT Make sure *pcf & *pszUserType are clear // on error // // Notes: // //-------------------------------------------------------------------------- STDAPI ReadFmtUserTypeStg ( IStorage * pstg, CLIPFORMAT * pcf, LPOLESTR * ppszUserType ) { OLETRACEOUTEX((API_ReadFmtUserTypeStg, PARAMFMT("pstg= %p, pcf= %p, ppszUserType= %p"), pstg, pcf, ppszUserType)); VDATEHEAP(); CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IStorage,(IUnknown **)&pstg); HRESULT hr; CompObjStmData cod; do { // Read the CompObj stream hr = ReadCompObjStm(pstg, &cod); if (FAILED(hr)) { // clean up and return break; } // Extract the clipboard format if (NULL != pcf) { ULONG ulFormatID = 0; DWORD dwFormatTag = 0; if (FAILED(hr = GetClipFormat(&cod, &ulFormatID, &dwFormatTag)) && GetScode(hr) != OLE_S_MAC_CLIPFORMAT) { // clean up and return break; } *pcf = (CLIPFORMAT) ulFormatID; } // Extract the User Type if (NULL != ppszUserType) { if (FAILED(hr = GetUNICODEUserType(&cod, ppszUserType))) { // clean up and return break; } } hr = S_OK; } while (FALSE); if (FAILED(hr)) { // Make sure the out parameters are zeroed out in the failure case if (NULL != pcf) { *pcf = 0; } if (NULL != ppszUserType) { *ppszUserType = NULL; } } OLETRACEOUT((API_ReadFmtUserTypeStg, hr)); return(hr); } STDAPI ReadFmtProgIdStg ( IStorage * pstg, LPOLESTR * pszProgID ) { VDATEHEAP(); HRESULT hr; CompObjStmData cod; // Read the CompObj stream if (FAILED(hr = ReadCompObjStm(pstg, &cod))) { return(hr); } // Extract the User Type if (pszProgID) { if (FAILED(hr = GetUNICODEProgID(&cod, pszProgID))) { return(hr); } } return(NOERROR); } STDAPI WriteFmtUserTypeStg ( LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR szUserType) { OLETRACEIN((API_WriteFmtUserTypeStg, PARAMFMT("pstg= %p, cf= %x, szUserType= %ws"), pstg, cf, szUserType)); VDATEHEAP(); HRESULT hr; CompObjStmData cod; CLSID clsid; LPOLESTR szProgID = NULL; VDATEIFACE_LABEL(pstg, errRtn, hr); CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IStorage,(IUnknown **)&pstg); // Read the CompObj stream. If it's not there, we don't care, // we'll build a new one. Some errors, such as E_OUTOFMEMORY, cannot // be overlooked, so we must return them. if (FAILED(hr = ReadCompObjStm(pstg, &cod))) { if (hr == ResultFromScode(E_OUTOFMEMORY)) { goto errRtn; } } // Set the User Type in the Object. if (szUserType) { if (FAILED(hr = PutUNICODEUserType(&cod, szUserType))) { goto errRtn; } } // Set the ProgID field if (ReadClassStg(pstg, &clsid) != NOERROR) { clsid = CLSID_NULL; } if (SUCCEEDED(ProgIDFromCLSID (clsid, &szProgID))) { PutUNICODEProgID(&cod, szProgID); } if (szProgID) { PubMemFree(szProgID); } // Set the clipboard format. 0xC000 is a magical constant which // bounds the standard clipboard format type IDs if (cf < 0xC000) { if (0 == cf) { PutClipFormat(&cod, 0, 0); // NULL format } else { PutClipFormat(&cod, (DWORD)-1, cf); // Standard format } } else { PutClipFormat(&cod, MAX_CFNAME, cf); // Custom format } // Now we have all the info in the CompObjData object. // Now we can write it out to the stream as a big atomic object. if (FAILED(hr = WriteCompObjStm(pstg, &cod))) { if (hr == ResultFromScode(E_OUTOFMEMORY)) { goto errRtn; } } hr = NOERROR; errRtn: OLETRACEOUT((API_WriteFmtUserTypeStg, hr)); return hr; }