5829 lines
156 KiB
C++
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
|