Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1476 lines
43 KiB

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