NT4/private/ole32/stg/props/propapi.cxx
2020-09-30 17:12:29 +02:00

574 lines
16 KiB
C++

//+==================================================================
//
// File: PropAPI.cxx
//
// This file provides the Property Set API routines.
//
// APIs: StgCreatePropSetStg (creates an IPropertySetStorage)
//
// StgCreatePropStg (creates an IPropertyStorage)
//
// StgOpenPropStg (opens an IPropertyStorage)
//
//+==================================================================
#include <pch.cxx>
#ifdef _MAC_NODOC
ASSERTDATA // File-specific data for FnAssert
#endif
//+------------------------------------------------------------------
//
// Function: QueryForIStream
//
// Synopsis: This routine queries an IUnknown for
// an IStream interface. This is isolated into
// a separate routine because some workaround code
// is required on the Mac.
//
// Inputs: [IUnknown*] pUnk
// The interface to be queried.
// [IStream**] ppStm
// Location to return the result.
//
// Returns: [HRESULT]
//
// Notes: On older Mac implementations (<=2.08, <=~1996)
// the memory-based IStream implementation
// (created by CreateStreamOnHGlobal) had a bug
// in QueryInterface: when you QI for an
// IStream or IUnknown, an addref is done, but an
// HR of E_NOINTERFACE is returned.
//
// Below, we look for this condition: if we get an
// E_NOINTERFACE on the Mac, we check to see if it's
// an OLE mem-based IStream. If it is, we simply cast
// IUnknown as an IStream. We validate it as an OLE
// the mem-based IStream by creating one of our own, and
// comparing the QueryInterface addresses.
//
// This is some ugly code, but at least it is isolated,
// only runs on the older Macs, and ensures that we
// work on all platforms.
//
//+------------------------------------------------------------------
inline HRESULT QueryForIStream( IUnknown * pUnk, IStream** ppStm )
{
HRESULT hr;
// Attempt to get the interface
hr = pUnk->QueryInterface( IID_IStream, (void**) ppStm );
#ifdef _MAC
// On the Mac, if we get a no-interface error, see if it is really
// a buggy mem-based IStream implementation.
if( E_NOINTERFACE == hr )
{
IStream *pstmMem = NULL;
// Create our own mem-based IStream.
hr = CreateStreamOnHGlobal( NULL, TRUE, &pstmMem );
if( FAILED(hr) ) goto Exit;
// If the mem-based Stream's QI implementation has the same
// address as the Unknown's QI implementation, then the Unknown
// must be an OLE mem-based stream.
if( pUnk->QueryInterface == pstmMem->QueryInterface )
{
// We can just cast the IUnknown* as an IStream* and
// we're done (the original QI, despite returning an
// error, has already done an AddRef).
hr = S_OK;
*ppStm = (IStream*) pUnk;
}
else
{
// This is a real no-interface error, so let's return it.
hr = E_NOINTERFACE;
}
pstmMem->Release();
}
// ----
// Exit
// ----
Exit:
#endif // #ifdef _MAC
return( hr );
} // QueryForIStream()
//+------------------------------------------------------------------
//
// Function: StgCreatePropStg
//
// Synopsis: Given an IStorage or IStream, create an
// IPropertyStorage. This is similar to the
// IPropertySetStorage::Create method. Existing
// contents of the Storage/Stream are removed.
//
// Inputs: [IUnknown*] pUnk
// An IStorage* for non-simple propsets,
// an IStream* for simple. grfFlags is
// used to disambiguate.
// [REFFMTID] fmtid
// The ID of the property set.
// [DWORD] grfFlags
// From the PROPSETFLAG_* enumeration.
// [IPropertySetStorage**] ppPropStg
// The result.
//
// Returns: [HRESULT]
//
// Notes: The caller is responsible for maintaining
// thread-safety between the original
// IStorage/IStream and this IPropertyStorage.
//
//+------------------------------------------------------------------
STDAPI StgCreatePropStg( IUnknown *pUnk,
REFFMTID fmtid,
const CLSID *pclsid,
DWORD grfFlags,
DWORD dwReserved,
IPropertyStorage **ppPropStg)
{
// ------
// Locals
// ------
HRESULT hr;
IStream *pstm = NULL;
IStorage *pstg = NULL;
// ----------
// Validation
// ----------
GEN_VDATEIFACE_LABEL( pUnk, E_INVALIDARG, Exit, hr );
GEN_VDATEREADPTRIN_LABEL(&fmtid, FMTID, E_INVALIDARG, Exit, hr );
GEN_VDATEPTRIN_LABEL(pclsid, CLSID, E_INVALIDARG, Exit, hr );
// grfFlags is validated by CPropertyStorage
GEN_VDATEPTROUT_LABEL( ppPropStg, IPropertyStorage*, E_INVALIDARG, Exit, hr );
*ppPropStg = NULL;
// -----------------------
// Non-Simple Property Set
// -----------------------
if( grfFlags & PROPSETFLAG_NONSIMPLE )
{
// Get the IStorage*
hr = pUnk->QueryInterface( IID_IStorage, (void**) &pstg );
if( FAILED(hr) ) goto Exit;
// Create the IPropertyStorage implementation
*ppPropStg = new CPropertyStorage();
if( NULL== *ppPropStg )
{
hr = E_OUTOFMEMORY;
goto Exit;
}
// Initialize the IPropertyStorage
((CPropertyStorage*) *ppPropStg)->Create( pstg,
fmtid,
pclsid,
grfFlags,
&hr );
if( FAILED(hr) ) goto Exit;
} // if( grfFlags & PROPSETFLAG_NONSIMPLE )
// -------------------
// Simple Property Set
// -------------------
else
{
// Get the IStream*
hr = QueryForIStream( pUnk, &pstm );
if( FAILED(hr) ) goto Exit;
// Create an IPropertyStorage implementation.
*ppPropStg = new CPropertyStorage();
if( NULL == *ppPropStg )
{
hr = E_OUTOFMEMORY;
goto Exit;
}
// Initialize the IPropertyStorage (which
// is responsible for sizing and seeking the
// stream).
((CPropertyStorage*) *ppPropStg)->Create( pstm,
fmtid,
pclsid,
grfFlags,
&hr );
if( FAILED(hr) ) goto Exit;
} // if( grfFlags & PROPSETFLAG_NONSIMPLE ) ... else
// ----
// Exit
// ----
Exit:
// If we created *ppPropStg, and there was an error, delete it.
if( FAILED(hr) )
{
PropDbg((DEB_ERROR, "StgCreatePropStg returns %08X\n", hr ));
// Only delete it if the caller gave us valid parameters
// and we created a CPropertyStorage
if( E_INVALIDARG != hr && NULL != *ppPropStg )
{
delete *ppPropStg;
*ppPropStg = NULL;
}
}
if( NULL != pstm )
pstm->Release();
if( NULL != pstg )
pstg->Release();
return( hr );
} // StgCreatePropStg()
//+------------------------------------------------------------------
//
// Function: StgOpenPropStg
//
// Synopsis: Given an IStorage or IStream which hold a
// serialized property set, create an
// IPropertyStorage. This is similar to the
// IPropertySetStorage::Open method.
//
// Inputs: [IUnknown*] pUnk
// An IStorage* for non-simple propsets,
// an IStream* for simple. grfFlags is
// used to disambiguate.
// [REFFMTID] fmtid
// The ID of the property set.
// [DWORD] grfFlags
// From the PROPSETFLAG_* enumeration.
// [IPropertySetStorage**] ppPropStg
// The result.
//
// Returns: [HRESULT]
//
// Notes: The caller is responsible for maintaining
// thread-safety between the original
// IStorage/IStream and this IPropertyStorage.
//
//+------------------------------------------------------------------
STDAPI StgOpenPropStg( IUnknown* pUnk,
REFFMTID fmtid,
DWORD grfFlags,
DWORD dwReserved,
IPropertyStorage **ppPropStg)
{
// ------
// Locals
// ------
HRESULT hr;
IStream *pstm = NULL;
IStorage *pstg = NULL;
// ----------
// Validation
// ----------
GEN_VDATEIFACE_LABEL( pUnk, E_INVALIDARG, Exit, hr );
GEN_VDATEREADPTRIN_LABEL(&fmtid, FMTID, E_INVALIDARG, Exit, hr);
// grfFlags is validated by CPropertyStorage
GEN_VDATEPTROUT_LABEL( ppPropStg, IPropertyStorage*, E_INVALIDARG, Exit, hr );
// -----------------------
// Non-Simple Property Set
// -----------------------
*ppPropStg = NULL;
if( grfFlags & PROPSETFLAG_NONSIMPLE )
{
// Get the IStorage*
hr = pUnk->QueryInterface( IID_IStorage, (void**) &pstg );
if( FAILED(hr) ) goto Exit;
// Create an IPropertyStorage* implementation.
*ppPropStg = new CPropertyStorage();
if( NULL == *ppPropStg )
{
hr = E_OUTOFMEMORY;
goto Exit;
}
// Initialize the IPropertyStorage by reading
// the serialized property set.
((CPropertyStorage*) *ppPropStg)->Open(
pstg,
fmtid,
grfFlags,
&hr );
if( FAILED(hr) ) goto Exit;
} // if( grfFlags & PROPSETFLAG_NONSIMPLE )
// -------------------
// Simple Property Set
// -------------------
else
{
// Get the IStream*
hr = QueryForIStream( pUnk, &pstm );
if( FAILED(hr) ) goto Exit;
// Create an IPropertyStorage* implementation.
*ppPropStg = new CPropertyStorage();
if( NULL == *ppPropStg )
{
hr = E_OUTOFMEMORY;
goto Exit;
}
// Initialize the IPropertyStorage by reading
// the serialized property set (the CPropertyStorage
// is responsible for seeking to the stream start).
((CPropertyStorage*) *ppPropStg)->Open( pstm,
fmtid,
grfFlags,
FALSE, // Not deleting
&hr );
if( FAILED(hr) ) goto Exit;
} // if( grfFlags & PROPSETFLAG_NONSIMPLE ) ... else
// ----
// Exit
// ----
Exit:
// If we created *ppPropStg, and there was an error, delete it.
if( FAILED(hr) )
{
PropDbg((DEB_ERROR, "StgOpenPropStg returns %08X\n", hr ));
// Only delete it if the caller gave us a valid ppPropStg
// and we created a CPropertyStorage
if( E_INVALIDARG != hr && NULL != *ppPropStg )
{
delete *ppPropStg;
*ppPropStg = NULL;
}
}
if( NULL != pstm )
pstm->Release();
if( NULL != pstg )
pstg->Release();
return( hr );
} // StgOpenPropStg()
//+------------------------------------------------------------------
//
// Function: StgCreatePropSetStg
//
// Synopsis: Given an IStorage, create an IPropertySetStorage.
// This is similar to QI-ing a DocFile IStorage for
// the IPropertySetStorage interface.
//
// Inputs: [IStorage*] pStorage
// Will be held by the propsetstg and used
// for create/open.
// [IPropertySetStorage**] ppPropSetStg
// Receives the result.
//
// Returns: [HRESULT]
//
// Notes: The caller is responsible for maintaining
// thread-safety between the original
// IStorage and this IPropertySetStorage.
//
//+------------------------------------------------------------------
STDAPI
StgCreatePropSetStg( IStorage *pStorage,
DWORD dwReserved,
IPropertySetStorage **ppPropSetStg)
{
HRESULT hr = S_OK;
// Validation
GEN_VDATEIFACE_LABEL( pStorage, E_INVALIDARG, Exit, hr );
GEN_VDATEPTROUT_LABEL( ppPropSetStg, IPropertySetStorage*, E_INVALIDARG, Exit, hr );
// Create the IPropertySetStorage implementation.
*ppPropSetStg = new CPropertySetStorage( pStorage );
if( NULL == *ppPropSetStg )
{
hr = E_OUTOFMEMORY;
goto Exit;
}
// ----
// Exit
// ----
Exit:
if( FAILED(hr) )
{
PropDbg((DEB_ERROR, "StgCreatePropSetStg() returns %08X\n", hr ));
}
return( hr );
} // StgCreatePropSetStg()
//+----------------------------------------------------------------------------
//
// Function: FmtIdToPropStgName
//
// Synopsis: This function maps a property set's FMTID to the name of
// the Stream or Storage which contains it. This name
// is 27 characters (including the terminator).
//
// Inputs: [const FMTID*] pfmtid (in)
// The FMTID of the property set.
// [LPOLESTR] oszName (out)
// The name of the Property Set's Stream/Storage
//
// Returns: [HRESULT] S_OK or E_INVALIDARG
//
//+----------------------------------------------------------------------------
STDAPI
FmtIdToPropStgName( const FMTID *pfmtid, LPOLESTR oszName )
{
HRESULT hr = S_OK;
// Validate Inputs
GEN_VDATEREADPTRIN_LABEL(pfmtid, FMTID, E_INVALIDARG, Exit, hr);
VDATESIZEPTROUT_LABEL(oszName,
sizeof(OLECHAR) * (CCH_MAX_PROPSTG_NAME+1),
Exit, hr);
// Make the Conversion
RtlGuidToPropertySetName( pfmtid, oszName );
// Exit
Exit:
if( FAILED(hr) )
{
PropDbg((DEB_ERROR, "FmtIdToPropStgName returns %08X\n", hr ));
}
return( hr );
} // FmtIdToPropStgName()
//+----------------------------------------------------------------------------
//
// Function: PropStgNameToFmtId
//
// Synopsis: This function maps a property set's Stream/Storage name
// to its FMTID.
//
// Inputs: [const LPOLESTR] oszName (in)
// The name of the Property Set's Stream/Storage
// [FMTID*] pfmtid (out)
// The FMTID of the property set.
//
//
// Returns: [HRESULT] S_OK or E_INVALIDARG
//
//+----------------------------------------------------------------------------
STDAPI
PropStgNameToFmtId( const LPOLESTR oszName, FMTID *pfmtid )
{
HRESULT hr = S_OK;
// Validate Inputs
GEN_VDATEPTROUT_LABEL(pfmtid, FMTID, E_INVALIDARG, Exit, hr);
#ifdef OLE2ANSI
if( FAILED(hr = ValidateNameA(oszName, CCH_MAX_PROPSTG_NAME )))
goto Exit;
#else
if( FAILED(hr = ValidateNameW(oszName, CCH_MAX_PROPSTG_NAME )))
goto Exit;
#endif
// Make the Conversion, passing in the name and its character-length
// (not including the null-terminator).
RtlPropertySetNameToGuid( ocslen(oszName), oszName, pfmtid );
// Exit
Exit:
if( FAILED(hr) )
{
PropDbg((DEB_ERROR, "PropStgNameToFmtId returns %08X\n", hr ));
}
return( hr );
} // PropStgNameToFmtId()