//+================================================================== // // 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 #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()