574 lines
16 KiB
C++
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()
|
|
|