Windows2003-3790/com/ole32/ole232/ole1/ostm2stg.cpp
2020-09-30 16:53:55 +02:00

5829 lines
156 KiB
C++

//+-------------------------------------------------------------------------
//
// 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 <le2int.h>
#include "ostm2stg.h"
#include <ctype.h>
#include <limits.h>
#include <string.h>
#include <ole1cls.h>
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
&& WNetGetConnection (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,
(short) ppres->m_ulWidth,
(short) 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