1805 lines
42 KiB
C++
1805 lines
42 KiB
C++
//
|
|
// 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;
|
|
} |