// // Copyright 2001 - Microsoft Corporation // // Created By: // Geoff Pease (GPease) 23-JAN-2001 // // Maintained By: // Geoff Pease (GPease) 23-JAN-2001 // #include "pch.h" #include "DocProp.h" #include "DefProp.h" #include "IEditVariantsInPlace.h" #include "PropertyCacheItem.h" #include "PropertyCache.h" #include "AdvancedDlg.h" #include "SimpleDlg.h" #include "SummaryPage.h" #include "shutils.h" #include "WMUser.h" #include "doctypes.h" #include "ErrorDlgs.h" #include "LicensePage.h" #pragma hdrstop DEFINE_THISCLASS( "CSummaryPage" ) // ************************************************************************ // // Constructor / Destructor // // ************************************************************************ // // CreateInstance - used by CFactory // HRESULT CSummaryPage::CreateInstance( IUnknown ** ppunkOut ) { TraceFunc( "" ); HRESULT hr; Assert( ppunkOut != NULL ); CSummaryPage * pthis = new CSummaryPage; if ( pthis != NULL ) { hr = THR( pthis->Init( ) ); if ( SUCCEEDED( hr ) ) { *ppunkOut = (IShellExtInit *) pthis; (*ppunkOut)->AddRef( ); } pthis->Release( ); } else { hr = E_OUTOFMEMORY; } HRETURN( hr ); } // // Constructor // CSummaryPage::CSummaryPage( void ) : _cRef( 1 ) { TraceFunc( "" ); InterlockedIncrement( &g_cObjects ); Assert( 1 == _cRef ); // we initialize this above // // We assume that we are ZERO_INITed - be paranoid. // Assert( NULL == _hdlg ); Assert( NULL == _pida ); Assert( FALSE == _fReadOnly ); Assert( FALSE == _fAdvanced ); Assert( NULL == _pAdvancedDlg ); Assert( NULL == _pSimpleDlg ); Assert( 0 == _dwCurrentBindMode ); Assert( NULL == _rgdwDocType ); Assert( 0 == _cSources ); Assert( NULL == _rgpss ); Assert( NULL == _pPropertyCache ); TraceFuncExit(); } // // Description: // Initializes class. Put calls that can fail in here. // HRESULT CSummaryPage::Init( void ) { TraceFunc( "" ); HRESULT hr = S_OK; // IUnknown stuff Assert( 1 == _cRef ); // IShellExtInit stuff // IShellPropSheetExt stuff HRETURN( hr ); } // // Destructor // CSummaryPage::~CSummaryPage( ) { TraceFunc( "" ); THR( PersistMode( ) ); // ignore failure - what else can we do? if ( NULL != _pAdvancedDlg ) { _pAdvancedDlg->Release( ); } if ( NULL != _pSimpleDlg ) { _pSimpleDlg->Release( ); } if ( NULL != _rgdwDocType ) { TraceFree( _rgdwDocType ); } if ( NULL != _rgpss ) { ULONG idx = _cSources; while ( 0 != idx ) { idx --; if ( NULL != _rgpss[ idx ] ) { _rgpss[ idx ]->Release( ); } } TraceFree( _rgpss ); } if ( NULL != _pPropertyCache ) { _pPropertyCache->Destroy( ); } if ( NULL != _pida ) { TraceFree( _pida ); } InterlockedDecrement( &g_cObjects ); TraceFuncExit(); } // ************************************************************************ // // IUnknown // // ************************************************************************ // // // STDMETHODIMP CSummaryPage::QueryInterface( REFIID riid, LPVOID *ppv ) { TraceQIFunc( riid, ppv ); HRESULT hr = E_NOINTERFACE; if ( IsEqualIID( riid, __uuidof(IUnknown) ) ) { *ppv = static_cast< IShellExtInit * >( this ); hr = S_OK; } else if ( IsEqualIID( riid, __uuidof(IShellExtInit) ) ) { *ppv = TraceInterface( __THISCLASS__, IShellExtInit, this, 0 ); hr = S_OK; } else if ( IsEqualIID( riid, __uuidof(IShellPropSheetExt) ) ) { *ppv = TraceInterface( __THISCLASS__, IShellPropSheetExt, this, 0 ); hr = S_OK; } if ( SUCCEEDED( hr ) ) { ((IUnknown*) *ppv)->AddRef( ); } QIRETURN( hr, riid ); } // // // STDMETHODIMP_(ULONG) CSummaryPage::AddRef( void ) { TraceFunc( "[IUnknown]" ); _cRef ++; // apartment RETURN( _cRef ); } // // // STDMETHODIMP_(ULONG) CSummaryPage::Release( void ) { TraceFunc( "[IUnknown]" ); _cRef --; // apartment if ( 0 != _cRef ) RETURN( _cRef ); delete this; RETURN( 0 ); } // ************************************************************************ // // IShellExtInit // // ************************************************************************ // // // STDMETHODIMP CSummaryPage::Initialize( LPCITEMIDLIST pidlFolderIn , LPDATAOBJECT lpdobjIn , HKEY hkeyProgIDIn ) { TraceFunc( "" ); HRESULT hr; // // Make a copy of the PIDLs. // Assert( NULL == _pida ); hr = THR( DataObj_CopyHIDA( lpdobjIn, &_pida ) ); if ( FAILED( hr ) ) goto Cleanup; // // Start out with READ ONLY access // _dwCurrentBindMode = STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE; _rgdwDocType = (DWORD *) TraceAlloc( HEAP_ZERO_MEMORY, sizeof(DWORD) * _pida->cidl ); if ( NULL == _rgdwDocType ) goto OutOfMemory; hr = STHR( BindToStorage( ) ); if ( FAILED( hr ) ) goto Cleanup; // // We were able to bind to anything? // if ( S_FALSE == hr ) { // // Nope. Indicate this by failing. // hr = E_FAIL; goto Cleanup; } // // Retrieve the properties // hr = THR( RetrieveProperties( ) ); if ( FAILED( hr ) ) goto Cleanup; // // Don't hang on to the storage. // THR( ReleaseStorage( ) ); hr = S_OK; Cleanup: HRETURN( hr ); OutOfMemory: hr = E_OUTOFMEMORY; goto Cleanup; } // ************************************************************************ // // IShellPropSheetExt // // ************************************************************************ // // // STDMETHODIMP CSummaryPage::AddPages( LPFNADDPROPSHEETPAGE lpfnAddPageIn , LPARAM lParam ) { TraceFunc( "" ); HRESULT hr = E_FAIL; // assume failure HPROPSHEETPAGE hPage; PROPSHEETPAGE psp = { 0 }; psp.dwSize = sizeof(psp); psp.dwFlags = PSP_USECALLBACK; psp.hInstance = g_hInstance; psp.pszTemplate = MAKEINTRESOURCE(IDD_SUMMARYPAGE); psp.pfnDlgProc = DlgProc; psp.pfnCallback = PageCallback; psp.lParam = (LPARAM) this; hPage = CreatePropertySheetPage( &psp ); if ( NULL != hPage ) { BOOL b = TBOOL( lpfnAddPageIn( hPage, lParam ) ); if ( b ) { hr = S_OK; } else { DestroyPropertySheetPage( hPage ); } } // // Add the License Page, if needed, but only if there is only // one source file selected. // if ( _fNeedLicensePage && 1 == _cSources ) { IUnknown * punk; hr = THR( CLicensePage::CreateInstance( &punk, _pPropertyCache ) ); if ( SUCCEEDED( hr ) ) { IShellPropSheetExt * pspse; hr = THR( punk->TYPESAFEQI( pspse ) ); if ( SUCCEEDED( hr ) ) { hr = THR( pspse->AddPages( lpfnAddPageIn, lParam ) ); pspse->Release( ); } punk->Release( ); } } HRETURN( hr ); } // // // STDMETHODIMP CSummaryPage::ReplacePage( UINT uPageIDIn , LPFNADDPROPSHEETPAGE lpfnReplacePageIn , LPARAM lParam ) { TraceFunc( "" ); HRESULT hr = THR( E_NOTIMPL ); HRETURN( hr ); } // *************************************************************************** // // Dialog Proc and Property Sheet Callback // // *************************************************************************** // // // INT_PTR CALLBACK CSummaryPage::DlgProc( HWND hDlgIn , UINT uMsgIn , WPARAM wParam , LPARAM lParam ) { // Don't do TraceFunc because every mouse movement will cause this function to spew. WndMsg( hDlgIn, uMsgIn, wParam, lParam ); LRESULT lr = FALSE; CSummaryPage * pPage = (CSummaryPage *) GetWindowLongPtr( hDlgIn, DWLP_USER ); if ( uMsgIn == WM_INITDIALOG ) { PROPSHEETPAGE * ppage = (PROPSHEETPAGE *) lParam; SetWindowLongPtr( hDlgIn, DWLP_USER, (LPARAM) ppage->lParam ); pPage = (CSummaryPage *) ppage->lParam; pPage->_hdlg = hDlgIn; } if ( pPage != NULL ) { Assert( hDlgIn == pPage->_hdlg ); switch( uMsgIn ) { case WM_INITDIALOG: lr = pPage->OnInitDialog( ); break; case WM_NOTIFY: lr = pPage->OnNotify( (int) wParam, (LPNMHDR) lParam ); break; case WMU_TOGGLE: lr = pPage->OnToggle( ); break; case WM_DESTROY: SetWindowLongPtr( hDlgIn, DWLP_USER, NULL ); lr = pPage->OnDestroy( ); break; } } return lr; } // // // UINT CALLBACK CSummaryPage::PageCallback( HWND hwndIn , UINT uMsgIn , LPPROPSHEETPAGE ppspIn ) { TraceFunc( "" ); UINT uRet = 0; CSummaryPage * pPage = (CSummaryPage *) ppspIn->lParam; if ( NULL != pPage ) { switch ( uMsgIn ) { case PSPCB_CREATE: uRet = TRUE; // allow the page to be created break; case PSPCB_ADDREF: pPage->AddRef( ); break; case PSPCB_RELEASE: pPage->Release( ); break; } } RETURN( uRet ); } // *************************************************************************** // // Private methods // // *************************************************************************** // // WM_INITDIALOG handler // LRESULT CSummaryPage::OnInitDialog( void ) { TraceFunc( "" ); HRESULT hr; LRESULT lr = FALSE; Assert( NULL != _hdlg ); // this should have been initialized in the DlgProc. THR( RecallMode( ) ); // ignore failure if ( _fAdvanced ) { hr = THR( EnsureAdvancedDlg( ) ); if ( S_OK == hr ) { hr = THR( _pAdvancedDlg->Show( ) ); } } else { hr = THR( EnsureSimpleDlg( ) ); if ( S_OK == hr ) { // // This returns S_FALSE indicating that no "Simple" properties // were found. // hr = STHR( _pSimpleDlg->Show( ) ); if ( S_FALSE == hr ) { hr = THR( EnsureAdvancedDlg( ) ); if ( S_OK == hr ) { _fAdvanced = TRUE; THR( _pSimpleDlg->Hide( ) ); THR( _pAdvancedDlg->Show( ) ); } } } } RETURN( lr ); } // // WM_NOTIFY handler // LRESULT CSummaryPage::OnNotify( int iCtlIdIn , LPNMHDR pnmhIn ) { TraceFunc( "" ); LRESULT lr = FALSE; switch( pnmhIn->code ) { case PSN_APPLY: { HRESULT hr; // // For some reason, we don't get the EN_KILLFOCUS when the user clicks // the "Apply" button. Calling Show( ) again toggles the focus, causing // the EN_KILLFOCUS which updates the property cache. // if ( !_fAdvanced && ( NULL != _pSimpleDlg ) ) { STHR( _pSimpleDlg->Show( ) ); } hr = STHR( PersistProperties( ) ); if ( FAILED( hr ) ) { DisplayPersistFailure( _hdlg, hr, ( _pida->cidl > 1 ) ); SetWindowLongPtr( _hdlg, DWLP_MSGRESULT, PSNRET_INVALID ); lr = TRUE; } } break; } RETURN( lr ); } // // WMU_TOGGLE handler // LRESULT CSummaryPage::OnToggle( void ) { TraceFunc( "" ); HRESULT hr; BOOL fMultiple = ( 1 < _cSources ); if ( _fAdvanced ) { hr = THR( _pAdvancedDlg->Hide( ) ); if ( FAILED( hr ) ) goto Cleanup; ShowSimple: hr = STHR( EnsureSimpleDlg( ) ); if ( FAILED( hr ) ) goto Cleanup; if ( S_FALSE == hr ) { hr = THR( _pSimpleDlg->PopulateProperties( _pPropertyCache, _rgdwDocType[ 0 ], fMultiple ) ); if ( FAILED( hr ) ) goto Cleanup; } hr = STHR( _pSimpleDlg->Show( ) ); if ( FAILED( hr ) ) goto ShowAdvanced; _fAdvanced = FALSE; } else { hr = THR( _pSimpleDlg->Hide( ) ); if ( FAILED( hr ) ) goto Cleanup; ShowAdvanced: hr = STHR( EnsureAdvancedDlg( ) ); if ( FAILED( hr ) ) goto Cleanup; if ( S_FALSE == hr ) { hr = THR( _pAdvancedDlg->PopulateProperties( _pPropertyCache, _rgdwDocType[ 0 ], fMultiple ) ); if ( FAILED( hr ) ) goto Cleanup; } hr = THR( _pAdvancedDlg->Show( ) ); if ( FAILED( hr ) ) goto ShowSimple; _fAdvanced = TRUE; } hr = S_OK; Cleanup: HRETURN( hr ); } // // WM_DESTROY handler // LRESULT CSummaryPage::OnDestroy( void ) { TraceFunc( "" ); LRESULT lr = FALSE; if ( NULL != _pAdvancedDlg ) { _pAdvancedDlg->Release( ); _pAdvancedDlg = NULL; } if ( NULL != _pSimpleDlg ) { _pSimpleDlg->Release( ); _pSimpleDlg = NULL; } RETURN( lr ); } // // Return Values: // S_OK // Successfully retrieved a PIDL // // S_FALSE // Call succeeded, no PIDL was found. // HRESULT CSummaryPage::Item( UINT idxIn , LPITEMIDLIST * ppidlOut ) { TraceFunc( "" ); HRESULT hr = S_FALSE; Assert( NULL != ppidlOut ); Assert( NULL != _pida ); *ppidlOut = NULL; if ( idxIn < _pida->cidl ) { *ppidlOut = IDA_FullIDList( _pida, idxIn ); if ( NULL != *ppidlOut ) { hr = S_OK; } } HRETURN( hr ); } // // Description: // Checks _pAdvancedDlg to make sure that it is not NULL. // If it is NULL, it will create a new instance of CAdvancedDlg. // // Return Values: // S_OK // A new _pAdvancedDlg was created. // // S_FALSE // _pAdvancedDlg was not NULL. // // other HRESULTs. // HRESULT CSummaryPage::EnsureAdvancedDlg( void ) { TraceFunc( "" ); HRESULT hr = S_OK; if ( NULL == _pAdvancedDlg ) { hr = THR( CAdvancedDlg::CreateInstance( &_pAdvancedDlg, _hdlg ) ); if ( S_OK == hr ) { hr = THR( _pAdvancedDlg->PopulateProperties( _pPropertyCache, _rgdwDocType[ 0 ], ( 1 < _cSources ) ) ); } } else { hr = S_FALSE; } HRETURN( hr ); } // // Description: // Checks _pSimpleDlg to make sure that it is not NULL. // If it is NULL, it will create a new instance of CSimpleDialog. // // Return Values: // S_OK // A new _pSimpleDlg was created. // // S_FALSE // _pSimpleDlg was not NULL. // // other HRESULTs. // HRESULT CSummaryPage::EnsureSimpleDlg( void ) { TraceFunc( "" ); HRESULT hr = S_OK; BOOL fMultiple = ( 1 < _cSources ); if ( NULL == _pSimpleDlg ) { hr = THR( CSimpleDlg::CreateInstance( &_pSimpleDlg, _hdlg, fMultiple ) ); if ( S_OK == hr ) { hr = THR( _pSimpleDlg->PopulateProperties( _pPropertyCache, _rgdwDocType[ 0 ], fMultiple ) ); } } else { hr = S_FALSE; } HRETURN( hr ); } // // Description: // Persists the UI mode settings for the page. // // Return Values: // S_OK // HRESULT CSummaryPage::PersistMode( void ) { DWORD dwAdvanced = _fAdvanced; SHRegSetUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\PropSummary"), TEXT("Advanced"), REG_DWORD, &dwAdvanced, sizeof(dwAdvanced), SHREGSET_HKCU | SHREGSET_FORCE_HKCU); return S_OK; } // // Description: // Retrieves the UI mode settings for the page. // // Return Values: // S_OK // HRESULT CSummaryPage::RecallMode( void ) { _fAdvanced = SHRegGetBoolUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\PropSummary"), TEXT("Advanced"), FALSE, TRUE); return S_OK; } // // Description: // Retrieves the properties from the storage and caches // them. // // Return Values: // S_OK // All values successfully read and cached. // // S_FALSE // Some values successfully read, but some weren't not. // // E_FAIL // No values were successfully read. // // E_OUTOFMEMORY // Out of memory. // // other HRESULTs // HRESULT CSummaryPage::RetrieveProperties( void ) { TraceFunc( "" ); const ULONG cBlocks = 10; // number of properties to grab at a time. HRESULT hr; ULONG cSource; ULONG idx; ULONG cPropertiesRetrieved = 0; ULONG cPropertiesCached = 0; IPropertyStorage * pPropStg = NULL; IEnumSTATPROPSETSTG * pEnumPropSet = NULL; IEnumSTATPROPSTG * pEnumProp = NULL; CPropertyCacheItem * pItem = NULL; CPropertyCache ** rgpPropertyCaches = NULL; IPropertyUI * ppui = NULL; // // If there are multiple sources, then follow these rules: // // If any of the properties/sources are read-only, mark all the properties as being read-only. // // If any of the properties != the first property, mark the item as multiple. // // // Make room for the property cache lists per source. // rgpPropertyCaches = ( CPropertyCache ** ) TraceAlloc( HEAP_ZERO_MEMORY, _cSources * sizeof(CPropertyCache *) ); if ( NULL == rgpPropertyCaches ) goto OutOfMemory; // // Enumerate the source's property sets via IPropertySetStorage interface // for ( cSource = 0; cSource < _cSources; cSource ++ ) { // // Cleanup before next pass // if ( NULL != pEnumPropSet ) { pEnumPropSet->Release( ); pEnumPropSet = NULL; } hr = THR( CPropertyCache::CreateInstance( &rgpPropertyCaches[ cSource ] ) ); if ( FAILED( hr ) ) continue; // ignore and keep trying... IPropertySetStorage * pss = _rgpss[ cSource ]; // just borrowing - no need to AddRef( ). // // Add properties. // if ( NULL != pss ) { // // Grab a set enumerator // hr = THR( pss->Enum( &pEnumPropSet ) ); if ( FAILED( hr ) ) continue; // ignore and try next source for( ;; ) // ever { STATPROPSETSTG statPropSet[ cBlocks ]; ULONG cSetPropsRetrieved; hr = STHR( pEnumPropSet->Next( cBlocks, statPropSet, &cSetPropsRetrieved ) ); if ( FAILED( hr ) ) break; // exit condition if ( 0 == cSetPropsRetrieved ) break; // exit condition // // for each property set // for ( ULONG cSet = 0; cSet < cSetPropsRetrieved; cSet ++ ) { UINT uCodePage; // // Cleanup before next pass // if ( NULL != pPropStg ) { pPropStg->Release( ); pPropStg = NULL; } if ( NULL != pEnumProp ) { pEnumProp->Release( ); pEnumProp = NULL; } // // Open the set. // hr = THR( SHPropStgCreate( pss , statPropSet[ cSet ].fmtid , NULL , PROPSETFLAG_DEFAULT , _dwCurrentBindMode , OPEN_EXISTING , &pPropStg , &uCodePage ) ); if ( FAILED( hr ) ) continue; // ignore and try to get the next set // // Grab a property enumerator // hr = THR( pPropStg->Enum( &pEnumProp ) ); if ( FAILED( hr ) ) continue; // ignore and try to get the next set for( ;; ) // ever { STATPROPSTG statProp[ cBlocks ]; ULONG cPropsRetrieved; hr = STHR( pEnumProp->Next( cBlocks, statProp, &cPropsRetrieved ) ); if ( FAILED( hr ) ) break; // exit condition if ( 0 == cPropsRetrieved ) break; // exit condition cPropertiesRetrieved += cPropsRetrieved; // // Retrieve default property item definition and the value for // each property in this set. // for ( ULONG cProp = 0; cProp < cPropsRetrieved; cProp++ ) { Assert( NULL != rgpPropertyCaches[ cSource ] ); hr = THR( rgpPropertyCaches[ cSource ]->AddNewPropertyCacheItem( &statPropSet[ cSet ].fmtid , statProp[ cProp ].propid , statProp[ cProp ].vt , uCodePage , _fReadOnly , pPropStg , NULL ) ); if ( FAILED( hr ) ) continue; // ignore cPropertiesCached ++; } } } } } // // Some file types have special copy-protection that prohibits us // from editing their properties because doing so would destroy // the copy-protection on the file. We need to detect these files // and toggle their properties to READ-ONLY if their property set // contains a copy-protection PID and it is enabled. // switch ( _rgdwDocType[ cSource ] ) { case FTYPE_WMA: case FTYPE_ASF: case FTYPE_MP3: case FTYPE_WMV: hr = THR( CheckForCopyProtection( rgpPropertyCaches[ cSource ] ) ); if ( S_OK == hr ) { _fReadOnly = TRUE; _fNeedLicensePage = TRUE; ChangeGatheredPropertiesToReadOnly( rgpPropertyCaches[ cSource ] ); } break; } // // Now, iterate our default property sets and add to our collection // those properties the source is missing. // // In DEBUG: // If the CONTROL key is down, we'll add all the properties in our // def prop table. // for ( idx = 0; NULL != g_rgDefPropertyItems[ idx ].pszName; idx ++ ) { if ( ( ( _rgdwDocType[ cSource ] & g_rgDefPropertyItems[ idx ].dwSrcType ) && ( g_rgDefPropertyItems[ idx ].fAlwaysPresentProperty ) ) #ifdef DEBUG || ( GetKeyState( VK_CONTROL ) < 0 ) #endif DEBUG ) { hr = STHR( rgpPropertyCaches[ cSource ]->FindItemEntry( g_rgDefPropertyItems[ idx ].pFmtID , g_rgDefPropertyItems[ idx ].propID , NULL ) ); if ( S_FALSE == hr ) { // // Create a new item for the missing property. // hr = THR( rgpPropertyCaches[ cSource ]->AddNewPropertyCacheItem( g_rgDefPropertyItems[ idx ].pFmtID , g_rgDefPropertyItems[ idx ].propID , g_rgDefPropertyItems[ idx ].vt , 0 , _fReadOnly , NULL , NULL ) ); } } } } if ( 1 == _cSources ) { // // Since there is only one source, give ownership of the list away. // Assert( NULL == _pPropertyCache ); _pPropertyCache = rgpPropertyCaches[ 0 ]; rgpPropertyCaches[ 0 ] = NULL; } else { CollateMultipleProperties( rgpPropertyCaches ); } if ( NULL == _pPropertyCache ) { hr = E_FAIL; // nothing retrieved - nothing to show } else if ( cPropertiesCached == cPropertiesRetrieved ) { hr = S_OK; // all retrieved and successfully cached. } else if ( 0 != cPropertiesRetrieved ) { hr = S_FALSE; // missing a few. } else { hr = E_FAIL; // nothing read and/or cached. } Cleanup: if ( NULL != pEnumPropSet ) { pEnumPropSet->Release( ); } if ( NULL != pPropStg ) { pPropStg->Release( ); } if ( NULL != pEnumProp ) { pEnumProp->Release( ); } if ( NULL != pItem ) { THR( pItem->Destroy( ) ); } if ( NULL != ppui ) { ppui->Release( ); } if ( NULL != rgpPropertyCaches ) { idx = _cSources; while( 0 != idx ) { idx --; if ( NULL != rgpPropertyCaches[ idx ] ) { rgpPropertyCaches[ idx ]->Destroy( ); } } TraceFree( rgpPropertyCaches ); } HRETURN( hr ); OutOfMemory: hr = E_OUTOFMEMORY; goto Cleanup; } // // Description: // Walks thru the property cache and saves the dirty items. // // Return Values: // S_OK // Success! // // S_FALSE // Success, but nothing was updated. // // E_OUTOFMEMORY // Out of memory. // // other HRESULTs. // HRESULT CSummaryPage::PersistProperties( void ) { TraceFunc( "" ); HRESULT hr; ULONG cDirtyCount; ULONG idx; ULONG cStart; ULONG cSource; CPropertyCacheItem * pItem; PROPSPEC * pSpecs = NULL; PROPVARIANT * pValues = NULL; FMTID * pFmtIds = NULL; Assert( NULL != _pida ); Assert( NULL != _pPropertyCache ); // // If the storage was read-only, then bypass this! // if ( _fReadOnly ) { hr = S_OK; goto Cleanup; } // // Bind to the storage // _dwCurrentBindMode = STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE; hr = THR( BindToStorage( ) ); if ( FAILED( hr ) ) goto Cleanup; // // Loop thru the properties to count how many need to be persisted. // hr = THR( _pPropertyCache->GetNextItem( NULL, &pItem ) ); if ( FAILED( hr ) ) goto Cleanup; cDirtyCount = 0; while ( NULL != pItem ) { hr = STHR( pItem->IsDirty( ) ); if ( S_OK == hr ) { cDirtyCount ++; } hr = STHR( pItem->GetNextItem( &pItem ) ); if ( FAILED( hr ) ) goto Cleanup; if ( S_FALSE == hr ) break; // exit condition } // // If nothing is dirty, then bail. // if ( 0 == cDirtyCount ) { hr = S_FALSE; goto Cleanup; } // // Allocate memory to persist the properties in one call. // pSpecs = (PROPSPEC *) TraceAlloc( HEAP_ZERO_MEMORY, cDirtyCount * sizeof(*pSpecs) ); if ( NULL == pSpecs ) goto OutOfMemory; pValues = (PROPVARIANT *) TraceAlloc( HEAP_ZERO_MEMORY, cDirtyCount * sizeof(*pValues) ); if ( NULL == pValues ) goto OutOfMemory; pFmtIds = (FMTID *) TraceAlloc( HEAP_ZERO_MEMORY, cDirtyCount * sizeof(*pFmtIds) ); if ( NULL == pFmtIds ) goto OutOfMemory; // // Loop thru the properties filling in the structures. // hr = THR( _pPropertyCache->GetNextItem( NULL, &pItem ) ); if ( FAILED( hr ) ) goto Cleanup; cDirtyCount = 0; // reset while ( NULL != pItem ) { hr = STHR( pItem->IsDirty( ) ); if ( S_OK == hr ) { PROPVARIANT * ppropvar; hr = THR( pItem->GetPropertyValue( &ppropvar ) ); if ( FAILED( hr ) ) goto Cleanup; PropVariantInit( &pValues[ cDirtyCount ] ); hr = THR( PropVariantCopy( &pValues[ cDirtyCount ], ppropvar ) ); if ( FAILED( hr ) ) goto Cleanup; hr = THR( pItem->GetFmtId( &pFmtIds[ cDirtyCount ] ) ); if ( FAILED( hr ) ) goto Cleanup; pSpecs[ cDirtyCount ].ulKind = PRSPEC_PROPID; hr = THR( pItem->GetPropId( &pSpecs[ cDirtyCount ].propid ) ); if ( FAILED( hr ) ) goto Cleanup; cDirtyCount ++; } hr = STHR( pItem->GetNextItem( &pItem ) ); if ( FAILED( hr ) ) goto Cleanup; if ( S_FALSE == hr ) break; // exit condition } // // Make the calls! // hr = S_OK; // assume success! for ( cSource = 0; cSource < _cSources; cSource ++ ) { for ( idx = cStart = 0; idx < cDirtyCount; idx ++ ) { // // Try to batch up the properties. // if ( ( idx == cDirtyCount - 1 ) || ( !IsEqualGUID( pFmtIds[ idx ], pFmtIds[ idx + 1 ] ) ) ) { HRESULT hrSet; IPropertyStorage* pps; UINT uCodePage = 0; hrSet = THR( SHPropStgCreate( _rgpss[ cSource ] , pFmtIds[ idx ] , NULL , PROPSETFLAG_DEFAULT , _dwCurrentBindMode , OPEN_ALWAYS , &pps , &uCodePage ) ); if ( SUCCEEDED( hrSet ) ) { hrSet = THR( SHPropStgWriteMultiple( pps , &uCodePage , ( idx - cStart ) + 1 , pSpecs + cStart , pValues + cStart , PID_FIRST_USABLE ) ); pps->Release(); } if ( FAILED( hrSet ) ) { hr = hrSet; } cStart = idx + 1; } } } Cleanup: THR( ReleaseStorage( ) ); if ( NULL != pSpecs ) { TraceFree( pSpecs ); } if ( NULL != pValues ) { TraceFree( pValues ); } if ( NULL != pFmtIds ) { TraceFree( pFmtIds ); } HRETURN( hr ); OutOfMemory: hr = E_OUTOFMEMORY; goto Cleanup; } // // Description: // Binds to the storage. // // Return Values: // S_OK // Success! // // other HRESULTs. // HRESULT CSummaryPage::BindToStorage( void ) { TraceFunc( "" ); // // Valid object state // Assert( NULL != _pida ); Assert( NULL == _rgpss ); Assert( NULL != _rgdwDocType ); _fReadOnly = FALSE; HRESULT hr = S_OK; _rgpss = (IPropertySetStorage **) TraceAlloc( HEAP_ZERO_MEMORY, sizeof(IPropertySetStorage *) * _pida->cidl ); if ( _rgpss ) { for ( _cSources = 0; _cSources < _pida->cidl; _cSources ++ ) { LPITEMIDLIST pidl; hr = STHR( Item( _cSources, &pidl ) ); if ( hr == S_FALSE ) break; // exit condition DWORD dwAttribs = SFGAO_READONLY; TCHAR szName[MAX_PATH]; hr = THR( SHGetNameAndFlags( pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, szName, ARRAYSIZE(szName), &dwAttribs ) ); if ( SUCCEEDED( hr ) ) { PTSRV_FILETYPE ftType; hr = STHR( CheckForKnownFileType( szName, &ftType ) ); if ( SUCCEEDED( hr ) ) { _rgdwDocType[ _cSources ] = ftType; } if ( SFGAO_READONLY & dwAttribs ) { _fReadOnly = TRUE; } } // // Don't try to bind to it if we don't support it. // if ( FTYPE_UNSUPPORTED != _rgdwDocType[ _cSources ] ) { hr = THR( BindToObjectWithMode( pidl , _dwCurrentBindMode , TYPESAFEPARAMS( _rgpss[ _cSources ] ) ) ); if ( SUCCEEDED( hr ) ) { // // TODO: gpease 19-FEB-2001 // Test to see if the DOC is an RTF or OLESS document. But, how do // we do that? // } else { Assert( NULL == _rgpss[ _cSources ] ); _rgdwDocType[ _cSources ] = FTYPE_UNSUPPORTED; } } else { hr = THR( E_FAIL ); } ILFree( pidl ); } } else { hr = E_OUTOFMEMORY; } HRETURN( hr ); } // // Description: // Releases the storage. // // Return Values: // S_OK // Success! // HRESULT CSummaryPage::ReleaseStorage( void ) { TraceFunc( "" ); HRESULT hr = S_OK; ULONG idx = _cSources; if ( NULL != _rgpss ) { while ( 0 != idx ) { idx --; if ( NULL != _rgpss[ idx ] ) { _rgpss[ idx ]->Release( ); } } TraceFree( _rgpss ); _rgpss = NULL; } HRETURN( hr ); } // // Description: // Collates the properties from multiple sources and places them in // pPropertyCache. It marks the properties "multiple" if more than one // property is found and their values don't match. // // NOTE: the number of entries in rgpPropertyCachesIn is _cSources. // void CSummaryPage::CollateMultipleProperties( CPropertyCache ** rgpPropertyCachesIn ) { TraceFunc( "" ); HRESULT hr; ULONG cSource; CPropertyCacheItem * pItem; CPropertyCacheItem * pItemCache; CPropertyCacheItem * pItemCacheLast; Assert( NULL != rgpPropertyCachesIn ); // // If any of the sources returned "no properties", then the union // of those properties is "none." We can take the easy way out and // bail here. // for ( cSource = 0; cSource < _cSources; cSource ++ ) { if ( NULL == rgpPropertyCachesIn[ cSource ] ) { Assert( NULL == _pPropertyCache ); // This must be NULL to ensure we bail above. goto Cleanup; // done - nothing to do } } // // First give ownership of the first source to the _pPropertyCache. From // there we will prune and manipulate that list into the final list. // _pPropertyCache = rgpPropertyCachesIn[ 0 ]; rgpPropertyCachesIn[ 0 ] = NULL; // // Now loop thru the other sources comparing them to the orginal list. // pItemCache = NULL; for( ;; ) { PROPID propidCache; FMTID fmtidCache; BOOL fFoundMatch = FALSE; pItemCacheLast = pItemCache; hr = STHR( _pPropertyCache->GetNextItem( pItemCache, &pItemCache ) ); if ( FAILED( hr ) ) goto Cleanup; if ( S_OK != hr ) break; // no more items - exit loop hr = THR( pItemCache->GetPropId( &propidCache ) ); if ( FAILED( hr ) ) goto Cleanup; hr = THR( pItemCache->GetFmtId( &fmtidCache ) ); if ( FAILED( hr ) ) goto Cleanup; for ( cSource = 1; cSource < _cSources; cSource ++ ) { pItem = NULL; for ( ;; ) { PROPID propid; FMTID fmtid; hr = STHR( rgpPropertyCachesIn[ cSource ]->GetNextItem( pItem, &pItem ) ); if ( S_OK != hr ) break; // end of list - exit loop hr = THR( pItem->GetPropId( &propid ) ); if ( FAILED( hr ) ) goto Cleanup; hr = THR( pItem->GetFmtId( &fmtid ) ); if ( FAILED( hr ) ) goto Cleanup; if ( IsEqualIID( fmtid, fmtidCache ) && ( propid == propidCache ) ) { LPCWSTR pcszItem; LPCWSTR pcszItemCache; // // Matched! // fFoundMatch = TRUE; hr = THR( pItem->GetPropertyStringValue( &pcszItem ) ); if ( FAILED( hr ) ) break; // ignore it - it can't be displayed hr = THR( pItemCache->GetPropertyStringValue( &pcszItemCache ) ); if ( FAILED( hr ) ) break; // ignore it - it can't be displayed if ( 0 != StrCmp( pcszItem, pcszItemCache ) ) { THR( pItemCache->MarkMultiple( ) ); // ignore failure } break; // exit cache loop } else { fFoundMatch = FALSE; } } // // If it is missing from at least one source, we must remove it. There // is no need to keep searching the other sources. // if ( !fFoundMatch ) break; } // for: cSource if ( !fFoundMatch ) { // // If a match was not found, delete the property from the property cache list. // hr = STHR( _pPropertyCache->RemoveItem( pItemCache ) ); if ( S_OK != hr ) goto Cleanup; pItemCache = pItemCacheLast; } } Cleanup: TraceFuncExit( ); } // // Description: // Walks the property cache and sets all properties to READ-ONLY mode. // void CSummaryPage::ChangeGatheredPropertiesToReadOnly( CPropertyCache * pCacheIn ) { TraceFunc( "" ); CPropertyCacheItem * pItem = NULL; if ( NULL == pCacheIn ) goto Cleanup; for( ;; ) { HRESULT hr = STHR( pCacheIn->GetNextItem( pItem, &pItem ) ); if ( S_OK != hr ) break; // must be done. THR( pItem->MarkReadOnly( ) ); // ignore failure and keep moving } Cleanup: TraceFuncExit( ); } // // Description: // Checks to see if the property set contains the music copy-protection // property, if the property is of type VT_BOOL and if that property is // set to TRUE. // // Return Values: // S_OK // Property found and is set to TRUE. // // S_FALSE // Property not found or is set to FALSE. // // E_INVALIDARG // pCacheIn is NULL. // // other HRESULTs // HRESULT CSummaryPage::CheckForCopyProtection( CPropertyCache * pCacheIn ) { TraceFunc( "" ); HRESULT hr; CPropertyCacheItem * pItem; HRESULT hrReturn = S_FALSE; if ( NULL == pCacheIn ) goto InvalidArg; hr = STHR( pCacheIn->FindItemEntry( &FMTID_DRM, PIDDRSI_PROTECTED, &pItem ) ); if ( S_OK == hr ) { PROPVARIANT * ppropvar; hr = THR( pItem->GetPropertyValue( &ppropvar ) ); if ( S_OK == hr ) { if ( ( VT_BOOL == ppropvar->vt ) && ( VARIANT_TRUE == ppropvar->boolVal ) ) { hrReturn = S_OK; } } } Cleanup: HRETURN( hrReturn ); InvalidArg: hr = THR( E_INVALIDARG ); goto Cleanup; }