//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1993. // // File: ostm2stg.cpp // // Contents: OLE 1 - OLE 2 Stream/IStorage Interoperatbility // // Functions: Implements API functions: // OleConvertOLESTREAMToIStorage // OleConvertIStorageToOLESTREAM // OleConvertOLESTREAMToIStorageEx // OleConvertIStorageToOLESTREAMEx // // // History: dd-mmm-yy Author Comment // 03-Feb-92 jasonful original version // 08-Aug-93 srinik added Ex functions // 12-Feb-94 davepl 32-bit port // //-------------------------------------------------------------------------- #include #include "ostm2stg.h" #include #include #include #include ASSERTDATA // We need a ptr value which will indicate that the associated handle // is a metafile handle, and therefore cannot be cleaned up as if // it were a normal global memory handle #define METADATAPTR ((void *) -1) // This fn is not prototyped in any include file, since it was static // to its file. Need to add the prototype to a common include file. HRESULT STDAPICALLTYPE CreateOle1FileMoniker(LPWSTR,REFCLSID,LPMONIKER FAR*); // This is defined in the new privstm.cpp; must be added to an include file. STDAPI ReadFmtProgIdStg ( IStorage * pstg, LPOLESTR * pszProgID ); FARINTERNAL wWriteFmtUserType (LPSTORAGE, REFCLSID); //+------------------------------------------------------------------------- // // Member: CGenericObject::CGenericObject // // Synopsis: Constructor for CGenericObject class // // Effects: Initializes all child pointers to NULL and sets // flags to FALSE // // History: dd-mmm-yy Author Comment // 14-Feb-94 davepl Cleanup and document // // Notes: // //-------------------------------------------------------------------------- CGenericObject::CGenericObject(void) { m_ppres = NULL; // Presentation data m_fLink = FALSE; // Flag: Linked (T) or Embedded (F) m_fStatic = FALSE; // Flag: Static object m_fNoBlankPres = FALSE; // Flag: do not want a blank presentation m_szTopic = NULL; // Topic string for this object m_szItem = NULL; // Item (file) string for this object } //+------------------------------------------------------------------------- // // Member: CGenericObject::~CGenericObject // // Synopsis: Desctuctor for CGenericObject class // // Effects: Removes children then self // // History: dd-mmm-yy Author Comment // 12-Aug-94 alexgo check for NULL before delete // 14-Feb-94 davepl Cleanup and document // // Notes: As much as I hated to do it, some of these strings // have to be freed with PubMemFree because they are // allocated by UtDupString, which allocates public memory. // //-------------------------------------------------------------------------- CGenericObject::~CGenericObject (void) { if( m_ppres ) { delete m_ppres; // Presentation data } if( m_szTopic ) { PubMemFree(m_szTopic); // Topic string } if( m_szItem ) { PubMemFree(m_szItem); // Item string } } //+------------------------------------------------------------------------- // // Member: CData::CData // // Synopsis: Constructor for a simple class which holds a piece of // memory. // // Effects: Clears size, flags, and pointer // // History: dd-mmm-yy Author Comment // // Notes: 14-Feb-94 davepl Cleanup and document // //-------------------------------------------------------------------------- CData::CData (void) { m_cbSize = 0; // Count, in bytes, of data size m_h = NULL; // Memory handke m_pv= NULL; // Memory pointer m_fNoFree = FALSE; // Flag: Should memory be freed in destructor } //+------------------------------------------------------------------------- // // Member: CData::~CData // // Synopsis: Destructor for simple data class // // Effects: Unlocks and frees memory if m_fNoFree is not set // // History: dd-mmm-yy Author Comment // 14-Feb-94 davepl Cleanup and document // // Notes: If a metafile handle is stored in the handle, the // pointer will be marked with a special value, indicating // that we must DeleteMetafile, not GlobalFree the handle. // //-------------------------------------------------------------------------- CData::~CData (void) { if (m_h) // Do we have a handle? { if (m_pv == METADATAPTR) { LEVERIFY(DeleteMetaFile((HMETAFILE) m_h)); } else { GlobalUnlock (m_h); // Dec lock count if (!m_fNoFree) // Free this memory if we { // have been flagged to do so LEVERIFY(0==GlobalFree (m_h)); } } } } //+------------------------------------------------------------------------- // // Member: CFormat::CFormat // // Synopsis: CFormat class constructor // // Effects: Initializes format tag and clipboard format // // History: dd-mmm-yy Author Comment // // Notes: 14-Feb-94 davepl Cleanup and document // //-------------------------------------------------------------------------- CFormat::CFormat (void) { m_ftag = ftagNone; // Format tag (string, clipformat, or none) m_cf = 0; // Clipboard format } //+------------------------------------------------------------------------- // // Member: CClass::CClass // // Synopsis: CClass constructor // // Effects: sets class ID and class ID string to NULL // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- CClass::CClass (void) { m_szClsid = NULL; m_clsid = CLSID_NULL; } //+------------------------------------------------------------------------- // // Member: CPres::CPres // // Synopsis: CPres constructor // // Effects: Initializes height & width of presentation data to zero. // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- CPres::CPres (void) { m_ulHeight = 0L; m_ulWidth = 0L; } //+------------------------------------------------------------------------- // // Member: CClass::Set, INTERNAL // // Synopsis: Sets the m_szClsid based on clsid // // Effects: Sets m_szClsid in the following order of preference: // - ProgID obtained from ProgIDFromCLSID() // - If it is a static type, m_szClsid is left blank // - Tries to read it from [pstg] // - Tries to obtain it from registry based on CLSID // // // Arguments: [clsid] - class id object is to be set to // [pstg] - storage which may contain info on the // clipboard format as a last resort // // Returns: NOERROR on success // REGDB_E_CLASSNOTREG unknown class // // History: dd-mmm-yy Author Comment // 16-Feb-94 davepl Cleaned up and documented // // Notes: Hard-coded maximum of 256 character clip format name. // On failure, m_clsid has still been set to clsid. // //-------------------------------------------------------------------------- INTERNAL CClass::Set (REFCLSID clsid, LPSTORAGE pstg) { CLIPFORMAT cf; unsigned short const ccBufSize = 256; LPOLESTR szProgId = NULL; Assert (m_clsid == CLSID_NULL && m_szClsid == NULL); // set the m_clsid member in the object m_clsid = clsid; // If we can get it using ProgIDFromCLSID, that's the simplest case if (NOERROR == wProgIDFromCLSID (clsid, &m_szClsid)) { return NOERROR; } // If not, maybe the object is static, in which case we leave the // class string NULL if (IsEqualCLSID(CLSID_StaticMetafile, clsid) || IsEqualCLSID(CLSID_StaticDib, clsid)) { return NOERROR; } // If still no luck, try to read the clipboard format from the storage // and then look that up. if (pstg && SUCCEEDED(ReadFmtUserTypeStg(pstg, &cf, NULL)) && SUCCEEDED(ReadFmtProgIdStg (pstg, &szProgId))) { // Last-ditch effort. If the class is an unregistered OLE1 class, // the ProgID should still be obtainable from the format tag. // If the class is an unregistered OLE2 class, the ProgId should be // at the end of the CompObj stream. if (CoIsOle1Class(clsid)) { Verify (GetClipboardFormatName (cf, szProgId, ccBufSize)); } else { // If its an OLE 2 object and we couldn't get the program ID from // the storage, we're out of luck if (szProgId == NULL || szProgId[0] == L'\0') { if (szProgId) { PubMemFree(szProgId); } return ResultFromScode (REGDB_E_CLASSNOTREG); } } // At this point we know we have a program ID and that this is an // OLE 2 object, so we use the program ID as the class name. m_szClsid = szProgId; return NOERROR; } else { // If we hit this path, we couldn't read from the storage return ResultFromScode (REGDB_E_CLASSNOTREG); } } //+------------------------------------------------------------------------- // // Member: CClass:SetSz, INTERNAL // // Synopsis: Sets CGenericObject's CLASS member ID based on the class // name passed in. // // History: dd-mmm-yy Author Comment // 15-Feb-94 davepl Cleaned up and documented // //-------------------------------------------------------------------------- INTERNAL CClass::SetSz (LPOLESTR sz) { HRESULT hr; // The class info should be completely unset at this point Assert (m_clsid==CLSID_NULL && m_szClsid==NULL); m_szClsid = sz; if (FAILED(hr = wCLSIDFromProgID (sz, &m_clsid, TRUE))) { return hr; } return NOERROR; } //+------------------------------------------------------------------------- // // Member: CClass::Reset // // Synopsis: Frees the Class ID string for CClass and resets the pointer, // then sets the class ID and string bassed on the CLSID // passed in. // // History: dd-mmm-yy Author Comment // 16-Feb-94 davepl Cleaned up and documented // // Notes: Class ID must already be set before calling RESET // //-------------------------------------------------------------------------- INTERNAL CClass::Reset (REFCLSID clsid) { m_clsid = clsid; // We should already have a class ID string if we are _re_setting it Assert(m_szClsid); PubMemFree(m_szClsid); HRESULT hr; m_szClsid = NULL; if (FAILED(hr = wProgIDFromCLSID (clsid, &m_szClsid))) { return hr; } return NOERROR; } //+------------------------------------------------------------------------- // // Member: CClass::~CClass // // Synopsis: CClass destructor // // History: dd-mmm-yy Author Comment // 12-Aug-94 alexgo check for NULL before free'ing memory // Notes: // //-------------------------------------------------------------------------- CClass::~CClass (void) { // The string is created by UtDupString, so its public memory if( m_szClsid ) { PubMemFree(m_szClsid); } } //+------------------------------------------------------------------------- // // Function: wConvertOLESTREAMToIStorage, INTERNAL // // Synopsis: Worker function. Ensures the OLESTREAM is correctly // set up then calls OLESTREAMToGenericObject. // // History: dd-mmm-yy Author Comment // 15-Feb-94 davepl Cleaned up and documented // Notes: // //-------------------------------------------------------------------------- INTERNAL wConvertOLESTREAMToIStorage( LPOLESTREAM polestream, LPSTORAGE pstg, PGENOBJ pgenobj) { VDATEIFACE (pstg); #if DBG==1 if (!IsValidReadPtrIn (polestream, sizeof(OLESTREAM)) || !IsValidReadPtrIn (polestream->lpstbl, sizeof(OLESTREAMVTBL)) || !IsValidCodePtr ((FARPROC)polestream->lpstbl->Get)) { AssertSz (0, "Bad OLESTREAM"); return ResultFromScode (E_INVALIDARG); } #endif return OLESTREAMToGenericObject (polestream, pgenobj); } //+------------------------------------------------------------------------- // // Function: OleConvertOLESTREAMToIStorage, STDAPI // // Synopsis: Given an OLE 1 stream and an OLE 2 storage, reads an object // from the OLE 1 stream into a CGenericObject. Once read in, // the object is written from generic format back into the OLE 2 // storage object. // // Arguments: [polestream] -- OLE 1 stream to read object from // [pstg] -- OLE 2 storage to write object to // [ptd] -- Target device // // Requires: Streams should be set up, and OLE 1 stream should be // positioned at the beginning of the next OLE 1 object // to be read. // // Returns: [DV_E_DVTARGETDEVICE] Invalid write ptr to target device // CONVERT10_E_OLESTREAM_FMT On unknown OLE 1 format // CONVERT10_E_OLESTREAM_GET On stream read failue // E_OUTOFMEMORY On stream I/O memory failure // // History: dd-mmm-yy Author Comment // 14-Feb-94 davepl Cleanup and documentation // // Notes: // //-------------------------------------------------------------------------- STDAPI OleConvertOLESTREAMToIStorage( LPOLESTREAM polestream, LPSTORAGE pstg, const DVTARGETDEVICE FAR* ptd) { OLETRACEIN((API_OleConvertOLESTREAMToIStorage, PARAMFMT("polestream= %p, pstg= %p, ptd= %td"), polestream, pstg, ptd)); LEDebugOut((DEB_TRACE, "%p _IN OleConvertOLESTREAMToIStorage (" " %p , %p , %p)\n", 0 /*function*/, polestream, pstg, ptd )); CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IStorage,(IUnknown **)&pstg); HRESULT hresult; // This is the generic object we will use as intermediate storage to // hold the contents of the OLESTREAM CGenericObject genobj; if (ptd) { // The side of the td is the first DWORD. Ensure that much is // valid and then we can use it to check the whole structure. if (!IsValidReadPtrIn (ptd, sizeof(DWORD))) { hresult = ResultFromScode (DV_E_DVTARGETDEVICE); goto errRtn; } if (!IsValidReadPtrIn (ptd, (UINT) ptd->tdSize)) { hresult = ResultFromScode (DV_E_DVTARGETDEVICE_SIZE); goto errRtn; } } if (FAILED(hresult=wConvertOLESTREAMToIStorage(polestream,pstg,&genobj))) { goto errRtn; } // If we were able to read the object out of the stream, we can now try // to write it back out to the storage hresult = GenericObjectToIStorage (genobj, pstg, ptd); errRtn: LEDebugOut((DEB_TRACE, "%p OUT OleConvertOLESTREAMToIStorage ( %lx ) " "\n", 0 /*function*/, hresult)); OLETRACEOUT((API_OleConvertOLESTREAMToIStorage, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Function: PrependUNCName // // Synopsis: Convert *pszFile to use szUNC=="\\server\share" instead // of drive letter // // Effects: Deletes the UNC name and returns *ppszFile as a NEW string // with the full UNC filename. The string originally held at // *ppszFile is deleted by this function. // // Arguments: [ppszFile] Pointer to incoming filename string pointer // // History: dd-mmm-yy Author Comment // 16-Feb-94 davepl Cleanup, documentation, allocation fixes // // Notes: This function does some frightening things by changing the // caller's pointer and deleting various reference parameters. // Be sure you know what's going on before turning this function // loose on one of your own pointers. // //-------------------------------------------------------------------------- static INTERNAL PrependUNCName (LPOLESTR FAR* ppszFile, LPOLESTR szUNC) { HRESULT hresult = NOERROR; LPOLESTR szNew; // No place to put result, so nothing to do... if (NULL==szUNC) { return hresult; } // Ensure the caller's pointer is valid if (NULL == *ppszFile) { return ResultFromScode(CONVERT10_E_OLESTREAM_FMT); } // Ensure the second letter of path is a colon (ie; X:\file) if((*ppszFile)[1] != L':') { return ResultFromScode(CONVERT10_E_OLESTREAM_FMT); } // Allocate enough space for new filename (we will be // omitting the X: portion of the filename, so this calculation // is _not_ short by 2 as it may appear) szNew = (LPOLESTR) PubMemAlloc((_xstrlen(*ppszFile)+_xstrlen (szUNC)) * sizeof(OLECHAR)); if (NULL == szNew) { return ResultFromScode(E_OUTOFMEMORY); } // Copy over the UNC name _xstrcpy (szNew, szUNC); // Add the original name, except for the X: _xstrcat (szNew, (*ppszFile) + 2); // Free the original name PubMemFree(*ppszFile); *ppszFile = szNew; // Delete the UNC name PubMemFree(szUNC); return hresult; } //+------------------------------------------------------------------------- // // Function: OLESTREAMToGenericObject, INTERNAL // // Synopsis: Reads and OLE 1.0 version of an object from an OLE 1 stream // and stores it internally, including presentation and native // data, in a GenericObject. // // Effects: Creates a GenericObject that can be written back in OLE 1 // or OLE 2 format // // Arguments: [pos] -- pointer to OLE 1 stream to read object from // [pgenobj] -- pointer to generic object to read into // // Requires: Input stream setup and GenObj created // // Returns: NOERROR On success // CONVERT10_E_OLESTREAM_FMT On unknown OLE 1 format // CONVERT10_E_OLESTREAM_GET On stream read failue // E_OUTOFMEMORY On stream I/O memory failure // // Signals: (none) // // Modifies: Stream position, GenObj // // Algorithm: // // History: dd-mmm-yy Author Comment // 14-Feb-94 davepl Added Trace code // davepl Cleaned up and documented // davepl Rerouted errors through central return // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(OLESTREAMToGenericObject) static INTERNAL OLESTREAMToGenericObject ( LPOLESTREAM pos, PGENOBJ pgenobj ) { HRESULT error = NOERROR; ULONG ulFmtId; LPOLESTR szClass = NULL; // Read OLE Version # from the stream and discard it if (FAILED(error = OLE1StreamToUL(pos, NULL))) { LEDebugOut(( DEB_ERROR, "Unable to read OLE ver# from stream at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } // Get Format ID from the stream if (FAILED(error = OLE1StreamToUL(pos, &ulFmtId))) { LEDebugOut(( DEB_ERROR, "Unable to read format ID from stream at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } // If this is a static object, read it into the generic object and return if (ulFmtId == FMTID_STATIC) { if (FAILED(error = GetStaticObject (pos, pgenobj))) { LEDebugOut(( DEB_ERROR, "Unable to read static object at line %d in %s\n", __LINE__, __FILE__)); } goto errRtn; } // If this is neither a linked nor an embedded object, something // is wrong if (ulFmtId != FMTID_LINK && ulFmtId != FMTID_EMBED) { LEDebugOut(( DEB_ERROR, "Object is neither linked nor embedded at line %d in %s\n", __LINE__, __FILE__)); error = ResultFromScode(CONVERT10_E_OLESTREAM_FMT); goto errRtn; } // If this is a linked object, set our flag in GenericObject if (FMTID_LINK == ulFmtId) { pgenobj->m_fLink = TRUE; } // Read the class name from the stream if (FAILED(error = OLE1StmToString(pos, &szClass))) { LEDebugOut(( DEB_ERROR, "Unable to read the class name from stream at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } if (NULL == szClass) { LEDebugOut(( DEB_ERROR, "Class name was returned NULL at line %d in %s\n", __LINE__, __FILE__)); error = CONVERT10_E_OLESTREAM_FMT; goto errRtn; } // If this is an embedded object, set the class ID and class string // If it is a linked object, set the class name but set the class ID // to CLSID_StdOleLink if (FMTID_EMBED == ulFmtId) { pgenobj->m_class.SetSz (szClass); } else { Assert (ulFmtId == FMTID_LINK); pgenobj->m_classLast.SetSz (szClass); pgenobj->m_class.Set (CLSID_StdOleLink, NULL); } // Read the Topic string from the stream if (FAILED(error = OLE1StmToString(pos, &(pgenobj->m_szTopic)))) { LEDebugOut(( DEB_ERROR, "Unable to read topic string from stream at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } // Read the Item string from the stream if (FAILED(error = OLE1StmToString(pos, &(pgenobj->m_szItem)))) { LEDebugOut(( DEB_ERROR, "Unable to get item string from stream at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } // If this is a linked object, set up the filename etc. if (FMTID_LINK == ulFmtId) { LPOLESTR szUNCName = NULL; // Read the network name from the stream if (FAILED(error = OLE1StmToString(pos, &szUNCName))) { LEDebugOut(( DEB_ERROR, "Unable to get network name from stream at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } // Convert a drive-letter based name to \\srv\share name if (FAILED(error = PrependUNCName (&(pgenobj->m_szTopic), szUNCName))) { LEDebugOut(( DEB_ERROR, "Unable to convert drv ltr to UNC name at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } // Read network type and network driver version # from stream // (They are both shorts and we discarding them, so read a LONG) if (FAILED(error = OLE1StreamToUL (pos, NULL))) { LEDebugOut(( DEB_ERROR, "Unable to get net type/ver from stream at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } // Read the link-updating options from the stream. This field // use OLE 1.0 enumeration values for the link update options if (FAILED(error = OLE1StreamToUL(pos, &(pgenobj->m_lnkupdopt)))) { LEDebugOut(( DEB_ERROR, "Unable to read link update opts at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } // OLE 1.0 duplicates the link update options in the highword // of the LONG, and we don't want that, so clear the highword. pgenobj->m_lnkupdopt &= 0x0000FFFF; } else // This path is taken to read in embedded objects { Assert (ulFmtId == FMTID_EMBED); // Read and store the native data from the stream if (FAILED(error = GetSizedDataOLE1Stm (pos, &(pgenobj->m_dataNative)))) { LEDebugOut(( DEB_ERROR, "Unable to get native data from stream at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } } // For both linked and embedded objects, we need to read in any // presentation data that may be present. Note that certain formats // such as MS-Paint will not provide presentation data; this is OK // since they can be rendered by native data alone (space saving measure) if (FAILED(error = GetPresentationObject (pos, pgenobj))) { LEDebugOut(( DEB_ERROR, "Unable to get presentation data from stream at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } errRtn: LEDebugOut((DEB_ITRACE, "%p OUT OLESTREAMToGenericObject ( %lx ) \n", NULL /*function*/, error)); return error; } //+------------------------------------------------------------------------- // // Function: GetStaticObject, INTERNAL // // Synopsis: Reads the presentation data for a static object into the // PPRES member of GenericObject, and sets format and class // flags accordingly // // Effects: // // Arguments: [pos] -- stream we are reading OLE 1 object from // [pgenobj] -- GenericObject we are reading into // Requires: // // Returns: NOERROR On success // CONVERT10_E_OLESTREAM_FMT On unknown OLE 1 format // CONVERT10_E_OLESTREAM_GET On stream read failue // E_OUTOFMEMORY On stream I/O memory failure // // Signals: (none) // // Modifies: Stream position, GenericObject // // Algorithm: // // History: dd-mmm-yy Author Comment // 14-Feb-94 davepl Cleanup and documentation // Notes: // //-------------------------------------------------------------------------- static INTERNAL GetStaticObject (LPOLESTREAM pos, PGENOBJ pgenobj) { HRESULT error; // Read the presentation data, standard or generic, into the // PPRES member of the GenericObject if (FAILED(error = GetPresentationObject(pos, pgenobj, TRUE))) { return ResultFromScode(error); } // Ensure that the format tag is a clipboard format if (ftagClipFormat != pgenobj->m_ppres->m_format.m_ftag) { return ResultFromScode(CONVERT10_E_OLESTREAM_FMT); } // If the clipboard format is a METAFILEPIC, set the CLASS // member of GenericObject to CLSID_StaticMetafile if (CF_METAFILEPICT == pgenobj->m_ppres->m_format.m_cf) { pgenobj->m_class.Set (CLSID_StaticMetafile, NULL); } // Otherwise, check to see if it is a DIB, and set the CLASS // member accordingly else if (CF_DIB == pgenobj->m_ppres->m_format.m_cf) { pgenobj->m_class.Set (CLSID_StaticDib, NULL); } // If it is neither a METAFILEPIC nor a DIB, we have a problem else { AssertSz (0, "1.0 static object not in one of 3 standard formats"); return ResultFromScode (CONVERT10_E_OLESTREAM_FMT); } // Flag the GenericObject as Static pgenobj->m_fStatic = TRUE; return NOERROR; } //+------------------------------------------------------------------------- // // Function: CreateBlankPres, INTERNAL // // Synopsis: Sets up the format in the PPRES struct as ClipFormat 0 // // History: dd-mmm-yy Author Comment // 16-Feb-94 davepl Cleaned up // Notes: // //-------------------------------------------------------------------------- static INTERNAL CreateBlankPres(PPRES ppres) { Assert (ppres); ppres->m_format.m_ftag = ftagClipFormat; ppres->m_format.m_cf = 0; return NOERROR; } //+------------------------------------------------------------------------- // // Function: GetPresentationObject, INTERNAL // // Synopsis: Reads the presentation data into the CGenericObject object // // Arguments: [pos] -- OLE 1 stream we are reading from // [pgenobj] -- Generic object we are reading to // [fStatic] -- Flag: getting a static pres object? // // Requires: stream open, object allocated // // Returns: CONVERT10_E_OLESTREAM_FMT unknown format id in stream // // // History: dd-mmm-yy Author Comment // 16-Feb-94 davepl Cleaned up and documented // // Notes: // //-------------------------------------------------------------------------- static INTERNAL GetPresentationObject( LPOLESTREAM pos, PGENOBJ pgenobj, BOOL fStatic) { LPOLESTR szClass = NULL; HRESULT hresult = NOERROR; Assert (pgenobj->m_ppres==NULL); if (TRUE != fStatic) //FALSE! { // Pull the OLE version number out of the stream, we don't want it if (FAILED(hresult = OLE1StreamToUL(pos, NULL))) { return hresult; } // Pull the OLE 1 format identifier out of the stream ULONG ulFmtId; if (FAILED(hresult = OLE1StreamToUL (pos, &ulFmtId))) { return hresult; } // If the format identifier is not FMTID_PRES, we've got a // problem... unless it's 0 in which case it simply means // that there _is no_ presentation data, ie: PBrush, Excel if (ulFmtId != FMTID_PRES) { if (0==ulFmtId) { return NOERROR; } else { return ResultFromScode(CONVERT10_E_OLESTREAM_FMT); } } } // Pull in the type name for the OLE1 data if (FAILED(hresult = OLE1StmToString (pos, &szClass))) { return hresult; } if (0==_xstrcmp (szClass, OLESTR("METAFILEPICT"))) { hresult = GetStandardPresentation (pos, pgenobj, CF_METAFILEPICT); } else if (0==_xstrcmp (szClass, OLESTR("BITMAP"))) { hresult = GetStandardPresentation (pos, pgenobj, CF_BITMAP); } else if (0==_xstrcmp (szClass, OLESTR("DIB"))) { hresult = GetStandardPresentation (pos, pgenobj, CF_DIB); } else if (0==_xstrcmp (szClass, OLESTR("ENHMETAFILE"))) { Assert (0 && "Encountered an unsupported format: ENHMETAFILE"); } else { // This is a Generic Presentation stream #if DBG==1 Assert (!fStatic); if (_xstrcmp (pgenobj->m_fLink ? pgenobj->m_classLast.m_szClsid : pgenobj->m_class.m_szClsid, szClass)) { Assert (0 && "Class name in embedded object stream does\n" "not match class name in pres object stream"); } #endif hresult = GetGenericPresentation (pos, pgenobj); } if (szClass) { PubMemFree(szClass); } return hresult; } //+------------------------------------------------------------------------- // // Function: GetBitmapAsDib, INTERNAL // // Synopsis: Reads a bitmap from the OLE1 stream, converts it to a DIB, // and stores it in the DATA member of CGenericObject // // Arguments: [pos] -- The OLE 1 stream to read from // [pdata] -- The DATA object to read into // // Requires: // // Returns: NOERROR success // CONVERT10_E_OLESTREAM_GET I/O error // CONVERT10_E_OLESTREAM_BITMAP_TO_DIB conversion error // // History: dd-mmm-yy Author Comment // 16-Feb-94 davepl Cleaned up and documented // Notes: // //-------------------------------------------------------------------------- static INTERNAL GetBitmapAsDib(LPOLESTREAM pos, PDATA pdata) { HRESULT hresult= NOERROR; HGLOBAL hBits = NULL; HGLOBAL hDib = NULL; LPVOID pBits = NULL; WIN16BITMAP bm; HBITMAP hBitmap = NULL; ULONG cbBits; ULONG ul; Assert (pdata->m_h==NULL && pdata->m_pv==NULL && pdata->m_cbSize==0); // Get size of all bitmap data, including the bitmap header struct if (FAILED(hresult = OLE1StreamToUL(pos, &ul))) { return hresult; } // Read the bitmap header structure. Since this was stored as Win16 // BITMAP, we have to pull a structure of that size from the stream // (A Win32 BITMAP uses LONGs and hence is larger). if (pos->lpstbl->Get (pos, &bm, sizeof(WIN16BITMAP)) < sizeof(WIN16BITMAP)) { return ResultFromScode (CONVERT10_E_OLESTREAM_GET); } // The bitmap data is total size - header size // Allocate enough memory to hold the bitmap data cbBits = ul - sizeof(WIN16BITMAP); hBits = GlobalAlloc (GMEM_MOVEABLE, cbBits); if (NULL == hBits) { hresult = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } pBits = (void FAR*) GlobalLock (hBits); if (pBits == NULL) { hresult = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } // Read the header data into our allocated buffer if (pos->lpstbl->Get (pos, pBits, cbBits) < cbBits) { hresult = ResultFromScode (CONVERT10_E_OLESTREAM_GET); goto errRtn; } // Turn that raw data into a bitmap hBitmap = CreateBitmap (bm.bmWidth, bm.bmHeight, bm.bmPlanes, bm.bmBitsPixel, pBits); if (NULL == hBitmap) { hresult = ResultFromScode(CONVERT10_E_OLESTREAM_BITMAP_TO_DIB); goto errRtn; } // NOTE: The following call gave only the first parameter in the // (davepl) original source; The second is the palette handle, which // I've passed as NULL to indicate the default stock palette. hDib = UtConvertBitmapToDib (hBitmap, NULL); if (NULL == hDib) { hresult = ResultFromScode(CONVERT10_E_OLESTREAM_BITMAP_TO_DIB); goto errRtn; } // Set the presentation data pointers to point to this new DIB pdata->m_pv = GlobalLock (hDib); if (NULL == pdata->m_pv) { hresult = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } pdata->m_cbSize = (ULONG) GlobalSize (hDib); pdata->m_h = hDib; // Free up allocations and resources, return result errRtn: if (pBits) { Verify (0==GlobalUnlock (hBits)); } if (hBits) { Verify (0==GlobalFree (hBits)); } if (hBitmap) { Verify (DeleteObject (hBitmap)); } return hresult; } //+------------------------------------------------------------------------- // // Function: GetMfBits, INTERNAL // // Synopsis: Strips the METAFILE header from the stream and then reads // the metafile bits into an allocated memory area; the // presentation data member of [pos] is then set to point // to this memory. // // Arguments: [pos] -- the OLE 1 stream to read from // [pdata] -- the presentation data member of generic object // // Returns: NOERROR success // CONVERT10_E_OLESTREAM_GET stream error // E_OUTOFMEMORY allocation failure // // History: dd-mmm-yy Author Comment // 16-Feb-94 davepl Cleaned up and documented // // Notes: // //-------------------------------------------------------------------------- static INTERNAL GetMfBits(LPOLESTREAM pos, PDATA pdata) { ULONG cbSize; WIN16METAFILEPICT mfpictDummy; HRESULT hresult = NOERROR; Assert (0==pdata->m_cbSize && pdata->m_h==NULL && NULL==pdata->m_pv); // Read the data size from the stream if (FAILED(hresult = (OLE1StreamToUL (pos, &cbSize)))) { return hresult; } // Now read the actual data if (cbSize <= sizeof(WIN16METAFILEPICT)) { return ResultFromScode(CONVERT10_E_OLESTREAM_FMT); } // An OLESTREAM contains a METAFILEPICT structure (with a meaningless // handle) followed by the metafile bits. So consume the METAFILEPICT. if (pos->lpstbl->Get (pos, &mfpictDummy, sizeof(WIN16METAFILEPICT)) < sizeof(WIN16METAFILEPICT)) { return ResultFromScode(CONVERT10_E_OLESTREAM_GET); } // Deduct from our count of bytes to read the size of the header which // we just consumed. Set the presentation data size to be this new size. cbSize -= sizeof(WIN16METAFILEPICT); pdata->m_cbSize = cbSize; // Grad some memory to store the metafile bits pdata->m_h = GlobalAlloc (GMEM_MOVEABLE, cbSize); if (NULL==pdata->m_h) { return ResultFromScode(E_OUTOFMEMORY); } pdata->m_pv = GlobalLock (pdata->m_h); if (NULL==pdata->m_pv) { return ResultFromScode(E_OUTOFMEMORY); } // Get the actual metafile bits if (pos->lpstbl->Get (pos, pdata->m_pv, cbSize) < cbSize) { return ResultFromScode(CONVERT10_E_OLESTREAM_GET); } return hresult; } //+------------------------------------------------------------------------- // // Function: GetStandardPresentation, INTERNAL // // Synopsis: Allocates a PRES member for generic object, then reads // whatever presentation may be found in the stream into // that PRES. // // Arguments: [pos] -- the OLE 1 stream to read from // [pgenobj] -- the generic object we are going to set // up with the presentation data // [cf] -- the clipboad format we are to read // // Returns: NOERROR success // E_OUTOFMEMORY allocation failure // // Modifies: [pgenobj] - sets up the m_ppres member // // History: dd-mmm-yy Author Comment // 16-Feb-94 davepl Cleaned up and documented // // Notes: // //-------------------------------------------------------------------------- static INTERNAL GetStandardPresentation( LPOLESTREAM pos, PGENOBJ pgenobj, CLIPFORMAT cf) { HRESULT hresult = NOERROR; // Allocate enough memory for the PRES object pgenobj->m_ppres = new PRES; if (NULL == pgenobj->m_ppres) { return ResultFromScode(E_OUTOFMEMORY); } // Set up the format tag and clipboard format pgenobj->m_ppres->m_format.m_ftag = ftagClipFormat; pgenobj->m_ppres->m_format.m_cf = cf; // Get the width of the data from the stream if (FAILED(hresult = OLE1StreamToUL(pos, &(pgenobj->m_ppres->m_ulWidth)))) { return hresult; } // Get the height of the data from the stream if (FAILED(hresult=OLE1StreamToUL(pos, &(pgenobj->m_ppres->m_ulHeight)))) { return hresult; } // The height saved by OLE 1.0 objects into the stream is always a // negative value (Y-increase in pixel is negative upward?) so we // have to correct that value. pgenobj->m_ppres->m_ulHeight = (ULONG) -((LONG) pgenobj->m_ppres->m_ulHeight); // Read the appropriate presentation data based on the clipboard // format ID switch(cf) { case CF_METAFILEPICT: { hresult = GetMfBits (pos, &(pgenobj->m_ppres->m_data)); break; } case CF_BITMAP: { // When reading a bitmap, we will convert from Bitmap to // DIB in the process, so update the PRES clipboard format ID pgenobj->m_ppres->m_format.m_cf = CF_DIB; hresult = GetBitmapAsDib (pos, &(pgenobj->m_ppres->m_data)); break; } case CF_DIB: { Assert (CF_DIB==cf); hresult = GetSizedDataOLE1Stm (pos, &(pgenobj->m_ppres->m_data)); break; } default: { Assert(0 && "Unexpected clipboard format reading PRES"); } } return hresult; } //+------------------------------------------------------------------------- // // Function: GetGenericPresentation, INTERNAL // // Synopsis: Allocated the PRES member of the generic object and reads // the generic presentation data into it. // // Effects: If the format is a known clipboard format, we set the // format tag to indicate this, and set the format type // to indicate the clipboard format type. If it is unknown, // we set the format tag to string and read the description // of the format. // // Arguments: [pos] -- the OLE 1 stream we are reading from // [pgenobj] -- the generic object we are reading to // // Returns: NOERROR on success // E_OUTOFMEMORY on allocation failure // // History: dd-mmm-yy Author Comment // 16-Feb-94 davepl Code cleanup and document // // Notes: // //-------------------------------------------------------------------------- static INTERNAL GetGenericPresentation( LPOLESTREAM pos, PGENOBJ pgenobj) { ULONG ulClipFormat; HRESULT hresult = NOERROR; // The PRES member should not exist at this point Assert (NULL==pgenobj->m_ppres); // Allocate the PRES member of the generic object pgenobj->m_ppres = new PRES; if (NULL == pgenobj->m_ppres) { return ResultFromScode(E_OUTOFMEMORY); } // Read the clipboard format ID if (FAILED(hresult = OLE1StreamToUL (pos, &ulClipFormat))) { delete (pgenobj->m_ppres); return hresult; } // If the clipboard format is not 0, we have a known clipboard // format and we should set the tag type and ID accordingly if (ulClipFormat) { pgenobj->m_ppres->m_format.m_ftag = ftagClipFormat; pgenobj->m_ppres->m_format.m_cf = (CLIPFORMAT) ulClipFormat; } else { // Otherwise, we have a custom format so we need to set the // tag type to string and read in the data format string pgenobj->m_ppres->m_format.m_ftag = ftagString; if (FAILED(hresult = (GetSizedDataOLE1Stm (pos, &(pgenobj->m_ppres->m_format.m_dataFormatString))))) { delete (pgenobj->m_ppres); return hresult; } } // We don't know the size, so reset to 0 pgenobj->m_ppres->m_ulHeight = 0; pgenobj->m_ppres->m_ulWidth = 0; // Read the raw generic presentation data into the PRES member if (FAILED(hresult=GetSizedDataOLE1Stm(pos,&(pgenobj->m_ppres->m_data)))) { delete (pgenobj->m_ppres); return hresult; } return NOERROR; } //+------------------------------------------------------------------------- // // Function: GetSizedDataOLE1Stm, INTERNAL // // Synopsis: Reads bytes from an OLE 1 stream into a CData object. // Obtains the number of bytes to read from the first // ULONG in the stream // // Arguments: [pos] -- the stream to read from // [pdata] -- the CData object to read to // // Requires: // // Returns: NOERROR on success // CONVERT10_E_OLESTREAM_GET on stream read problem // E_OUTOFMEMORY on allocation failure // // History: dd-mmm-yy Author Comment // 16-Feb-94 davepl Cleaned up and documented // Notes: // //-------------------------------------------------------------------------- static INTERNAL GetSizedDataOLE1Stm(LPOLESTREAM pos, PDATA pdata) { ULONG cbSize; HRESULT hr; Assert (0==pdata->m_cbSize && pdata->m_h==NULL && NULL==pdata->m_pv); // Read size of data if (FAILED(hr = OLE1StreamToUL(pos, &cbSize))) { return hr; } if (cbSize==0) { return NOERROR; } // Allocate memory for data pdata->m_cbSize = cbSize; pdata->m_h = GlobalAlloc (GMEM_MOVEABLE, cbSize); if (NULL==pdata->m_h) { return ResultFromScode(E_OUTOFMEMORY); } pdata->m_pv = GlobalLock (pdata->m_h); if (NULL==pdata->m_pv) { return ResultFromScode(E_OUTOFMEMORY); } // Read data into allocated buffer if (pos->lpstbl->Get (pos, pdata->m_pv, cbSize) < cbSize) { return ResultFromScode(CONVERT10_E_OLESTREAM_GET); } return NOERROR; } //+------------------------------------------------------------------------- // // Function: OLE1StreamToUL, INTERNAL // // Synopsis: Reads a ULONG from an OLE1 stream // // Arguments: [pos] -- the OLE 1 stream to read from // [pul] -- the ULONG to read into // // Returns: NOERROR on success // CONVERT10_E_OLESTREAM_GET on stream read failure // // History: dd-mmm-yy Author Comment // 16-Feb-94 davepl Cleaned up and documented // // Notes: on failure [pul] is preserved // //-------------------------------------------------------------------------- static INTERNAL OLE1StreamToUL(LPOLESTREAM pos, ULONG FAR* pul) { ULONG ul; // Read the data from the stream into the local ULONG if (pos->lpstbl->Get (pos, &ul, sizeof(ULONG)) < sizeof(ULONG)) { return ResultFromScode(CONVERT10_E_OLESTREAM_GET); } // If all went well, store the data into [pul] if (pul != NULL) { Assert (IsValidPtrOut (pul, sizeof(ULONG))); *pul = ul; } return NOERROR; } //+------------------------------------------------------------------------- // // Function: DataToOLE1Stm, INTERNAL INLINE // // Synopsis: Writes raw data out to an OLE 1 stream // // Arguments: [pos] -- the stream to write to // [pvBuf] -- the buffer to write from // [ulSize] -- the number of bytes to write // // Returns: NOERROR on success // CONVERT10_E_OLESTREAM_PUT on stream write failure // // History: dd-mmm-yy Author Comment // 16-Feb-94 davepl Cleaned up and documented // Notes: // //-------------------------------------------------------------------------- inline static INTERNAL DataToOLE1Stm(LPOLESTREAM pos, LPVOID pvBuf, ULONG ulSize) { // Write the data out to the stream if (pos->lpstbl->Put(pos, pvBuf, ulSize) < ulSize) { return ResultFromScode(CONVERT10_E_OLESTREAM_PUT); } return NOERROR; } //+------------------------------------------------------------------------- // // Function: ULToOLE1Stream, INTERNAL INLINE // // Synopsis: Write a ULONG to the specified OLESTREAM via the Put() // member of the stream's VTBL // // Effects: Advances stream position by sizeof(ULONG) on success. // // Arguments: [pos] -- The stream into which the ULONG is written // [ul] -- The ULONG, passed by value // // Requires: // // Returns: NOERROR on success // CONVERT10_E_OLESTREAM_PUT on failure // // Signals: (none) // // Modifies: Stream position // // Algorithm: // // History: dd-mmm-yy Author Comment // 11-Jan-93 davepl Cleaned up and documented // // Notes: On failure 0-3 bytes may have been written // //-------------------------------------------------------------------------- inline static INTERNAL ULToOLE1Stream(LPOLESTREAM pos, ULONG ul) { if (pos->lpstbl->Put (pos, &ul, sizeof(ULONG)) < sizeof(ULONG)) { return ResultFromScode(CONVERT10_E_OLESTREAM_PUT); } return NOERROR; } //+------------------------------------------------------------------------- // // Function: StringToOLE1Stm, INTERNAL // // Synopsis: Converts the input OLESTR to ANSI and writes it to an // OLE 1 stream, preceded by a ULONG indicating the number // of bytes in the ANSI representation (terminator included). // // Arguments: [pos] -- The stream into which the ULONG is written // [szOleStr] -- The STR to be written // // Returns: NOERROR on success // CONVERT10_E_OLESTREAM_PUT on stream write failure // E_NOMEMORY on allocation failure // // Modifies: Stream position // // History: dd-mmm-yy Author Comment // 11-Feb-94 davepl Cleaned up and documented // 15-Feb-94 davepl Re-write for ANSI/WCHAR handling // 17-Feb-94 davepl Restructured error handling // // Notes: On failure, 0 to (cbSize-1) bytes may have been written // //-------------------------------------------------------------------------- static INTERNAL StringToOLE1Stm(LPOLESTREAM pos, LPCOLESTR pszOleStr) { HRESULT hr = NOERROR; LPSTR pszAnsi = NULL; // Ansi version of OLE input string if (pszOleStr) { // This handy function will calculate the size of the buffer we // need to represent the OLESTR in ANSI format for us. ULONG cbSize = WideCharToMultiByte(CP_ACP, // Code Page ANSI 0, // No flags pszOleStr, // Input OLESTR -1, // Input len (auto detect) NULL, // Output buffer 0, // Output len (check only) NULL, // Default char NULL);// Flag: Default char used if (cbSize == FALSE) { return ResultFromScode(E_UNSPEC); } // Now that we know the actual needed length, allocate a buffer pszAnsi = (LPSTR) PrivMemAlloc(cbSize); if (NULL == pszAnsi) { return ResultFromScode(E_OUTOFMEMORY); } // We've got out buffer and our length, so do the conversion now // We don't need to check for cbSize == FALSE since that was // already done during the length test, but we need to check // for substitution. Iff this call sets the fDefChar even when // only doing a length check, these two tests could be merged, // but I don't believe this is the case. BOOL fDefUsed = 0; cbSize = WideCharToMultiByte(CP_ACP, // Code Page ANSI 0, // No flags pszOleStr, // Input OLESTR -1, // Input len (auto detect) pszAnsi, // Output buffer cbSize, // Output len NULL, // Default char (use system's) &fDefUsed); // Flag: Default char used // If number of bytes converted was 0, we failed if (fDefUsed) { hr = ResultFromScode(E_UNSPEC); } // Write the size of the string (including null terminator) to stream else if (FAILED(hr = ULToOLE1Stream(pos, cbSize))) { NULL; } // Write the Ansi version of the string into the stream else if (pos->lpstbl->Put(pos, pszAnsi, cbSize) < cbSize) { hr = ResultFromScode(CONVERT10_E_OLESTREAM_PUT); } if (pszAnsi) { PrivMemFree(pszAnsi); } } // If the pointer is not valid, we write a length of zero into // the stream else { hr = ULToOLE1Stream(pos, 0); } return hr; } //+------------------------------------------------------------------------- // // Function: OLE2StmToUL, INTERNAL // // Synopsis: Reads a ULONG from the specified ISTREAM and stores it at // the ULONG deferenced by pul // // Effects: Writes the value read into memory at pul // // Arguments: [pstm] -- The stream from which the ULONG is read // [pul] -- ULONG to hold the value read // // Requires: // // Returns: NOERROR on success // CONVERT10_E_OLESTREAM_PUT on failure // // Signals: (none) // // Modifies: Stream position // // Algorithm: // // History: dd-mmm-yy Author Comment // 11-Feb-93 davepl Cleaned up and documented // // Notes: On failure, *pul is not disturbed regardless of how // many bytes were actually read from the stream // //-------------------------------------------------------------------------- static INTERNAL OLE2StmToUL(LPSTREAM pstm, ULONG FAR* pul) { ULONG ul; ULONG cbRead; HRESULT hr = NOERROR; // Attempt to read 4 bytes from the stream to form a ULONG. if (FAILED(hr = pstm->Read (&ul, sizeof(ULONG), &cbRead))) { return hr; } if (cbRead != sizeof(ULONG)) { hr = STG_E_READFAULT; } // Ensure that the [pul] pointer is valid and that we have write // access to all 4 bytes (assertion only). If OK, transfer the // ULONG to [*pul] else if (pul != NULL) { Assert (FALSE == !IsValidPtrOut(pul, sizeof(ULONG))); *pul = ul; } return hr; } //+------------------------------------------------------------------------- // // Function: OLE1StmToString, INTERNAL // // Synopsis: Reads a cstr from the specified STREAM and stores it in // a dynamically allocated buffer as an OLESTR; sets the // user's pointer to point to this new buffer. // // Effects: Allocates memory on the input pointer, advances stream pos'n // // Arguments: [pos ] -- The stream from which the STR is read // [ppsz] -- OLESTR ** which allows this fn to modify the // caller's pointer to point to memory allocated // by this fn to hold the OLESTR // // Requires: Stream must be set up. Caller's responsibilty to free memory. // // Returns: NOERROR on success // CONVERT10_E_OLESTREAM_GET on failure // E_OUTOFMEMORY if buffers couldn't be allocated // // Signals: (none) // // Modifies: Stream position, caller's string pointer // // Algorithm: if ppsz == NULL, string is read from stream and discarded // if ppsz != NULL, string is read and converted into a // dynamically allocated buffer. *ppsz is set // to point to this buffer, which must be later // freed by the caller // // History: dd-mmm-yy Author Comment // 12-Jan-93 davepl Cleaned up and documented // 14-Jan-93 davepl Changed to return LPOLESTR // // Notes: [ppsz] may be NULL on entry; string is read and discarded // with no cleanup required by the caller // // //-------------------------------------------------------------------------- static INTERNAL OLE1StmToString(LPOLESTREAM pos, LPOLESTR FAR* ppsz) { ULONG cbSize; // Size in bytes of cstr LPOLESTR pszOleStr = NULL; LPSTR pszAnsiStr = NULL; HRESULT error = NOERROR; // if ppsz is valid, NULL out *ppsz as default out parameter if (NULL != ppsz) { *ppsz = NULL; } // Retrieve the incoming string size from the stream if (FAILED(error = OLE1StreamToUL (pos, &cbSize))) { goto errRtn; } // If there are chars to be read, allocate memory for the // ANSI and OLESTR versions. Read the string into the // ANSI version and convert it to OLESTR if (0 < cbSize) { // Allocate the ANSI buffer pszAnsiStr = (LPSTR) PrivMemAlloc((size_t)cbSize); if (NULL == pszAnsiStr) { error = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } // Read the string into the ANSI buffer if (pos->lpstbl->Get (pos, pszAnsiStr, cbSize) < cbSize) { error = ResultFromScode(CONVERT10_E_OLESTREAM_GET); goto errRtn; } // We only need to perform the ANSI->OLESTR conversion in those // cases where the caller needs an out parameter if (NULL != ppsz) { // Allocate the OLESTR buffer pszOleStr = (LPOLESTR) PubMemAlloc((size_t)cbSize * 2); if (NULL == pszOleStr) { error = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } // Convert from ANSI buffer to OLESTR buffer if (FALSE==MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pszAnsiStr, cbSize, pszOleStr, cbSize *2)) { error = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION); PubMemFree(pszOleStr); goto errRtn; } *ppsz = pszOleStr; } } errRtn: if (pszAnsiStr) { PrivMemFree(pszAnsiStr); } return error; } //+------------------------------------------------------------------------- // // Function: GenericObjectToIStorage // // Synopsis: Write the generic object in memory out to an OLE 2 IStorage // This invovles writing the class, native data, and // presentation data out where applicable. // // Arguments: [genobj] -- the generic object holding the info // [pstg] -- the IStorage object to write to // [ptd] -- target device // // Returns: NOERROR on success // CONVERT10_S_NO_PRESENTATION in cases where the object did // not have needed presentation data // // History: dd-mmm-yy Author Comment // 17-Feb-94 davepl Cleanup and document // // Notes: // //-------------------------------------------------------------------------- FARINTERNAL GenericObjectToIStorage( const GENOBJ FAR& genobj, LPSTORAGE pstg, const DVTARGETDEVICE FAR* ptd) { HRESULT hr = NOERROR; // Assert (genobj.m_class.m_clsid != CLSID_NULL); // Write the class ID out to the storage if (FAILED(hr = WriteClassStg (pstg, genobj.m_class.m_clsid))) { LEDebugOut(( DEB_ERROR, "Unable to WriteClassStg at line %d in %s\n", __LINE__, __FILE__)); return hr; } if (!genobj.m_fLink) { if (genobj.m_fStatic) { // If we are a static embedded object, get the format name from // the registration database and write it out to the IStorage LPOLESTR pszUserType = NULL; OleRegGetUserType(genobj.m_class.m_clsid, USERCLASSTYPE_FULL, &pszUserType); WriteFmtUserTypeStg (pstg, genobj.m_ppres->m_format.m_cf, pszUserType); if (pszUserType) { PubMemFree(pszUserType); } } else if (wWriteFmtUserType (pstg, genobj.m_class.m_clsid) != NOERROR) { // This happens when the class is not registered. // Use ProgId as UserType. WriteFmtUserTypeStg (pstg, (CLIPFORMAT) RegisterClipboardFormat (genobj.m_class.m_szClsid), genobj.m_class.m_szClsid); } } if (FAILED(hr = GenObjToOLE2Stm (pstg, genobj))) { LEDebugOut(( DEB_ERROR, "Unable to write gen obj to stream at line %d in %s\n", __LINE__, __FILE__)); return hr; } // If it's not a link and not a static object, dump its native // data out to the storage if (!genobj.m_fLink && !genobj.m_fStatic) { if (FAILED(hr=Write20NativeStreams (pstg, genobj))) { LEDebugOut(( DEB_ERROR, "Unable to write native stream at line %d in %s\n", __LINE__, __FILE__)); return hr; } } if (! genobj.m_fLink) { if (genobj.m_class.m_clsid == CLSID_PBrush) { if (! genobj.m_ppres || (genobj.m_ppres->m_format.m_cf == CF_DIB)) { // If the object is not a link, and it is a PBrush object with // either a DIB presentation or no presentation at all, we // don't need to do anything. return NOERROR; } } if (genobj.m_class.m_clsid == CLSID_MSDraw) { if (! genobj.m_ppres || (genobj.m_ppres->m_format.m_cf == CF_METAFILEPICT)) { // Similarly, if it is not a link, and it is an MSDraw object // with no presentation or a METAFILEPICT presentation, we // don't need to do anything. return NOERROR; } } } // In all other cases, we have to dump the presenation data out to // the storage. if (FAILED(hr = PresToIStorage (pstg, genobj, ptd))) { LEDebugOut(( DEB_ERROR, "Unable to write pres to IStorage at line %d in %s\n", __LINE__, __FILE__)); return hr; } // If we are a static object, copy the contents of the presentation // stream over to the contents stream. if (genobj.m_fStatic) { UINT uiStatus; return UtOlePresStmToContentsStm(pstg, OLE_PRESENTATION_STREAM, TRUE, &uiStatus); } // If we don't have a presentation (but weren't one of the special // cases handled above), we have a problem // // We don't care if genobj.m_pres is NULL if a blank presentation is // permited as the routine PresToIStorage will generate a blank pres. // if ((NULL == genobj.m_ppres) && genobj.m_fNoBlankPres) { LEDebugOut(( DEB_ERROR, "We have no presentation at line %d in %s\n", __LINE__, __FILE__)); return ResultFromScode(CONVERT10_S_NO_PRESENTATION); } return NOERROR; } //+------------------------------------------------------------------------- // // Function: GenObjToOLE2Stm, INTERNAL // // Synopsis: Write the generic object out to the OLE 2 stream // // Effects: Write the whole object, including presentation data, etc. // // Arguments: [pstg] -- the IStorage to write to // [genobj] -- the generic object to write // // Returns: NOERROR on success // This is an upper level function, so there are numerous // error that could be propagated up through it // // History: dd-mmm-yy Author Comment // 14-Feb-94 davepl Code cleanup and document // // Notes: The code is enclosed in a do{}while(FALSE) block so that // we can break out of it on any error and fall through to // the cleanup and error return code. // //-------------------------------------------------------------------------- static INTERNAL GenObjToOLE2Stm(LPSTORAGE pstg, const GENOBJ FAR& genobj) { HRESULT hr = NOERROR; LPSTREAM pstm=NULL; do { // The do{}while(FALSE) allows us to break out on error // Create a stream in the current IStorage if (FAILED(hr = OpenOrCreateStream (pstg, OLE_STREAM, &pstm))) { LEDebugOut(( DEB_ERROR, "Can't create streamat line %d in %s\n", __LINE__, __FILE__)); break; } // Write the Ole version out to that new stream if (FAILED(hr = ULToOLE2Stm (pstm, gdwOleVersion))) { break; } // Write the object flags (for links only, otherwise 0) to the stream if (FAILED(hr = ULToOLE2Stm (pstm, genobj.m_fLink ? OBJFLAGS_LINK : 0L))) { break; } // Write the update options out to the stream if (genobj.m_fLink || genobj.m_class.m_clsid == CLSID_StdOleLink) { // If our object's link update options are UPDATE_ONCALL, we // write out the corresponding OLE 2 flags, otherwise, we // write out OLEUPDATE_ALWAYS if (genobj.m_lnkupdopt==UPDATE_ONCALL) { if (FAILED(hr = ULToOLE2Stm (pstm, OLEUPDATE_ONCALL))) { break; } } else { if (FAILED(hr = ULToOLE2Stm (pstm, OLEUPDATE_ALWAYS))) { break; } } } else { // We are neither a link nor a StdOleLink, so we have no // update options.. just write out a 0. if (FAILED(hr = ULToOLE2Stm (pstm, 0L))) { break; } } // This is a reserved filed (was View Format), just write a 0 if (FAILED(hr = ULToOLE2Stm (pstm, 0L))) { break; } // We have no relative moniker, write out NULL if (FAILED(hr = WriteMonikerStm (pstm, (LPMONIKER)NULL))) { LEDebugOut(( DEB_ERROR, "Unable to write moniker to stream at line %d in %s\n", __LINE__, __FILE__)); break; } // If we are a link, we have to write out all of that information... if (genobj.m_fLink || genobj.m_class.m_clsid == CLSID_StdOleLink) { // relative source moniker if (FAILED(hr = WriteMonikerStm (pstm, (LPMONIKER)NULL))) { LEDebugOut(( DEB_ERROR, "Unable to write moniker to stream at line %d in %s\n", __LINE__, __FILE__)); break; } // absolute source moniker if (FAILED(hr = MonikerToOLE2Stm (pstm, genobj.m_szTopic, genobj.m_szItem, genobj.m_classLast.m_clsid))) { LEDebugOut(( DEB_ERROR, "Unable to write moniker to stream at line %d in %s\n", __LINE__, __FILE__)); break; } // write the classLast field to the stream CLSID clsid; // If we have the classLast already, use that clsid if (genobj.m_classLast.m_szClsid) { clsid = genobj.m_classLast.m_clsid; } else { // Otherwise, if it's a StdOleLink, class id is NULL if (genobj.m_class.m_clsid == CLSID_StdOleLink) { clsid = CLSID_NULL; } else { // If we don't have last class and not a link, use the // class id of the generic object clsid = genobj.m_class.m_clsid; } } if (FAILED(hr = WriteM1ClassStm(pstm, clsid))) { LEDebugOut(( DEB_ERROR, "Unable to write M1 to stream at line %d in %s\n", __LINE__, __FILE__)); break; } // last display == NULL string if (FAILED(hr = ULToOLE2Stm (pstm, 0L))) { break; } // Last Change time if (FAILED(hr = FTToOle2Stm (pstm))) { break; } // Last known up to date if (FAILED(hr = FTToOle2Stm (pstm))) { break; } // rtUpdate if (FAILED(hr = FTToOle2Stm (pstm))) { break; } // end marker if (FAILED(hr = ULToOLE2Stm(pstm, (ULONG) -1L))) { break; } } } while (FALSE); // This do{}while(FALSE) is a once-through "loop" // that we can break out of on error and fall // through to the return. if (pstm) { pstm->Release(); } return hr; } //+------------------------------------------------------------------------- // // Function: MonikerToOLE2Stm, INTERNAL // // Synopsis: Write the file and item moniker as a composite to the stream // // Effects: Builds a composite of the file and item monikers, and then // writes them out. If there is no file, a NULL moniker is // written in its place // // Arguments: [pstm] -- The OLE2 storage we are writing to // [pszFile] -- The file associated with the object // [spzItem] -- The item // [clsid] -- The class ID of the object // // Returns: NOERROR on success // // History: dd-mmm-yy Author Comment // 18-Feb-94 davepl Reworked, cleaned up and documented // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(MonikerToOLE2Stm) static INTERNAL MonikerToOLE2Stm( LPSTREAM pstm, LPOLESTR szFile, LPOLESTR szItem, CLSID clsid) // CLSID of the link source file, szFile { HRESULT hr = NOERROR; LPMONIKER pmkFile = NULL; // File moniker LPMONIKER pmkItem = NULL; // Item moniker LPMONIKER pmkComp = NULL; // Composite of file + item monikers // If we don't have a file, write a NULL moniker if (NULL == szFile) { if (FAILED(hr = WriteMonikerStm (pstm, NULL))) { goto errRtn; } } else { // Otherwise, create a file moniker (OLE1 or OLE2 as appplicable) if (CoIsOle1Class (clsid)) { if (FAILED(hr = CreateOle1FileMoniker (szFile, clsid, &pmkFile))) { LEDebugOut(( DEB_ERROR, "Can't create OLE 1 moniker at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } } else { if (FAILED(hr = CreateFileMoniker (szFile, &pmkFile))) { LEDebugOut(( DEB_ERROR, "Can't create file moniker at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } } // If we don't have an Item, write just the file moniker if (NULL==szItem) { if (FAILED(hr = WriteMonikerStm (pstm, pmkFile))) { LEDebugOut(( DEB_ERROR, "Unable to write moniker to stream at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } } // Otherwise, create a composite of the file + item monikers // and write it out else { if (FAILED(hr=CreateItemMoniker(OLESTR("!"), szItem, &pmkItem))) { LEDebugOut(( DEB_ERROR, "Unable to create item moniker at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } if (FAILED(hr=CreateGenericComposite(pmkFile, pmkItem, &pmkComp))) { LEDebugOut(( DEB_ERROR, "Unable to create generic pres at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } if (FAILED(hr = WriteMonikerStm (pstm, pmkComp))) { LEDebugOut(( DEB_ERROR, "Unable to write moniker to stream at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } } } errRtn: if (pmkFile) { pmkFile->Release(); } if (pmkItem) { pmkItem->Release(); } if (pmkComp) { pmkComp->Release(); } return hr; } //+------------------------------------------------------------------------- // // Function: IsStandardFormat, INTERNAL // // Synopsis: Returns TRUE if object is in clipboard format and is one // one of the three standard formats (METAFILE, DIB, BITMAP) // // Arguments: [format] -- the format object which contains the // format tag and clipboard format type // // Returns: TRUE if METAFILE, DIB, or BITMAP // FALSE if other format or not clipboard format at all // // History: dd-mmm-yy Author Comment // 16-Feb-94 davepl documented and chaged from big // conditional to a switch() // Notes: // //-------------------------------------------------------------------------- static INTERNAL_(BOOL) IsStandardFormat(const FORMAT FAR& format) { // First we must ensure that the format tag indicates that this // object is in clipboard format at all... if (format.m_ftag == ftagClipFormat) { // If so, there is a limited set of clipboard formats which // we consider "standard". If it is not among these, // we return FALSE. switch(format.m_cf) { case CF_METAFILEPICT: case CF_BITMAP: case CF_DIB: return TRUE; default: return FALSE; } } return FALSE; } //+------------------------------------------------------------------------- // // Function: PresToIStorage, INTERNAL // // Synopsis: Given an generic object and an IStorage, write genobj's // presentation data out to the storage // // Effects: Will call PresToNewOLE2Stm to create a stream in this // storage to hold the presentation data // // Arguments: [pstg] -- the storage to save to // [genobj] -- the generic object holding the presenation // [ptd] -- the target device for the presentation // // Returns: NOERROR on success // Various other errors may propagate back up from I/O funcs // // History: dd-mmm-yy Author Comment // 18-Feb-94 davepl ARRGR! Cleanup and document // Notes: // //-------------------------------------------------------------------------- static INTERNAL PresToIStorage( LPSTORAGE pstg, const GENOBJ FAR& genobj, const DVTARGETDEVICE FAR* ptd) { HRESULT hr = NOERROR; if (genobj.m_fNoBlankPres) { return NOERROR; } PRES pres; if (NULL==genobj.m_ppres) { // If we're not a link, and we don't have a presentation, we will // create a blank presentation and write it out. If we are a link, // we will do nothing, and just fall through to the return. if (!genobj.m_fLink) { if (FAILED(hr = CreateBlankPres (&pres))) { LEDebugOut(( DEB_ERROR, "Unable to create blank pres at line %d in %s\n", __LINE__, __FILE__)); return hr; } if (FAILED(hr = PresToNewOLE2Stm (pstg, genobj.m_fLink, pres, ptd, OLE_PRESENTATION_STREAM))) { LEDebugOut(( DEB_ERROR, "Unable to write pres to new stream at line %d in %s\n", __LINE__, __FILE__)); return hr; } } } else { // If the object did indeed have a presentation, we write it // out to a new stream if (IsStandardFormat (genobj.m_ppres->m_format)) { // If the presentation is a standard clipboard // format, we can write it out with no other work if (FAILED(hr = PresToNewOLE2Stm ( pstg, genobj.m_fLink, *(genobj.m_ppres), ptd, OLE_PRESENTATION_STREAM))) { LEDebugOut(( DEB_ERROR, "Unable to write pres to new stream at line %d in %s\n", __LINE__, __FILE__)); return hr; } } else { // If the presentation is not a standard format, // it may be a PBrush object (handled below), or if // not, we write it as a generic presentation stream if (genobj.m_classLast.m_clsid != CLSID_PBrush) { if(FAILED(hr = PresToNewOLE2Stm ( pstg, genobj.m_fLink, *(genobj.m_ppres), ptd, OLE_PRESENTATION_STREAM))) { LEDebugOut(( DEB_ERROR, "Unable to write pres to new stream at line %d in %s\n", __LINE__, __FILE__)); return hr; } } else // PBrush { BOOL fPBrushNative = FALSE; // We know this is a PBrush object. If the // format tag is a format string, check to see // if that string is "Native", in which case // we set the flag to indicate that this is // native pbrush data. if (genobj.m_ppres->m_format.m_ftag == ftagString) { if (!strcmp( (LPCSTR) genobj.m_ppres-> m_format.m_dataFormatString.m_pv, "Native" ) ) { fPBrushNative = TRUE; } } if (FAILED(hr = PresToNewOLE2Stm( pstg, genobj.m_fLink, *(genobj.m_ppres), ptd, OLE_PRESENTATION_STREAM, fPBrushNative))) { LEDebugOut(( DEB_ERROR, "Unable to write pres to new stream at line %d in %s\n", __LINE__, __FILE__)); return hr; } } } } return NOERROR; } //+------------------------------------------------------------------------- // // Function: PresToNewOLE2Stm, INTERNAL // // Synopsis: Creates a new stream within a storage and writes the // generic object's presentation data out to it. // // Arguments: [pstg] -- the storage in which to create the stream // [fLink] -- flag: is this object a link? // [pres] -- the presentation data to be saved // [ptd] -- the target render device // [szStream] -- the name of the new stream // [fPBrushNative] -- flag: is this native PBrush pres data? // // Returns: NOERROR on success // STG_E_WRITEFAULT on stream write failure // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Code cleanup and documentation // // Notes: // //-------------------------------------------------------------------------- static INTERNAL PresToNewOLE2Stm( LPSTORAGE pstg, BOOL fLink, const PRES FAR& pres, const DVTARGETDEVICE FAR* ptd, LPOLESTR szStream, BOOL fPBrushNative ) { HRESULT hr = NOERROR; LPSTREAM pstm=NULL; FORMATETC foretc; // Create the new stream to hold the presentation data if (FAILED(hr = OpenOrCreateStream (pstg, szStream, &pstm))) { goto errRtn; } // Fill in the FormatEtc structure if (fPBrushNative) { foretc.cfFormat = CF_DIB; } else { switch( pres.m_format.m_ftag) { case ftagClipFormat: foretc.cfFormat = pres.m_format.m_cf; break; case ftagString: // m_dataFormatString is an ASCII string. foretc.cfFormat = (CLIPFORMAT) SSRegisterClipboardFormatA( (LPCSTR) pres.m_format.m_dataFormatString.m_pv); Assert(0 != foretc.cfFormat); break; default: AssertSz(0,"Error in Format"); hr = E_UNEXPECTED; goto errRtn; break; } } foretc.ptd = (DVTARGETDEVICE *) ptd; foretc.dwAspect = DVASPECT_CONTENT; foretc.lindex = -1; foretc.tymed = TYMED_NULL; // tymed field is ignored by utWriteOlePresStmHeader. if (FAILED(hr = UtWriteOlePresStmHeader(pstm,&foretc,(fLink) ? (ADVF_PRIMEFIRST) : (0L)))) { goto errRtn; } if (fPBrushNative) { if (FAILED(hr = UtHDIBFileToOlePresStm(pres.m_data.m_h, pstm))) { LEDebugOut(( DEB_ERROR, "Unable to write DIB to stream at line %d in %s\n", __LINE__, __FILE__)); goto errRtn; } } else { // Compression if (FAILED(hr = ULToOLE2Stm (pstm, 0L))) { goto errRtn; } // Width / Height if (FAILED(hr = ULToOLE2Stm (pstm, pres.m_ulWidth))) { goto errRtn; } if (FAILED(hr = ULToOLE2Stm (pstm, pres.m_ulHeight))) { goto errRtn; } // Presentation data if (FAILED(hr = DataObjToOLE2Stm (pstm, pres.m_data))) { goto errRtn; } } errRtn: if (pstm) { pstm->Release(); } return hr; } //+------------------------------------------------------------------------- // // Function: ULToOLE2Stm, INTERNAL // // Synopsis: Writes a ULONG out to an OLE2 stream // // Arguments: [pstm] -- the stream to write to // [ul] -- the ULONG to write to that stream // // Returns: NOERROR on success // STG_E_WRITEFAULT on write failure // // History: dd-mmm-yy Author Comment // 18-Feb-94 davepl Cleaned up and documented // //-------------------------------------------------------------------------- inline static INTERNAL ULToOLE2Stm(LPSTREAM pstm, ULONG ul) { // Write the ULONG out return pstm->Write (&ul, sizeof(ULONG), NULL); } //+------------------------------------------------------------------------- // // Function: FTToOLE2Stm, INTERNAL // // Synopsis: Writes a dummy filetime out to an OLE2 stream // // Arguments: [pstm] -- the stream to write to // // Returns: NOERROR on success // STG_E_WRITEFAULT on write failure // // History: dd-mmm-yy Author Comment // 31-Mar-95 scottsk Created // //-------------------------------------------------------------------------- inline static INTERNAL FTToOle2Stm(LPSTREAM pstm) { FILETIME ft = { 0, 0 }; return pstm->Write (&ft, sizeof(FILETIME), NULL); } //+------------------------------------------------------------------------- // // Function: DataObjToOLE2Stm // // Synopsis: Writes a fixed-size data buffer to an OLE2 stream preceded // by a ULONG indicating the number of bytes to follow. // // Returns: NOERROR on success // STG_E_WRITEFAULT on write failure // // History: dd-mmm-yy Author Comment // 18-Feb-94 davepl Code cleanup // Notes: // //-------------------------------------------------------------------------- static INTERNAL DataObjToOLE2Stm(LPSTREAM pstm, const DATA FAR& data) { HRESULT hr; // Write a ULONG indicating the number of bytes to follow if (FAILED(hr = ULToOLE2Stm (pstm, data.m_cbSize))) { return hr; } // If there are any bytes to follow... if (data.m_cbSize) { if (FAILED(hr = pstm->Write (data.m_pv, data.m_cbSize, NULL))) { return hr; } } return NOERROR; } //+------------------------------------------------------------------------- // // Function: SizedDataToOLE1Stm // // Synopsis: Writes a fixed-size data buffer to an OLE1 stream preceded // by a ULONG indicating the number of bytes to follow. // // Parameters: [pos] -- The stream to write to // [data] -- The data object to write out // // Returns: NOERROR on success // STG_E_WRITEFAULT on write failure // // History: dd-mmm-yy Author Comment // 18-Feb-94 davepl Code cleanup // Notes: // //-------------------------------------------------------------------------- static INTERNAL SizedDataToOLE1Stm(LPOLESTREAM pos, const DATA FAR& data) { HRESULT hr = NOERROR; // Ensure the memory we are going to write out is valid Assert (data.m_pv); // Write the ULONG representing the byte count of the sized data if (FAILED(hr = ULToOLE1Stream (pos, data.m_cbSize))) { Assert (0 && "Can't write UL to ole1 stream"); return hr; } if (pos->lpstbl->Put (pos, data.m_pv, data.m_cbSize) < data.m_cbSize) { Assert (0 && "Cant write sized data to ole1 stream"); return ResultFromScode(CONVERT10_E_OLESTREAM_PUT); } return NOERROR; } //+------------------------------------------------------------------------- // // Function: Write20NativeStreams, INTERNAL // // Synopsis: Writes the generic object's native data out to an OLE 2 stream // // Effects: Creates an ILockBytes on the handle to the native data, and // then attempts to create a storage on it. If it can, it uses // the CopyTo interface to write that storage into our OLE 2 // stream. Otherwise, it manually creates a stream in the OLE 2 // storage and dumps the native data into it. // // Arguments: [pstg] -- the OLE 2 storage we are saving genobj to // [genobj] -- the generic object we are writing // // Returns: NOERROR on success // E_OUTOFMEMORY on allocation failure // STG_E_WRITEFAULT on storage write failure // // History: dd-mmm-yy Author Comment // 18-Feb-94 davepl Removed 14 goto's (for better or worse) // See "Notes" for new control flow // 24-Mar-94 alext Fix OLE 1 native case (there was an // extra stream open) // // Notes: There are two possible major codepaths based on the creation // of the Stg on ILockBytes. The outcome is handled by a // switch statement, and both the TRUE and FALSE cases are // loaded with break statements that will bail out to the // bottom of the function on any failure. This gives us a // single entry and exit point, without all the gotos // //-------------------------------------------------------------------------- static INTERNAL Write20NativeStreams(LPSTORAGE pstg, const GENOBJ FAR& genobj) { LPLOCKBYTES plkbyt = NULL; LPSTORAGE pstgNative = NULL; LPSTREAM pstmNative = NULL; HRESULT hr = NOERROR; // Create an ILockBytes instance on our generic object's native data if (SUCCEEDED(hr = CreateILockBytesOnHGlobal (genobj.m_dataNative.m_h, FALSE, &plkbyt))) { // If the ILockBytes appears to contain an IStorage, then this was // an OLE 2 object "hiding" within the OLE 1 stream as native data switch ((DWORD)(S_OK == StgIsStorageILockBytes (plkbyt))) { case (TRUE): // Open the IStorage contained in the ILockBytes if (FAILED(hr = StgOpenStorageOnILockBytes (plkbyt, (LPSTORAGE)NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT, (SNB)NULL, 0, &pstgNative))) { LEDebugOut(( DEB_ERROR, "Can't open storage on ILBytes at line %d in %s\n", __LINE__, __FILE__)); break; // on failure fall through to error return } // Remove the stream from the native data if (FAILED(hr = UtDoStreamOperation(pstgNative, NULL, // pstgDst OPCODE_REMOVE, // operation STREAMTYPE_CACHE))) // stream { LEDebugOut(( DEB_ERROR, "OPCODE REMOVE stream op failed at line %d in %s\n", __LINE__, __FILE__)); break; // on failure fall through to error return } // Copy the "hidden" IStorage to our destination storage if (FAILED(hr = pstgNative->CopyTo (0, NULL,(SNB)NULL, pstg))) { LEDebugOut(( DEB_ERROR, "CopyTo member fn failed at line %d in %s\n", __LINE__, __FILE__)); break; // on failure fall through to error return } break; // end case TRUE case FALSE: // This is the typical case, where the OLE 1 stream had just // plain old native data, so write it to a stream inside our // output IStorage and call it OLE10_NATIVE_STREAM ULONG cb; LPVOID pv = genobj.m_dataNative.m_pv; if (NULL == pv) { hr = ResultFromScode(E_OUTOFMEMORY); break; } // Create the new stream to hold the native data if (FAILED(hr = OpenOrCreateStream (pstg, OLE10_NATIVE_STREAM, &pstmNative))) { break; // on failure fall through to error return } // Write the length of the native data to the stream if (FAILED(hr = pstmNative->Write (&genobj.m_dataNative.m_cbSize, sizeof(ULONG), &cb))) { break; // on failure fall through to error return } // Now write the actual native data if (FAILED(hr = pstmNative->Write (pv, genobj.m_dataNative.m_cbSize, &cb))) { break; // on failure fall through to error return } // Write out the item name if (genobj.m_szItem) { ULONG cchItem; LPSTR pszAnsiItem; int cbWritten; // We need to convert m_szItem from Wide to Ansi // The ANSI string is bounded by the byte length of the // Unicode string (one Unicode character maximally translates // to one double-byte char, so we just use that length cchItem = lstrlenW(genobj.m_szItem) + 1; pszAnsiItem = (LPSTR) PrivMemAlloc(cchItem * sizeof(OLECHAR)); if (NULL == pszAnsiItem) { hr = E_OUTOFMEMORY; break; } // We've got out buffer and our length, so do the conversion now // We don't need to check for cbSize == FALSE since that was // already done during the length test, but we need to check // for substitution. Iff this call sets the fDefChar even when // only doing a length check, these two tests could be merged, // but I don't believe this is the case. BOOL fDefUsed = 0; cbWritten = WideCharToMultiByte(CP_ACP, // Code Page ANSI 0, // No flags genobj.m_szItem, // Input OLESTR cchItem, // Input len (auto detect) pszAnsiItem, // Output buffer cchItem * sizeof(OLECHAR), // Output len NULL, // Default char (use system's) &fDefUsed); // Flag: Default char used // If number of bytes converted was 0, we failed if ((FALSE == cbWritten) || fDefUsed) { hr = ResultFromScode(E_UNSPEC); } else { // Write the size of the string (including null terminator) to stream hr = StSave10ItemName(pstg, pszAnsiItem); } PrivMemFree(pszAnsiItem); if (FAILED(hr)) { break; // on failure fall through to error return } } break; } // end switch } // end if // Free up any resources that may have been allocated in any of the // code paths above if (NULL != plkbyt) { plkbyt->Release(); } if (NULL != pstgNative) { pstgNative->Release(); } if (NULL != pstmNative) { pstmNative->Release(); } return hr; } //+------------------------------------------------------------------------- // // Function: wConvertIStorageToOLESTREAM, INTERNAL // // Synopsis: Worker function; brings object from the IStorage into // the internal generic object representation // // Arguments: [pstg] -- the IStorage the object resides in // [polestream]-- the OLE 1 stream it will be going to // [pgenobj] -- the generic object to hold the internal rep // // Returns: NOERROR on success // STG_E_FILENOTFOUND bad IStorage // CONVERT10_E_STG_NO_STD_STREAM the IStorage was missing one // of the required standard streams // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Code cleanup and documentation // // Notes: // //-------------------------------------------------------------------------- INTERNAL wConvertIStorageToOLESTREAM ( LPSTORAGE pstg, LPOLESTREAM polestream, PGENOBJ pgenobj ) { SCODE scode = S_OK; VDATEIFACE (pstg); // Ensure that all of the pointers are valid #if DBG==1 if (!IsValidReadPtrIn (polestream, sizeof(OLESTREAM)) || !IsValidReadPtrIn (polestream->lpstbl, sizeof(OLESTREAMVTBL)) || !IsValidCodePtr ((FARPROC)polestream->lpstbl->Put)) { LEDebugOut(( DEB_ERROR, "Bad OLESTREAM at line %d in %s\n", __LINE__, __FILE__)); return ResultFromScode (E_INVALIDARG); } #endif scode = GetScode (StorageToGenericObject (pstg, pgenobj)); // If the storage was not there, modify the return code to // make it specific to the conversion process, otherwise just // return whatever error code came back. if (scode != S_OK) { if (scode == STG_E_FILENOTFOUND) { return ResultFromScode(CONVERT10_E_STG_NO_STD_STREAM); } else { return ResultFromScode(scode); } } return NOERROR; } //+------------------------------------------------------------------------- // // Function: OleConvertIStorageToOLESTREAM, STDAPI // // Synopsis: Reads an object from an IStorage into a generic internal // representation, then writes it back out to an OLE 1 stream // // Arguments: [pstg] -- the IStorage to read from // [polestream] -- the OLESTREAM to write to // // Returns: NOERROR on success // CONVERT10_E_STG_NO_STD_STREAM when one of the needed streams // inside the IStorage was not // present // E_INVALIDARG bad input argument // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Cleanup and documentation // // Notes: // //-------------------------------------------------------------------------- STDAPI OleConvertIStorageToOLESTREAM(LPSTORAGE pstg, LPOLESTREAM polestream) { OLETRACEIN((API_OleConvertIStorageToOLESTREAM, PARAMFMT("pstg= %p, polestream= %p"), pstg, polestream)); LEDebugOut((DEB_TRACE, "%p _IN OleConvertIStorageToOLESTREAM (" " %p , %p )\n", 0 /*function*/, pstg, polestream )); CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IStorage,(IUnknown **)&pstg); HRESULT hr; CGenericObject genobj; // Read from the IStorage into the generic object hr = wConvertIStorageToOLESTREAM(pstg, polestream, &genobj); if (FAILED(hr)) { goto errRtn; } // Write from the generic object out to the OLE 1 stream hr = GenericObjectToOLESTREAM (genobj, polestream); errRtn: LEDebugOut((DEB_TRACE,"%p OUT OleConvertIStorageToOLESTREAM ( %lx ) " "\n", 0 /*function*/, hr)); OLETRACEOUT((API_OleConvertIStorageToOLESTREAM, hr)); return hr; } //+------------------------------------------------------------------------- // // Function: wFillPpres, INTERNAL // // Synopsis: Fills in the generic object's presentation data by // building a presentation out of the native data // // Arguments: [pstg] -- the IStorage we are reading from // [pgenobj] -- the generic object // [cfFormat] -- what clipboard format is being used // [fOle10Native] -- flag: is this OLE 1 native data? // // Returns: NOERROR on success // E_OUTOFMEMORY can't allocate mem for PRES member // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Code cleanup, documentation // 19-Jul-94 davepl Fixed HMETAFILE cases // // Notes: Since most of this code treats HMETAFILE handles and // HGLOBALS indentically, we need to special case the // the HMETAFILE case by marking the pointer with a // special value // //-------------------------------------------------------------------------- static INTERNAL wFillPpres( LPSTORAGE pstg, PGENOBJ pgenobj, CLIPFORMAT cfFormat, BOOL fOle10Native) { pgenobj->m_ppres = new PRES; if (pgenobj->m_ppres == NULL) { return ResultFromScode(E_OUTOFMEMORY); } // Set the format tag and clipboard format in the PRES member pgenobj->m_ppres->m_format.m_cf = cfFormat; pgenobj->m_ppres->m_format.m_ftag = ftagClipFormat; // Build the presentation based on the object's native data HANDLE hpres = UtGetHPRESFromNative(pstg, NULL, pgenobj->m_ppres->m_format.m_cf, fOle10Native); void * lppres = NULL; if (hpres == NULL) { return NOERROR; } // Lock the DIB or the METAFILEPICT structure lppres = GlobalLock(hpres); if (NULL == lppres) { goto errRtn; } if (cfFormat == CF_DIB) { // If it's a DIB, fill in the extents LPBITMAPINFOHEADER lpbmi = (LPBITMAPINFOHEADER) lppres; UtGetDibExtents(lpbmi, (LPLONG) &(pgenobj->m_ppres->m_ulWidth), (LPLONG) &(pgenobj->m_ppres->m_ulHeight)); GlobalUnlock(hpres); pgenobj->m_ppres->m_data.m_h = hpres; pgenobj->m_ppres->m_data.m_cbSize = (ULONG) GlobalSize(pgenobj->m_ppres->m_data.m_h); pgenobj->m_ppres->m_data.m_pv = GlobalLock(pgenobj->m_ppres->m_data.m_h); } else if (cfFormat == CF_METAFILEPICT) { LPMETAFILEPICT lpmfp = (LPMETAFILEPICT) lppres; // If it's a METAFILE, fill in the width, height pgenobj->m_ppres->m_ulWidth = (ULONG) lpmfp->xExt; pgenobj->m_ppres->m_ulHeight = (ULONG) lpmfp->yExt; pgenobj->m_ppres->m_data.m_h = lpmfp->hMF; GlobalFree(hpres); hpres = NULL; // We place a special known value in the pointer field // to indicate that the associated handle is a metafile // handle (as opposed to a global memory handle), which // signals us to special case its cleanup. pgenobj->m_ppres->m_data.m_pv = METADATAPTR; // We cannot merely GlobalSize() the HMETAFILE, so we // ask the GDI how many bytes we will need to store the // data. pgenobj->m_ppres->m_data.m_cbSize = GetMetaFileBitsEx((HMETAFILE) pgenobj->m_ppres->m_data.m_h, 0, NULL); if (0 == pgenobj->m_ppres->m_data.m_cbSize) { pgenobj->m_ppres->m_data.m_h = NULL; goto errRtn; } } else { goto errRtn; } return NOERROR; errRtn: if (hpres) { Verify(GlobalUnlock(hpres)); GlobalFree(hpres); } delete pgenobj->m_ppres; pgenobj->m_ppres = NULL; return ResultFromScode(E_OUTOFMEMORY); } //+------------------------------------------------------------------------- // // Function: StorageToGenericObject, INTERNAL // // Synopsis: Read an object from an IStorage into the generic object, // and set up the format type, native and pres data. // // Arguments: [pstg] -- the IStorage we are reading from // [pgenobj] -- the generic object we are reading into // // Returns: NOERROR on success // various possible errors from lower-level fns // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Code cleanup and documentation // // Notes: // //-------------------------------------------------------------------------- static INTERNAL StorageToGenericObject(LPSTORAGE pstg, PGENOBJ pgenobj) { CLSID clsid; CLIPFORMAT cf = NULL; BOOL fObjFmtKnown = FALSE; HRESULT hr; // Get the class ID from the IStorage if (FAILED(hr = ReadRealClassStg (pstg, &clsid))) { return hr; } // Set the class ID in our generic object if (CLSID_StaticMetafile == clsid || CLSID_StaticDib == clsid) { if (CLSID_StaticMetafile == clsid) { cf = CF_METAFILEPICT; } else { cf = CF_DIB; } fObjFmtKnown = TRUE; pgenobj->m_class.Set(clsid, NULL); pgenobj->m_fStatic = TRUE; } else { if (FAILED(hr = pgenobj->m_class.Set (clsid, pstg))) { return hr; } } // Get the OLE version, flags, update opts, and moniker SCODE sc = GetScode (Read20OleStream (pstg, pgenobj)); // It is okay for the Ole Stream to be missing. if (sc != S_OK) { if (sc != STG_E_FILENOTFOUND) { return ResultFromScode (sc); } } // Read the native data into the generic object if (FAILED(hr = Read20NativeStreams (pstg, &(pgenobj->m_dataNative)))) { return hr; } // Try to ascertain the clipboard format if (cf == 0) { if (clsid == CLSID_PBrush) { cf = CF_DIB; } else if (clsid == CLSID_MSDraw) { cf = CF_METAFILEPICT; } else { ReadFmtUserTypeStg (pstg, &cf, NULL); } fObjFmtKnown = (cf == CF_METAFILEPICT || cf == CF_DIB); } // Read the presentation data if possible if (FAILED(hr = Read20PresStream (pstg, pgenobj, fObjFmtKnown))) { return hr; } // If we don't have a presentation, it might be a PBrush object, // which is OK because OLE 1 DLLs know how to draw them based on // the native data. Otherwise, we will try and create a presentation // based on the native data. if (pgenobj->m_ppres == NULL) { if (clsid == CLSID_PBrush) { return NOERROR; } if (cf == CF_METAFILEPICT || cf == CF_DIB) { if (FAILED(hr=wFillPpres(pstg,pgenobj,cf,clsid == CLSID_MSDraw))) { return hr; } } } return NOERROR; } //+------------------------------------------------------------------------- // // Function: GenericObjectToOLESTREAM, INTERNAL // // Synopsis: Writes the interal object representation out to an OLE1 // stream. // // Arguments: [genobj] -- the object to write out // [pos] -- the OLE 1 stream to write to // // Returns: NOERROR on success // // History: dd-mmm-yy Author Comment // 22-Feb-94 davepl 32-bit port // Notes: // //-------------------------------------------------------------------------- static INTERNAL GenericObjectToOLESTREAM( const GENOBJ FAR& genobj, LPOLESTREAM pos) { HRESULT hr; if (genobj.m_fStatic) { return PutPresentationObject (pos, genobj.m_ppres, genobj.m_class, TRUE /* fStatic*/ ); } // OLE version if (FAILED(hr = ULToOLE1Stream (pos, dwVerToFile))) { return hr; } // Format ID for embedded or linked object if (FAILED(hr = ULToOLE1Stream (pos, genobj.m_fLink ? FMTID_LINK : FMTID_EMBED))) { return hr; } // We must have the class id string by this point Assert (genobj.m_class.m_szClsid); // Write out the class ID string if (FAILED(hr = StringToOLE1Stm (pos, genobj.m_class.m_szClsid))) { return hr; } // Write out the topic string if (FAILED(hr = StringToOLE1Stm (pos, genobj.m_szTopic))) { return hr; } // Write out the item string if (FAILED(hr = StringToOLE1Stm (pos, genobj.m_szItem))) { return hr; } // Write out the update options, network info for a link, // or the native data for an embedded object if (genobj.m_fLink) { // Network information if (FAILED(hr = PutNetworkInfo (pos, genobj.m_szTopic))) { return hr; } // Link update options if (FAILED(hr = ULToOLE1Stream (pos, genobj.m_lnkupdopt))) { return hr; } } else { if (FAILED(hr = SizedDataToOLE1Stm (pos, genobj.m_dataNative))) { return hr; } } // Write out the presentation data return PutPresentationObject (pos, genobj.m_ppres, genobj.m_class); } //+------------------------------------------------------------------------- // // Function: PutNetworkInfo, INTERNAL // // Synopsis: If needed, converts a DOS-style path to a proper network // path. In any case, writes network path to OLE 1 stream // // Arguments: [pos] -- the OLE 1 stream we are writing to // [szTopic] -- the topic string for this object // // Returns: NOERROR on success // Various possible I/O errors on write // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Code cleanup and documentation // // Notes: // //-------------------------------------------------------------------------- static INTERNAL PutNetworkInfo(LPOLESTREAM pos, LPOLESTR szTopic) { LPOLESTR szNetName = NULL; HRESULT hr = NOERROR; // If we have an X:\ style path, we want to convert that // to a proper network name if (szTopic && IsCharAlphaW(szTopic[0]) && szTopic[1]==':') { OLECHAR szBuf[80]; DWORD u; OLECHAR szDrive[3]; szDrive[0] = (OLECHAR)CharUpperW((LPWSTR)szTopic[0]); szDrive[1] = ':' ; szDrive[2] = '\0'; if (GetDriveType (szDrive) == DRIVE_REMOTE && OleWNetGetConnection (szDrive, szBuf, &u) == WN_SUCCESS) { szNetName =szBuf; } } // We now have the network name, so write it out to OLE 1 stream if (FAILED(hr = StringToOLE1Stm (pos, szNetName))) { return hr; } // Network type, driver version number, but we have to pad for // the space anyway if (FAILED(hr = ULToOLE1Stream (pos, 0L))) { return hr; } Assert (hr == NOERROR); return hr; } //+------------------------------------------------------------------------- // // Function: OpenStream, INTERNAL // // Synopsis: Opens a stream in SHARE_EXCLUSIVE, READ mode // // Arguments: [pstg] -- the storage the stream resides in // [szName] -- the name of the stream // [ppstm] -- out parameter for stream // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Code cleanup and document // // Notes: // //-------------------------------------------------------------------------- static inline INTERNAL OpenStream( LPSTORAGE pstg, LPOLESTR szName, LPSTREAM FAR* ppstm) { return pstg->OpenStream (szName, NULL, STGM_SHARE_EXCLUSIVE| STGM_READ, 0, ppstm); } //+------------------------------------------------------------------------- // // Function: ReadRealClassStg, INTERNAL // // Synopsis: Reads the _real_ class of the object. ie: if the class is // StdOleLink, we need to find out the class of the object // to which this is linked // // Arguments: pstg -- the storage to read from // pclsid -- caller's CLSID holder // // Returns: NOERROR on success // // History: dd-mmm-yy Author Comment // 04-Mar-04 davepl 32-bit port // Notes: // //-------------------------------------------------------------------------- static INTERNAL ReadRealClassStg(LPSTORAGE pstg, LPCLSID pclsid) { LPSTREAM pstm = NULL; HRESULT hr = NOERROR; // Get the class ID from the IStorage if (FAILED(hr = ReadClassStg (pstg, pclsid))) { return hr; } // If it's a link, we have to figure out what class its a link _to_ if (CLSID_StdOleLink == *pclsid) { LPMONIKER pmk = NULL; if (FAILED(hr = ReadOleStg (pstg, NULL, NULL, NULL, NULL, &pstm))) { return hr; } if (FAILED(hr = ReadMonikerStm (pstm, &pmk))) { goto errRtn; } if (pmk) { pmk->Release(); } if (FAILED(hr = ReadMonikerStm (pstm, &pmk))) { goto errRtn; } if (pmk) { pmk->Release(); } // Read "last class" if (FAILED(hr = ReadM1ClassStm (pstm, pclsid))) { goto errRtn; } } errRtn: if (pstm) { pstm->Release(); } return hr; } //+------------------------------------------------------------------------- // // Function: Read20OleStream, INTERNAL // // Synopsis: Reads the update options and absolute source class from // an OLE 2 object // // Arguments: pstg -- the IStorage to read from // pgenobj -- the genobj we are reading into // // Returns: NOERROR on success // // History: dd-mmm-yy Author Comment // 06-Mar-94 davepl 32-bit port // Notes: // //-------------------------------------------------------------------------- static INTERNAL Read20OleStream(LPSTORAGE pstg, PGENOBJ pgenobj) { LPMONIKER pmk = NULL; HRESULT hr = NOERROR; LPSTREAM pstm = NULL; ULONG ul = (ULONG) -1L; CLSID clsidLast; if (SUCCEEDED(hr = OpenStream (pstg, OLE_STREAM, &pstm))) { // OLE version if (SUCCEEDED(hr = OLE2StmToUL (pstm, NULL))) { // Object flags if (SUCCEEDED(hr = OLE2StmToUL (pstm, &ul))) { if (ul & OBJFLAGS_LINK) { pgenobj->m_fLink = TRUE; } // Update options hr = OLE2StmToUL (pstm, &ul); } } } // If no errors so far... // If this is a link, get the update options if (SUCCEEDED(hr) && pgenobj->m_fLink) { switch (ul) { case OLEUPDATE_ALWAYS: pgenobj->m_lnkupdopt = UPDATE_ALWAYS; break; case OLEUPDATE_ONCALL: pgenobj->m_lnkupdopt = UPDATE_ONCALL; break; default: AssertSz (0, "Warning: Invalid update options in Storage"); hr = ResultFromScode(CONVERT10_E_STG_FMT); } } if (SUCCEEDED(hr)) // Only continue if no failures so far { // Reserved (was view format) if (SUCCEEDED(hr = OLE2StmToUL (pstm, NULL))) { if (pgenobj->m_fLink) { // All 4 of these calls must succeed or we simply fall // through to the cleanup code // ignore relative moniker if (SUCCEEDED(hr = OLE2StmToMoniker (pstm, NULL)) && // ignore relative source moniker SUCCEEDED(hr = OLE2StmToMoniker (pstm, NULL)) && // get absolute source moniker SUCCEEDED(hr = OLE2StmToMoniker (pstm, &pmk)) && // get class from abs moniker SUCCEEDED(hr = ReadM1ClassStm (pstm, &clsidLast)) ) { hr = MonikerIntoGenObj (pgenobj, clsidLast, pmk); } } } } // Clean up any resources and return status to caller if (pstm) { pstm->Release(); } if (pmk) { pmk->Release(); } return hr; } //+------------------------------------------------------------------------- // // Function: OLE2StmToMoniker, INTERNAL // // Synopsis: Calls ReadMonikerStm to get a moniker from a stream, // and if the ppmk parameter was NULL, it does a Release() // on the moniker object immediately, otherwise sets the // caller's pointer to point to the moniker that was read. // // Arguments: [pstm] -- the stream to read the moniker from // [ppmk] -- points to caller's moniker ptr // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Code cleanup and documentation // // Notes: // //-------------------------------------------------------------------------- static INTERNAL OLE2StmToMoniker(LPSTREAM pstm, LPMONIKER FAR* ppmk) { LPMONIKER pmk = NULL; HRESULT hr = NOERROR; if (FAILED(hr = ReadMonikerStm (pstm, &pmk))) { return hr; } if (ppmk) // If the callers wanted a result, return the { // moniker as an out parameter *ppmk = pmk; } else // Otherwise, release it immediately and { // return to caller if (pmk) { pmk->Release(); } } return NOERROR; } //+------------------------------------------------------------------------- // // Function: ReadFormat, INTERNAL // // Synopsis: Reads the format ID type from the stream, and based on that, // reads the format ID from the stream. // // Arguments: [pstm] -- the stream to read from // [pformat] -- caller's format member object // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Code cleanup and documentation // // Notes: The first ULONG indicates the type (standard clipboard, // Mac, NULL, or string) of the identifier // //-------------------------------------------------------------------------- static INTERNAL ReadFormat(LPSTREAM pstm, PFORMAT pformat) { ULONG ul; HRESULT hr = NOERROR; // Get the format ID type indicator if (FAILED(hr = OLE2StmToUL (pstm, &ul))) { return hr; } // The first ULONG indicates what kind of format ID will // found in the stream: // // -1 => A standard clipboard format ID // -2 => A Macintosh format // 0 => NULL format // >0 => The number of bytes of the text string // identifier to follow switch ((signed long)ul) { case -1L: // Standard clipboard format ULONG ulClipFormat; pformat->m_ftag = ftagClipFormat; if (FAILED(hr = OLE2StmToUL (pstm, &ulClipFormat))) { return hr; } pformat->m_cf = (CLIPFORMAT) ulClipFormat; break; case -2L: // Macintosh format return ResultFromScode(CONVERT10_E_STG_FMT); case 0: // NULL format pformat->m_ftag = ftagNone; pformat->m_cf = 0; return NOERROR; default: // ul == size of string (format name) pformat->m_ftag = ftagString; if (FAILED(hr = OLE2StmToSizedData (pstm, &(pformat->m_dataFormatString), 0, ul))) { return hr; } break; } return NOERROR; } #ifdef _OBSOLETE //+------------------------------------------------------------------------- // // Function: WriteFormat, INTERNAL // // Synopsis: Depending on what kind of format (standard cf, string, etc) // the format object holds, this fn writes out the appropriate // information to the stream // // Arguments: [pstm] -- the stream to write to // [format] -- the format object to get info from // // Returns: NOERROR on success // E_UNEXPECTED for a NULL format tag // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Code cleanup and documentation // Notes: //-------------------------------------------------------------------------- static INTERNAL WriteFormat(LPSTREAM pstm, const FORMAT FAR& format) { HRESULT hr; switch (format.m_ftag) { case ftagNone: Assert (0 && "Cant write a NULL format tag"); return ResultFromScode (E_UNEXPECTED); case ftagClipFormat: if (FAILED(hr = ULToOLE2Stm (pstm, (ULONG) -1L))) { return hr; } if (FAILED(hr = ULToOLE2Stm (pstm, format.m_cf))) { return hr; } break; case ftagString: if (FAILED(hr=DataObjToOLE2Stm(pstm,format.m_dataFormatString))) { return hr; } break; default: AssertSz (0, "invalid m_ftag value"); return ResultFromScode (E_UNEXPECTED); } return NOERROR; } #endif // _OBSOLETE //+------------------------------------------------------------------------- // // Function: ReadDibAsBitmap, INTERNAL // // Synopsis: Reads a DIB from an OLE 2 stream and stores it as a // Bitmap in a DATA structure // // Arguments: [pstm] -- the OLE 2 stream to read from // [pdata] -- the data object to hold the bitmap // // Returns: NOERROR on success // CONVERT10_E_STG_DIB_TO_BITMAP conversion failure // E_OUTOFMEMORY allocation failure // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Code cleanup and documentation // // Notes: // //-------------------------------------------------------------------------- static INTERNAL ReadDibAsBitmap(LPSTREAM pstm, PDATA pdata) { DATA dataDib; ULONG cb; ULONG cbBits; ULONG cbBitsFake; BITMAP bm; HBITMAP hBitmap = NULL; HRESULT hr = NOERROR; HGLOBAL hBits = NULL; LPBYTE pBits = NULL; Assert (pdata&&pdata->m_cbSize==0&&pdata->m_h==NULL&&pdata->m_pv==NULL); // Read the DIB into our local DATA object if (FAILED(hr = OLE2StmToSizedData (pstm, &dataDib))) { return hr; } // Convert the DIB to a Bitmap hBitmap = UtConvertDibToBitmap (dataDib.m_h); if (NULL == hBitmap ) { return ResultFromScode(CONVERT10_E_STG_DIB_TO_BITMAP); } if (0 == GetObject (hBitmap, sizeof(BITMAP), &bm)) { return ResultFromScode(CONVERT10_E_STG_DIB_TO_BITMAP); } cbBits = (DWORD) bm.bmHeight * (DWORD) bm.bmWidthBytes * (DWORD) bm.bmPlanes; // There was a bug in OLE 1.0. It calculated the size of a bitmap // as Height * WidthBytes * Planes * BitsPixel. // So we need to put that many bytes here even if most of the end of that // data block is garbage. Otherwise OLE 1.0 will try to read too many // bytes of the OLESTREAM as bitmap bits. cbBitsFake = cbBits * (DWORD) bm.bmBitsPixel; // Allocate enough memory for our resultant BITMAP & header hBits = GlobalAlloc (GMEM_MOVEABLE, cbBitsFake + sizeof (BITMAP)); if (NULL == hBits) { if (hBitmap) { Verify (DeleteObject (hBitmap)); } return ResultFromScode(E_OUTOFMEMORY); } // Get a pointer to the memory pBits = (LPBYTE) GlobalLock (hBits); if (NULL == pBits) { if (hBitmap) { Verify (DeleteObject (hBitmap)); } GlobalFree(hBits); return ResultFromScode(E_OUTOFMEMORY); } // Copy the raw bitmap data cb = GetBitmapBits (hBitmap, cbBits, pBits + sizeof(BITMAP)); if (cb != cbBits) { if (hBitmap) { Verify (DeleteObject (hBitmap)); } GlobalFree(hBits); return ResultFromScode(CONVERT10_E_STG_DIB_TO_BITMAP); } // Set the caller's pointer to point to the bitmap *((BITMAP FAR*)pBits) = bm; pdata->m_h = hBits; pdata->m_pv = pBits; pdata->m_cbSize = cbBitsFake + sizeof(BITMAP); return NOERROR; } //+------------------------------------------------------------------------- // // Function: Read20PresStream, INTERNAL // // Synopsis: Reads presentation data from an IStorage into a // generic object // // Arguments: [pstg] -- the IStorage holding the pres stream // [pgenobj] -- the generic object to read to // [fObjFmtKnown] -- flag: Do we know the object format? // // Returns: NOEROR on success // E_OUTOFMEMORY on allocation failure // // History: dd-mmm-yy Author Comment // 22-Feb-94 davepl Code cleanup and documentation // Notes: // //-------------------------------------------------------------------------- static INTERNAL Read20PresStream( LPSTORAGE pstg, PGENOBJ pgenobj, BOOL fObjFmtKnown) { HRESULT hr = NOERROR; LPSTREAM pstm = NULL; // Find the best presentation stream in this IStorage if (FAILED(hr = FindPresStream (pstg, &pstm, fObjFmtKnown))) { return hr; } if (pstm) { // Allocate a generic presentation object Assert (NULL==pgenobj->m_ppres); pgenobj->m_ppres = new PRES; if (NULL == pgenobj->m_ppres) { pstm->Release(); return ResultFromScode(E_OUTOFMEMORY); } } else { // No presentation stream Assert (NULL == pgenobj->m_ppres); return NOERROR; } // read the format if (FAILED(hr = ReadFormat (pstm, &(pgenobj->m_ppres->m_format)))) { pstm->Release(); return hr; } // This is the fix for Bug 4020, highly requested by Access if (pgenobj->m_ppres->m_format.m_ftag == ftagNone) { // NULL format delete pgenobj->m_ppres; pgenobj->m_ppres = NULL; Assert (hr == NOERROR); pstm->Release(); return hr; } // Each of the following calls must succeed in order for the following // one to be executed; if any fails, the if( .. && ..) will be false // and hr will be set to the error that caused the failure // target device if (SUCCEEDED(hr = OLE2StmToSizedData (pstm, NULL, 4)) && // aspect SUCCEEDED(hr = OLE2StmToUL (pstm, NULL)) && // lIndex SUCCEEDED(hr = OLE2StmToUL (pstm, NULL)) && // cache flags SUCCEEDED(hr = OLE2StmToUL (pstm, NULL)) && // compression SUCCEEDED(hr = OLE2StmToUL (pstm, NULL)) && // width SUCCEEDED(hr = OLE2StmToUL (pstm, &(pgenobj->m_ppres->m_ulWidth)))) { // height hr = OLE2StmToUL (pstm, &(pgenobj->m_ppres->m_ulHeight)); } // We only proceed if everything so far has suceeded if (SUCCEEDED(hr)) { if (pgenobj->m_ppres->m_format.m_ftag == ftagClipFormat && pgenobj->m_ppres->m_format.m_cf == CF_DIB && !pgenobj->m_fStatic) { pgenobj->m_ppres->m_format.m_cf = CF_BITMAP; hr = ReadDibAsBitmap (pstm, &(pgenobj->m_ppres->m_data)); } else { // In most cases, we look for a sized block of data in the // stream. hr = OLE2StmToSizedData (pstm, &(pgenobj->m_ppres->m_data)); } } // Free up the stream and return status to caller if (pstm) { pstm->Release(); } return hr; } //+------------------------------------------------------------------------- // // Function: OLE2StmToSizedData, INTERNAL // // Synopsis: Reads a set amount of data from an OLE 2 stream into a // DATA structure. If the number of bytes are not known // ahead of time, the data length is pulled as the first // ULONG at the current stream position. // // Arguments: [pstm] -- the stream to read from // [pdata] -- the DATA structure to read to // [cbSizeDelta] -- amount to be subtracted from // length; used to read target devices // where the length of data includes // prefixed length // [cbSizeKnown] -- number of bytes to read if known // ahead of time // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Code cleanup and documentation // Notes: // //-------------------------------------------------------------------------- static INTERNAL OLE2StmToSizedData( LPSTREAM pstm, PDATA pdata, ULONG cbSizeDelta, // default 0 ULONG cbSizeKnown) // default 0 { ULONG cbSize; ULONG cbRead; LARGE_INTEGER large_integer; HRESULT hr = NOERROR; // If we don't know the data size ahead of time, read it from the stream; // it will be the first ULONG at the current position if (cbSizeKnown) { cbSize = cbSizeKnown; } else { if (FAILED(hr = (OLE2StmToUL (pstm, &cbSize)))) { return hr; } } cbSize -= cbSizeDelta; // If pdata is set, it means we actually do want to read the // data to a buffer, rather than just skip over it (the NULL case) if (pdata) { Assert (pdata->m_cbSize==0 && pdata->m_h==NULL && pdata->m_pv==NULL); // Set the number of bytes in the DATA structure pdata->m_cbSize = cbSize; // If there are any, allocate a buffer and read them. if (cbSize) { // Allocate memory on the DATA handle pdata->m_h = GlobalAlloc (GMEM_MOVEABLE, cbSize); if (NULL == pdata->m_h) { return ResultFromScode(E_OUTOFMEMORY); } // Lock memory in for the read pdata->m_pv = GlobalLock (pdata->m_h); if (NULL == pdata->m_pv) { GlobalFree(pdata->m_h); return ResultFromScode(E_OUTOFMEMORY); } // Read the data to the buffer if (FAILED(hr = pstm->Read (pdata->m_pv, cbSize, &cbRead))) { GlobalUnlock(pdata->m_h); GlobalFree(pdata->m_h); return hr; } // If we didn't get enough bytes, bail now if (cbRead != cbSize) { GlobalUnlock(pdata->m_h); GlobalFree(pdata->m_h); return ResultFromScode(STG_E_READFAULT); } } else { // We have 0 bytes to read, so mark the // memory handle and ptr as NULL pdata->m_h = NULL; pdata->m_pv = NULL; } } else { // we don't care what the data is, so just skip it LISet32( large_integer, cbSize ); if (FAILED(hr = pstm->Seek (large_integer, STREAM_SEEK_CUR, NULL))) { return hr; } } return NOERROR; } //+------------------------------------------------------------------------- // // Function: RankOfPres, INTERNAL // // Synopsis: Returns a ULONG indicating the relative "goodness" of a // presentation. The preference is, in descending order: // // Type Rank // ---------- ---------- // METAFILE x30000 // DIB x20000 // none x10000 // // Add x200 for fScreenTargDev being set // Add x4 for Content aspect // Add x3 for Thumbnail aspect // Add x2 for Icon aspect // Add x1 for Docprint aspect // // Eg: Metafile in Content aspect, with ScreenTargDev: 30204 // // The whole point of this is that there may be many // presentation streams available in the IStorage. This fn // is used to select the best one. // // Arguments: [format] -- the format tag & type structure // [fScreenTargDev]-- do we have a handle to the target dev // [dwAspect] -- the aspect type // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Code cleanup and documentation // // Notes: // //-------------------------------------------------------------------------- static INTERNAL_(ULONG) RankOfPres( const FORMAT FAR& format, const BOOL fScreenTargDev, const DWORD dwAspect) { ULONG ul = 0L; if (format.m_cf==CF_METAFILEPICT) { ul += 0x030000; } else if (format.m_cf==CF_DIB) { ul += 0x020000; } else if (format.m_ftag != ftagNone) { ul += 0x010000; } ul += (fScreenTargDev + 1) * 0x0100; switch (dwAspect) { case DVASPECT_CONTENT: ul += 0x04; break; case DVASPECT_THUMBNAIL: ul += 0x03; break; case DVASPECT_ICON: ul += 0x02; break; case DVASPECT_DOCPRINT: ul += 0x01; break; } return ul; } //+------------------------------------------------------------------------- // // Function: IsBetter, INTERNAL INLINE // // Synopsis: Calls RankOfPres to determine if one presentation is // better than another // // Effects: // // Arguments: [format] -- the format tag and type // [fScreenTargDev]-- do we have a handle to target device // [dwAspect] -- the aspect of the presentation // [formatBest] -- the best format seen so far // [fScreenTargDevBest] -- flag for best format seen so far // [dwAspectBest] -- the aspect of best format seen so far // // History: dd-mmm-yy Author Comment /// 21-Feb-94 davepl Code cleanup and documentation // // Notes: // //-------------------------------------------------------------------------- inline static INTERNAL_(BOOL) IsBetter( const FORMAT FAR& format, const BOOL fScreenTargDev, const DWORD dwAspect, const FORMAT FAR& formatBest, const BOOL fScreenTargDevBest, const DWORD dwAspectBest) { return RankOfPres (format, fScreenTargDev, dwAspect) > RankOfPres (formatBest, fScreenTargDevBest, dwAspectBest); } //+------------------------------------------------------------------------- // // Function: FindPresStream, INTERNAL // // Synopsis: Enumerates over the streams in an IStorage, looking for // presentation streams. Selects the best stream among // these based on the comparison fn, IsBetter(), which uses // for comparison the criteria established in RankOfPres(). // // Arguments: [pstg] -- the IStorage to look in // [ppstmBest] -- out param for best pres stream // [fObjFmtKnown] is the object format known // // Returns: NOERROR on success // If no presentation is found, it is not an error but // *ppstm is set to NULL. // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Code cleanup and documentation // // Notes: // //-------------------------------------------------------------------------- static INTERNAL FindPresStream( LPSTORAGE pstg, LPSTREAM FAR* ppstmBest, BOOL fObjFmtKnown) { HRESULT hr = NOERROR; LPSTREAM pstm = NULL; IEnumSTATSTG FAR* penumStg = NULL; DWORD dwAspectBest = 0; BOOL fTargDevBest = -1; STATSTG statstg; FORMAT formatBest; Assert (ppstmBest); *ppstmBest = NULL; // Set up the enumeration on the available IStreams in the storage if (FAILED(hr = pstg->EnumElements (NULL, NULL, NULL, &penumStg))) { return hr; } // Enumerate through them and search for the best among all // presentation streams while (penumStg->Next (1, &statstg, NULL) == NOERROR) { // Check to see if this a presentation stream if (lstrlenW(statstg.pwcsName) >= 8 && 0==memcmp(statstg.pwcsName, OLESTR("\2OlePres"), 8*sizeof(WCHAR))) { FORMAT format; DATA dataTargDev; DWORD dwAspect; // Open the presentation stream if (FAILED(hr = OpenStream (pstg, statstg.pwcsName, &pstm))) { goto errRtn; } // Read the format from the pres stream if (FAILED(hr = ReadFormat (pstm, &format))) { goto errRtn; } // Read the target device from the pres stream if (FAILED(hr = OLE2StmToSizedData (pstm, &dataTargDev, 4))) { goto errRtn; } // Get the aspect from the pres stream if (FAILED(hr = OLE2StmToUL (pstm, &dwAspect))) { goto errRtn; } // Check to see if this presentation stream is better // than the best seen so far if (IsBetter (format, dataTargDev.m_h==NULL, dwAspect, formatBest, fTargDevBest, dwAspectBest)) { // If it is, we can release the "best" if (*ppstmBest) { (*ppstmBest)->Release(); } // The king is dead, long live the king *ppstmBest = pstm; pstm->AddRef(); formatBest = format; fTargDevBest = (dataTargDev.m_h==NULL); dwAspectBest = dwAspect; } pstm->Release(); pstm = NULL; } PubMemFree(statstg.pwcsName); statstg.pwcsName = NULL; } // On Windows For Workgroups machines, statstg.pwcsName!=NULL when // Next() returns S_FALSE. Bug 3370. statstg.pwcsName = NULL; errRtn: if (statstg.pwcsName) { PubMemFree(statstg.pwcsName); } if (*ppstmBest) { if (dwAspectBest != DVASPECT_CONTENT && fObjFmtKnown) { // then don't use this stream, we will get the presentaion // from the CONTENTS stream (*ppstmBest)->Release(); *ppstmBest = NULL; } else { LARGE_INTEGER large_integer; LISet32( large_integer, 0); hr = (*ppstmBest)->Seek(large_integer, STREAM_SEEK_SET,NULL); } } if (penumStg) { penumStg->Release(); } if (pstm) { pstm->Release(); } return hr; } //+------------------------------------------------------------------------- // // Function: Reads native data from an OLE 2 stream // // Synopsis: If the fn can find OLE 1 native data in the stream, it is // read out; otherwise, it attempts to create an IStorage // in memory on the data in the stream, and then uses the // CopyTo interface to extract the data. // // Arguments: [pstg] -- The OLE 2 IStorage to look in // [pdata] -- The DATA object to read native data to // // Returns: NOERROR on success // STG_E_READFAULT on read failure // E_OUTOFMEMORY on allocation failure // // History: dd-mmm-yy Author Comment // 21-feb-94 davepl Cleaned up and documented code // Notes: // //-------------------------------------------------------------------------- static INTERNAL Read20NativeStreams(LPSTORAGE pstg, PDATA pdata) { LPSTREAM pstm = NULL; LPLOCKBYTES plkbyt = NULL; LPSTORAGE pstgNative= NULL; HRESULT hr = NOERROR; // There are two possible codepaths based on the success of // OpenStream. If it is true, it is because we were able to // open the OLE 1 presentation stream in the OLE 2 object. // Thus, it must have been an OLE 1 object "hidden" in // an OLE 2 IStream. // // If that fails, we create an in-memory IStorage based on // the native data and use the CopyTo member to extract the // natice data. // // If we experience a failure at any point, a "break" statement // bails us out past everything to the error cleanup and return // code following the closure of the switch() statement. switch ((DWORD)(NOERROR==OpenStream (pstg, OLE10_NATIVE_STREAM, &pstm))) { case TRUE: { // This was a 1.0 object "hidden" inside a 2.0 IStorage ULONG cbRead; Assert (pdata->m_cbSize==0 && NULL==pdata->m_h && NULL==pdata->m_pv); // read size if (FAILED(hr = pstm->Read(&(pdata->m_cbSize),sizeof(DWORD),&cbRead))) { break; } if (sizeof(DWORD) != cbRead) { hr = ResultFromScode (STG_E_READFAULT); break; } // allocate memory to store copy of stream pdata->m_h = GlobalAlloc (GMEM_MOVEABLE, pdata->m_cbSize); if (NULL == pdata->m_h) { hr = ResultFromScode(E_OUTOFMEMORY); break; } pdata->m_pv = GlobalLock (pdata->m_h); if (NULL == pdata->m_pv) { hr = ResultFromScode(E_OUTOFMEMORY); break; } // read stream if (FAILED(hr = pstm->Read(pdata->m_pv,pdata->m_cbSize,&cbRead))) { break; } if (pdata->m_cbSize != cbRead) { hr= ResultFromScode (STG_E_READFAULT); break; } break; } case FALSE: { const DWORD grfCreateStg = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_CREATE ; // Copy pstg into pstgNative, thereby removing slack and // giving us access to the bits via an ILockBytes if (FAILED(hr = CreateILockBytesOnHGlobal (NULL, FALSE, &plkbyt))) { break; } if (FAILED(hr = StgCreateDocfileOnILockBytes (plkbyt, grfCreateStg, 0, &pstgNative))) { break; } if (FAILED(hr = pstg->CopyTo (0, NULL, 0, pstgNative))) { break; } // Set pdata->m_cbSize STATSTG statstg; if (FAILED(hr = plkbyt->Stat (&statstg, 0))) { break; } pdata->m_cbSize = statstg.cbSize.LowPart; // Set pdata->m_h if (FAILED(hr = GetHGlobalFromILockBytes (plkbyt, &(pdata->m_h)))) { break; } Assert (GlobalSize (pdata->m_h) >= pdata->m_cbSize); // Set pdata->m_pv pdata->m_pv = GlobalLock (pdata->m_h); if (NULL == pdata->m_pv) { hr = ResultFromScode(E_OUTOFMEMORY); break; } } // end case } // end switch // Cleanup and return status to caller if (pstm) { pstm->Release(); } if (plkbyt) { plkbyt->Release(); } if (pstgNative) { pstgNative->Release(); } return hr; } //+------------------------------------------------------------------------- // // Function: PutPresentationObject, INTERNAL // // Synopsis: Writes a presentation to an OLE 1 stream. // // Arguments: [pos] -- the OLE 1 stream to write to // [ppres] -- the presentation object // [cls] -- the class object // [fStatic] -- flag: is this a static object // // Returns: NOERROR on success // various possible I/O errors on failure // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Code cleaned up and documented // Notes: // //-------------------------------------------------------------------------- static INTERNAL PutPresentationObject( LPOLESTREAM pos, const PRES FAR* ppres, const CLASS FAR& cls, BOOL fStatic) // optional { HRESULT hr; // Is there a real presentation? BOOL fIsPres = FALSE; if (ppres) { if (ppres->m_format.m_ftag != ftagClipFormat || ppres->m_format.m_cf != 0 ) { fIsPres = TRUE; } } // write the OLE version to the stream if (FAILED(hr = ULToOLE1Stream (pos, dwVerToFile))) { return hr; } // Calc format ID for presentation object, use 0 for no presentation ULONG id = 0L; if (fIsPres) { if (fStatic) { id = FMTID_STATIC; } else { id = FMTID_PRES; } } if (FAILED(hr = ULToOLE1Stream(pos, id))) { return hr; } if (!fIsPres) { // No presentation return NOERROR; } if (IsStandardFormat (ppres->m_format)) { return PutStandardPresentation (pos, ppres); } else { Assert (!fStatic); return PutGenericPresentation (pos, ppres, cls.m_szClsid); } } //+------------------------------------------------------------------------- // // Function: PutStandardPresentation, INTERNAL // // Synopsis: Writes a standard presentation (META, DIB, or BITMAP) out // to an OLE 1 stream. Creates the METAFILEPICT header // as required. // // Arguments: [pos] -- the OLE 1 stream to write to // [ppres] -- the presentation to write // // Returns: NOERROR on success // Various other errors are possible from I/O routines // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Cleaned up and documented // Notes: // //-------------------------------------------------------------------------- static INTERNAL PutStandardPresentation( LPOLESTREAM pos, const PRES FAR* ppres) { HRESULT hr = NOERROR; Assert (ppres->m_format.m_ftag == ftagClipFormat); // Write the clipboard format string to the OLE 1 stream // (Will be written in ANSI, not OLESTR format) switch (ppres->m_format.m_cf) { case CF_METAFILEPICT: if (FAILED(hr = StringToOLE1Stm (pos, OLESTR("METAFILEPICT")))) { return hr; } break; case CF_DIB: if (FAILED(hr = StringToOLE1Stm (pos, OLESTR("DIB")))) { return hr; } break; case CF_BITMAP: if (FAILED(hr = StringToOLE1Stm (pos, OLESTR("BITMAP")))) { return hr; } break; default: Assert (0 && "Don't know how to write pres format"); } // Write width if (FAILED(hr = ULToOLE1Stream(pos, ppres->m_ulWidth))) { return hr; } // OLE 1.0 file format expects height to be saved as a negative value if (FAILED(hr = ULToOLE1Stream(pos, - ((LONG)ppres->m_ulHeight)))) { return hr; } // Do special handling for CF_METAFILEPICT if (ppres->m_format.m_cf == CF_METAFILEPICT) { // Need a header to write, crete one here WIN16METAFILEPICT mfpict = { MM_ANISOTROPIC, (int)(long) ppres->m_ulWidth, (int)(long) ppres->m_ulHeight, 0 }; // put size ater adjusting it for metafilepict if (FAILED(hr = ULToOLE1Stream (pos, (ppres->m_data.m_cbSize + sizeof(WIN16METAFILEPICT))))) { return hr; } // put metafilepict if (FAILED(hr = DataToOLE1Stm(pos, &mfpict, sizeof(mfpict)))) { return hr; } // put metafile bits // There are two possible means by which we got these metafile // bits: either we have an in-memory metafile, or raw bits // which we read from disk. If it is an in-memory metafile, // the m_pv ptr will have been set to METADATAPTR, and we need // to extract the bits to our own buffer before saving them. // If they came from disk, we can just re-write the buffer // into which we read them. if (METADATAPTR == ppres->m_data.m_pv) { BYTE *pb = (BYTE *) PrivMemAlloc(ppres->m_data.m_cbSize); if (NULL == pb) { return E_OUTOFMEMORY; } if (0 == GetMetaFileBitsEx((HMETAFILE) ppres->m_data.m_h, ppres->m_data.m_cbSize, pb)) { PrivMemFree(pb); return HRESULT_FROM_WIN32(GetLastError()); } if (FAILED(hr = DataToOLE1Stm(pos, pb, ppres->m_data.m_cbSize))) { PrivMemFree(pb); return hr; } PrivMemFree(pb); } else // Bits were originally read into our buffer from disk { if (FAILED(hr = DataToOLE1Stm(pos, ppres->m_data.m_pv, ppres->m_data.m_cbSize))) { return hr; } } } else { // Not a METAFILE, just write the data if (FAILED(hr = SizedDataToOLE1Stm (pos, ppres->m_data))) { return hr; } } return hr; } //+------------------------------------------------------------------------- // // Function: PutGenericPresentation, INTERNAL // // Synopsis: Writes a generic presentation to the stream based on // the clipboard format. (Dumps raw pres data to stm) // // Arguments: [pos] -- the stream to write to // [ppres] -- the presentation // [szClass] -- class name // // History: dd-mmm-yy Author Comment // 16-Feb-94 davepl 32-bit port'n'doc // Notes: // //-------------------------------------------------------------------------- static INTERNAL PutGenericPresentation( LPOLESTREAM pos, const PRES FAR* ppres, LPCOLESTR szClass) { Assert (szClass); HRESULT hr = NOERROR; // Write the format class name out to the stream if (FAILED(hr = StringToOLE1Stm(pos, szClass))) { return hr; } // This semi-mythical 0xC000 occurs in // other code I've seen in this project also; if there's // a constant defined, someone ought to fix this if (ppres->m_format.m_ftag == ftagClipFormat) { if (ppres->m_format.m_cf < 0xc000) { if (FAILED(hr = ULToOLE1Stream (pos, ppres->m_format.m_cf))) { return hr; } } else { if (FAILED(hr = ULToOLE1Stream (pos, 0L))) { return hr; } OLECHAR buf[256]; if (!GetClipboardFormatName(ppres->m_format.m_cf, buf, sizeof(buf)/sizeof(OLECHAR))) { return ResultFromScode(DV_E_CLIPFORMAT); } if (FAILED(hr = StringToOLE1Stm (pos, buf))) { return hr; } } } else if (ppres->m_format.m_ftag == ftagString) { // Write the format string to the stream if (FAILED(hr = ULToOLE1Stream (pos, 0L))) { return hr; } if (FAILED(hr = SizedDataToOLE1Stm (pos, ppres->m_format.m_dataFormatString))) { return hr; } } else { AssertSz (0, "Bad format"); } Assert (ppres->m_data.m_cbSize && ppres->m_data.m_h); // Write the raw presentation data out if (FAILED(hr = SizedDataToOLE1Stm (pos, ppres->m_data))) { return hr; } return NOERROR; } //+------------------------------------------------------------------------- // // Function: wClassesMatchW, INTERNAL INLINE // // Synopsis: Worker function to compare classes. Special case for // handling when the class of the file cannot be determined // because it is not a real file; this returns NOERROR // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Cleaned up and documented // // Notes: // //-------------------------------------------------------------------------- inline INTERNAL wClassesMatchW(REFCLSID clsidIn, LPOLESTR szFile) { CLSID clsid; // If we can get the CLSID for the code that works with this file, // compare it to the CLSID passed in, and return the result of // that comparison if (NOERROR==GetClassFile (szFile, &clsid)) { if (IsEqualCLSID(clsid, clsidIn)) { return NOERROR; } else { return ResultFromScode(S_FALSE); } } else { // If we can't determine the class of the file (because it's // not a real file) then OK. // Bug 3937. return NOERROR; } } //+------------------------------------------------------------------------- // // Function: MonikerIntoGenObj, INTERNAL // // Synopsis: Merges an OLE 2.0 moniker into a generic object // // Effects: Sets ths Topic, Item, and class members // // Arguments: [pgenobj] -- the generic object to receive moniker // [clsidLast] -- if a link, what its a link to // [pmk] -- the moniker to merge in // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Code cleanup // // Notes: // //-------------------------------------------------------------------------- static INTERNAL MonikerIntoGenObj( PGENOBJ pgenobj, REFCLSID clsidLast, LPMONIKER pmk ) { LPOLESTR szFile=NULL; LPOLESTR szItem=NULL; BOOL fClassesMatch = FALSE; // If the classes match, that implies this is a link to a pseudo-object // not to an embedded object. If GetClassFile fails because the file // does not exist or is unsaved then we give the link the benefit // of the doubt and let it stay a link. Only if we know the // classes do NOT match do we change the link into an Ole2Link // embedded object. // Ole10_PareMoniker returns S_FALSE in the FileMoniker - ItemMoniker - ItemMoniker... case // so check for NOERROR explicitly. if (NOERROR == Ole10_ParseMoniker (pmk, &szFile, &szItem)) { if (szFile) { SCODE sc = GetScode(wClassesMatchW(clsidLast, szFile)); if (sc == S_OK || sc == MK_E_CANTOPENFILE) { pgenobj->m_szTopic = szFile; pgenobj->m_szItem = szItem; fClassesMatch = TRUE; } } } if (FALSE == fClassesMatch) { // This moniker is either not a File or File::Item moniker, // or is a link to an embedded object, so the only // way we can convert it to OLE 1.0 is to make it an opaque Ole2Link pgenobj->m_fLink = FALSE; pgenobj->m_class.Reset (CLSID_StdOleLink); } return NOERROR; } //+------------------------------------------------------------------------- // // Function: OleConvertIStorageToOLESTREAMEx, STDAPI // // Synopsis: Similar to OleConvertIStorageToOLESTREAM, except that the // presentation data that needs to be written into OLESTREAM // is passed in. pmedium->tymed can only be TYMED_HGLOBAL // or TYMED_ISTREAM and the medium will not be released by the // api. cfFormat can be NULL, If it is NULL then the other // parameters (lWidth, lHeight, dwSize, pmedium) will be ignored. // // Arguments: [pstg] -- the storage object to convert from // [cfFormat] -- clipboard format // [lWidth] -- width // [lHeight] -- height // [dwSize] -- size in bytes // [pmedium] -- serialized bytes // [polestm] -- the OLE 1 stream to write to // // Returns: NOERROR on success // DV_E_TYMED invalid clipboard format // E_INVALIDARG invalid arg, normally stg or stm // DV_E_STGMEDIUM bad medium ptr // E_OUTOFMEMORY allocation failure // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Cleaned up and documented // // Notes: // //-------------------------------------------------------------------------- STDAPI OleConvertIStorageToOLESTREAMEx ( LPSTORAGE pstg, CLIPFORMAT cfFormat, LONG lWidth, LONG lHeight, DWORD dwSize, LPSTGMEDIUM pmedium, LPOLESTREAM polestm ) { OLETRACEIN((API_OleConvertIStorageToOLESTREAMEx, PARAMFMT("pstg= %p, cfFormat= %x, lWidth= %d, lHeight= %d, dwSize= %ud, pmedium= %ts, polestm= %p"), pstg, cfFormat, lWidth, lHeight, dwSize, pmedium, polestm)); LEDebugOut((DEB_ITRACE, "%p _IN OleConvertIStorageToOLESTREAMEx (" " %p, %x , %lx , %lx , %x , %p , %p )\n", 0 /*function*/, pstg, cfFormat, lWidth, lHeight, dwSize, pmedium, polestm )); CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IStorage,(IUnknown **)&pstg); HGLOBAL hGlobal = NULL; HRESULT hr = NOERROR; BOOL fFree = FALSE; CGenericObject genobj; // If we are given a clipboard format... if (cfFormat) { VDATEPTRIN_LABEL(pmedium, STGMEDIUM, errRtn, hr); // Check that the medium ptr is valid if (pmedium->hGlobal == NULL) { hr = ResultFromScode(DV_E_STGMEDIUM); goto errRtn; } // Cannot have a 0 sized clipboard representation if (dwSize == 0) { hr = ResultFromScode(E_INVALIDARG); goto errRtn; } switch (pmedium->tymed) { case TYMED_HGLOBAL: hGlobal = pmedium->hGlobal; break; case TYMED_ISTREAM: VDATEIFACE_LABEL(pmedium->pstm, errRtn, hr); if ((hr = UtGetHGLOBALFromStm(pmedium->pstm, dwSize, &hGlobal)) != NOERROR) { goto errRtn; } fFree = TRUE; break; default: hr = ResultFromScode(DV_E_TYMED); goto errRtn; } } if (FAILED(hr = wConvertIStorageToOLESTREAM(pstg, polestm, &genobj))) { goto errRtn; } // Clean m_ppres if (genobj.m_ppres) { delete genobj.m_ppres; genobj.m_ppres = NULL; } if (cfFormat) { // fill genobj.m_ppres PPRES ppres; if ((genobj.m_ppres = ppres = new PRES) == NULL) { hr = ResultFromScode(E_OUTOFMEMORY); goto errRtn; } ppres->m_ulWidth = (ULONG) lWidth; ppres->m_ulHeight = (ULONG) lHeight; ppres->m_data.m_cbSize = dwSize; ppres->m_data.m_fNoFree = !fFree; ppres->m_data.m_h = hGlobal; ppres->m_data.m_pv = GlobalLock(hGlobal); ppres->m_format.m_ftag = ftagClipFormat; ppres->m_format.m_cf = cfFormat; } else { genobj.m_fNoBlankPres = TRUE; } // REVIEW: We may not want to allow NULL cfFormat with static object hr = GenericObjectToOLESTREAM (genobj, polestm); LEDebugOut((DEB_ITRACE, "%p OUT OleConvertIStorageToOLESTREAMEx ( %lx ) " "\n", 0 /*function*/, hr)); OLETRACEOUT((API_OleConvertIStorageToOLESTREAMEx, hr)); return hr; errRtn: if (fFree && hGlobal != NULL) { GlobalFree(hGlobal); } LEDebugOut((DEB_ITRACE, "%p OUT OleConvertIStorageToOLESTREAMEx ( %lx ) " "\n", 0 /*function*/, hr)); OLETRACEOUT((API_OleConvertIStorageToOLESTREAMEx, hr)); return hr; } //+------------------------------------------------------------------------- // // Function: OleConvertOLESTREAMToIStorageEx, STDAPI // // Synopsis: Similar to OleConvertOLESTREAMToIStorage, except that the // presentation data that is read from OLESTREAM is passed out. // And no presentation stream will written in to the storage. // pmedium->tymed can be TYMED_ISTREAM ot TYMED_NULL. If // TYMED_NULL, then the bits will be returned in a global // handle through pmedium->hGlobal. Otherwise data will be // written into pmedium->pstm. NULL will be returned through // *pcfFormat, if there is no presentation in the OLESTREAM. // // Arguments: [pstg] -- the storage object to convert to // [cfFormat] -- clipboard format // [lWidth] -- width // [lHeight] -- height // [dwSize] -- size in bytes // [pmedium] -- serialized bytes // [polestm] -- the OLE 1 stream to write from // // Returns: DV_E_TYMED invalid clipboard format // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Cleaned up and documented // Notes: // //-------------------------------------------------------------------------- STDAPI OleConvertOLESTREAMToIStorageEx ( LPOLESTREAM polestm, LPSTORAGE pstg, CLIPFORMAT FAR* pcfFormat, LONG FAR* plWidth, LONG FAR* plHeight, DWORD FAR* pdwSize, LPSTGMEDIUM pmedium ) { OLETRACEIN((API_OleConvertOLESTREAMToIStorageEx, PARAMFMT("polestm= %p, pstg= %p, pcfFormat= %p, plWidth= %p, plHeight= %p, pdwSize= %p, pmedium= %p"), polestm, pstg, pcfFormat, plWidth, plHeight, pdwSize, pmedium)); LEDebugOut((DEB_ITRACE, "%p _IN OleConvertOLESTREAMToIStorageEx (" " %p , %p , %p , %p , %p , %p , %p )\n", 0 /*function*/, polestm, pstg, pcfFormat,plWidth,plHeight,pdwSize,pmedium )); HRESULT hr; PPRES ppres = NULL; GENOBJ genobj; VDATEPTROUT_LABEL(pcfFormat, CLIPFORMAT, errRtn, hr); VDATEPTROUT_LABEL(plWidth, LONG, errRtn, hr); VDATEPTROUT_LABEL(plHeight, LONG, errRtn, hr); VDATEPTROUT_LABEL(pdwSize, DWORD, errRtn, hr); VDATEPTROUT_LABEL(pmedium, STGMEDIUM, errRtn, hr); CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IStorage,(IUnknown **)&pstg); if (pmedium->tymed == TYMED_ISTREAM) { VDATEIFACE_LABEL(pmedium->pstm, errRtn, hr); } else if (pmedium->tymed != TYMED_NULL) { hr = ResultFromScode(DV_E_TYMED); goto errRtn; } // Bring the object into genobj if (FAILED((hr = wConvertOLESTREAMToIStorage(polestm, pstg, &genobj)))) { goto errRtn; } ppres = genobj.m_ppres; genobj.m_ppres = NULL; if (FAILED(hr = GenericObjectToIStorage (genobj, pstg, NULL))) { goto errRtn; } // If no presentation is available, clear our all the pres // dimensions and format if (ppres == NULL) { *pcfFormat = 0; *plWidth = 0L; *plHeight = 0L; *pdwSize = 0L; // Don't worry about the pmedium, it is already in the proper state hr = NOERROR; goto errRtn; } // If we reach here, we have a presentation, so set the OUT // parameters accordingly *plWidth = (LONG) ppres->m_ulWidth; *plHeight = (LONG) ppres->m_ulHeight; *pdwSize = ppres->m_data.m_cbSize; Assert(ppres->m_format.m_ftag != ftagNone); // If we have a clipboard format ID, return that in the OUT paramter, // otherwise return whatever we get back from an attempt to register // the format string if (ppres->m_format.m_ftag == ftagClipFormat) { *pcfFormat = ppres->m_format.m_cf; } else { // m_dataFormatString is an ASCII string. *pcfFormat = (CLIPFORMAT) SSRegisterClipboardFormatA( (LPCSTR) ppres->m_format.m_dataFormatString.m_pv); Assert(0 != *pcfFormat); } if (pmedium->tymed == TYMED_NULL) { if (ppres->m_data.m_h) { Assert(ppres->m_data.m_pv != NULL); GlobalUnlock(ppres->m_data.m_h); } // transfer the ownership pmedium->tymed = TYMED_HGLOBAL; pmedium->hGlobal = ppres->m_data.m_h; // Null out the handle and pointer so that destructor of PRES will not // free it. ppres->m_data.m_h = NULL; ppres->m_data.m_pv = NULL; } else { hr = pmedium->pstm->Write(ppres->m_data.m_pv, *pdwSize, NULL); } errRtn: if (ppres) { delete ppres; } LEDebugOut((DEB_ITRACE, "%p OUT OleConvertOLESTREAMToIStorageEx ( %lx ) " "\n", 0 /*function*/, hr)); OLETRACEOUT((API_OleConvertOLESTREAMToIStorageEx, hr)); return hr; } //+------------------------------------------------------------------------- // // Function: wWriteFmtUserType, INTERNAL // // Synopsis: Gets the user type for a class ID and writes it to // an IStorage // // // Arguments: [pstg] -- the storage to write to // [clsid] -- the class ID // // // Returns: NOERROR on success // // History: dd-mmm-yy Author Comment // 21-Feb-94 davepl Cleaned up and documented // Notes: // //-------------------------------------------------------------------------- FARINTERNAL wWriteFmtUserType(LPSTORAGE pstg, REFCLSID clsid) { HRESULT hr = NOERROR; LPOLESTR szProgID = NULL; LPOLESTR szUserType = NULL; // Get the program ID if (FAILED(hr = ProgIDFromCLSID (clsid, &szProgID))) { goto errRtn; } // Get the user type if (FAILED(hr = OleRegGetUserType(clsid,USERCLASSTYPE_FULL,&szUserType))) { goto errRtn; } // Write the user type out to the storage if (FAILED(hr = WriteFmtUserTypeStg (pstg, (CLIPFORMAT) RegisterClipboardFormat (szProgID), szUserType))) { goto errRtn; } // Clean up and return status errRtn: if (szProgID) { PubMemFree(szProgID); } if (szUserType) { PubMemFree(szUserType); } return hr; } //+------------------------------------------------------------------------- // // Function: wCLSIDFromProgID // // Synopsis: Looks for the key HKEY_CLASSES_ROOT\{ProgID}\Clsid\ to get // the string version of the class ID, then returns the CLSID // value of whatever it found. // // History: dd-mmm-yy Author Comment // 25-Jun-94 alexgo fixed Ole1 CLSID creation // 15-Apr-94 davepl Rewrite // // Notes: Used to be in clipboard code, but used in this file // //-------------------------------------------------------------------------- INTERNAL wCLSIDFromProgID(LPOLESTR szProgID, LPCLSID pclsid, BOOL fForceAssign) { VDATEHEAP(); // Apparently some optimization. If the class name is "OLE2Link", we can // return CLSID_StdOleLInk without even bothering to check the registry. if (0 == _xstrcmp(szProgID, OLESTR("OLE2Link"))) { *pclsid = CLSID_StdOleLink; return NOERROR; } else { // this function will look for a CLSID under the ProgID entry in // the registry or manufacture one if none present. return CLSIDFromOle1Class(szProgID, pclsid, fForceAssign); } } //+------------------------------------------------------------------------- // // Function: wProgIDFromCLSID // // Synopsis: A wrapper for ProgIDFromCLSID. The only change in // functionality is to check and see if this is a // CLSID_StdOleLink, and if so, return a prog ID of // "OLE2Link" rather than failing. // // // History: dd-mmm-yy Author Comment // 15-Feb-94 davepl Rewrite // //-------------------------------------------------------------------------- FARINTERNAL wProgIDFromCLSID(REFCLSID clsid, LPOLESTR FAR* psz) { VDATEHEAP(); HRESULT hresult; // If we can get the ProgID by conventional methods, great, just // return it. if (NOERROR == (hresult = ProgIDFromCLSID(clsid, psz))) { return hresult; } // If we failed, it might be because this is a standard OLE link, which // will not have a ProgID entry in the registry, so we fake it out by // returning the ProgID manually. if (IsEqualCLSID(clsid, CLSID_StdOleLink)) { *psz = UtDupString(OLESTR("OLE2Link")); if (*psz == NULL) { hresult = E_OUTOFMEMORY; } else { hresult = NOERROR; } } // Must not have been able to resolve for ProgID, so return the error. return(hresult); } #if 0 // We don't need these conversion fns yet, but we likely will soon. inline INTERNAL_(VOID) ConvertBM32to16(LPBITMAP lpsrc, LPWIN16BITMAP lpdest) { lpdest->bmType = (short)lpsrc->bmType; lpdest->bmWidth = (short)lpsrc->bmWidth; lpdest->bmHeight = (short)lpsrc->bmHeight; lpdest->bmWidthBytes = (short)lpsrc->bmWidthBytes; lpdest->bmPlanes = (BYTE)lpsrc->bmPlanes; lpdest->bmBitsPixel = (BYTE)lpsrc->bmBitsPixel; } inline INTERNAL_(VOID) ConvertBM16to32(LPWIN16BITMAP lpsrc, LPBITMAP lpdest) { lpdest->bmType = MAKELONG(lpsrc->bmType,NULL_WORD); lpdest->bmWidth = MAKELONG(lpsrc->bmWidth,NULL_WORD); lpdest->bmHeight = MAKELONG(lpsrc->bmHeight,NULL_WORD); lpdest->bmWidthBytes = MAKELONG(lpsrc->bmWidthBytes,NULL_WORD); lpdest->bmPlanes = (WORD)lpsrc->bmPlanes; lpdest->bmBitsPixel = (WORD)lpsrc->bmBitsPixel; } inline INTERNAL_(VOID) ConvertMF16to32( LPWIN16METAFILEPICT lpsrc, LPMETAFILEPICT lpdest ) { lpdest->mm = (DWORD)lpsrc->mm; lpdest->xExt = (DWORD)MAKELONG(lpsrc->xExt,NULL_WORD); lpdest->yExt = (DWORD)MAKELONG(lpsrc->yExt,NULL_WORD); } inline INTERNAL_(VOID) ConvertMF32to16( LPMETAFILEPICT lpsrc, LPWIN16METAFILEPICT lpdest ) { lpdest->mm = (short)lpsrc->mm; lpdest->xExt = (short)lpsrc->xExt; lpdest->yExt = (short)lpsrc->yExt; } #endif