|
|
//
// 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 "PropertyCacheItem.h"
#pragma hdrstop
// ***************************************************************************
//
// Class statics
//
// ***************************************************************************
WCHAR CPropertyCacheItem::_szMultipleString[ MAX_PATH ] = { 0 };
// ***************************************************************************
//
// Constructor / Destructor / Initialization
//
// ***************************************************************************
//
// CreateInstance
//
HRESULT CPropertyCacheItem::CreateInstance( CPropertyCacheItem ** ppItemOut ) { TraceFunc( "" );
HRESULT hr;
Assert( NULL != ppItemOut );
CPropertyCacheItem * pthis = new CPropertyCacheItem; if ( NULL != pthis ) { hr = THR( pthis->Init( ) ); if ( SUCCEEDED( hr ) ) { *ppItemOut = pthis; } else { pthis->Destroy( ); } } else { hr = E_OUTOFMEMORY; }
HRETURN( hr ); }
//
// Constructor
//
CPropertyCacheItem::CPropertyCacheItem( void ) { TraceFunc( "" );
Assert( NULL == _pNext );
Assert( FALSE == _fReadOnly ); Assert( FALSE == _fDirty ); Assert( IsEqualIID( _fmtid, CLSID_NULL ) ); Assert( 0 == _propid ); Assert( VT_EMPTY == _vt ); Assert( 0 == _uCodePage ); Assert( VT_EMPTY == _propvar.vt );
Assert( 0 == _idxDefProp ); Assert( NULL == _ppui ); Assert( 0 == _wszTitle[ 0 ] ); Assert( 0 == _wszDesc[ 0 ] ); Assert( 0 == _wszValue[ 0 ] ); Assert( 0 == _wszHelpFile[ 0 ] ); Assert( NULL == _pDefVals );
TraceFuncExit( ); }
//
// Initialization
//
HRESULT CPropertyCacheItem::Init( void ) { TraceFunc( "" );
HRESULT hr = S_OK;
_idxDefProp = -1L;
HRETURN( hr ); }
//
// Destructor
//
CPropertyCacheItem::~CPropertyCacheItem( void ) { TraceFunc( "" );
if ( NULL != _ppui ) { _ppui->Release( ); }
if ( NULL != _pDefVals ) { for ( ULONG idx = 0; NULL != _pDefVals[ idx ].pszName; idx ++ ) { TraceFree( _pDefVals[ idx ].pszName ); } TraceFree( _pDefVals ); }
TraceFuncExit( ); }
//
// Description:
// Attempts to destroy the property item.
//
// Return Values:
// S_OK
// Success!
//
HRESULT CPropertyCacheItem::Destroy( void ) { TraceFunc( "" );
HRESULT hr = S_OK;
delete this;
HRETURN( hr ); }
// ***************************************************************************
//
// Private Methods
//
// ***************************************************************************
//
// Description:
// Looks in our "default property list" for a matching fmtid/propid
// combination and sets _idxDefProp to that index.
//
// Return Values:
// S_OK
// Success!
//
// HRESULT_FROM_WIN32(ERROR_NOT_FOUND)
// Entry was not found. _idxDefProp is invalid.
//
HRESULT CPropertyCacheItem::FindDefPropertyIndex( void ) { TraceFunc( "" );
HRESULT hr = S_OK;
if ( -1L == _idxDefProp ) { ULONG idx;
for ( idx = 0; NULL != g_rgDefPropertyItems[ idx ].pFmtID; idx ++ ) { if ( IsEqualPFID( _fmtid, *g_rgDefPropertyItems[ idx ].pFmtID ) && _propid == g_rgDefPropertyItems[ idx ].propID ) { _idxDefProp = idx; break; } }
if ( -1L == _idxDefProp ) { // don't wrap.
hr = HRESULT_FROM_WIN32( ERROR_NOT_FOUND ); } }
HRETURN( hr ); }
//
// Description:
// Check the static member _szMultipleString to make sure it has been
// loaded.
//
void CPropertyCacheItem::EnsureMultipleStringLoaded( void ) { TraceFunc( "" );
if ( 0 == _szMultipleString[ 0 ] ) { int iRet = LoadString( g_hInstance, IDS_COMPOSITE_MISMATCH, _szMultipleString, ARRAYSIZE(_szMultipleString) ); AssertMsg( 0 != iRet, "Missing string resource?" ); }
TraceFuncExit( ); }
// ***************************************************************************
//
// Public Methods
//
// ***************************************************************************
//
// Description:
// Stores a IPropetyUI interface to be used for translating the property
// "properties" into different forms.
//
// Return Values:
// S_OK
// Success!
//
HRESULT CPropertyCacheItem::SetPropertyUIHelper( IPropertyUI * ppuiIn ) { TraceFunc( "" );
HRESULT hr = S_OK;
//
// If we have an existing helper, release it.
//
if ( NULL != _ppui ) { _ppui->Release( ); }
_ppui = ppuiIn;
if ( NULL != _ppui ) { _ppui->AddRef( ); }
HRETURN( hr ); }
//
// Description:
// Retrieves a copy (AddRef'ed) of the IPropertyUI interface that this
// property item is using.
//
// Return Values:
// S_OK
// Success! pppuiOut is valid.
//
// S_FALSE
// Success, but pppuiOut is NULL.
//
// E_POINTER
// pppuiOut is NULL.
//
// other HRESULTs.
//
HRESULT CPropertyCacheItem::GetPropertyUIHelper( IPropertyUI ** pppuiOut ) { TraceFunc( "" );
HRESULT hr = S_OK;
if ( NULL == pppuiOut ) goto InvalidPointer;
//
// If we have an existing helper, release it.
//
if ( NULL == _ppui ) { *pppuiOut = NULL; hr = S_FALSE; } else { hr = THR( _ppui->TYPESAFEQI( *pppuiOut ) ); }
Cleanup: HRETURN( hr );
InvalidPointer: hr = THR( E_POINTER ); goto Cleanup; }
//
// Description:
// Changes the _pNext member variable
//
// Return Values:
// S_OK
// Success!
//
HRESULT CPropertyCacheItem::SetNextItem( CPropertyCacheItem * pNextIn ) { TraceFunc( "" );
HRESULT hr = S_OK;
_pNext = pNextIn;
HRETURN( hr ); }
//
// Description:
// Retrieves the _pNext member variable
//
// Return Values:
// S_OK
// Success!
//
// S_FALSE
//
//
// E_POINTER
// ppNextOut is NULL.
//
HRESULT CPropertyCacheItem::GetNextItem( CPropertyCacheItem ** ppNextOut ) { TraceFunc( "" );
HRESULT hr;
if ( NULL != ppNextOut ) { *ppNextOut = _pNext;
if ( NULL == _pNext ) { hr = S_FALSE; } else { hr = S_OK; } } else { hr = THR( E_POINTER ); }
HRETURN( hr ); }
//
// Description:
// Sets the FMTID of the property.
//
// Return Values:
// S_OK
// Success!
//
HRESULT CPropertyCacheItem::SetFmtId( const FMTID * pFmtIdIn ) { TraceFunc( "" );
HRESULT hr = S_OK;
_fmtid = *pFmtIdIn; _idxDefProp = -1;
HRETURN( hr ); }
//
// Description:
// Retrieves the FMTID of the property.
//
// Return Values:
// S_OK
// Success!
//
// E_POINTER
// pfmtidOut is invalid.
//
HRESULT CPropertyCacheItem::GetFmtId( FMTID * pfmtidOut ) { TraceFunc( "" );
HRESULT hr;
if ( NULL != pfmtidOut ) { *pfmtidOut = _fmtid; hr = S_OK; } else { hr = THR( E_POINTER ); }
HRETURN( hr ); }
//
// Description:
// Sets the PROPID of the property.
//
// Return Values:
// S_OK
// Success!
//
HRESULT CPropertyCacheItem::SetPropId( PROPID propidIn ) { TraceFunc( "" );
HRESULT hr = S_OK;
_propid = propidIn; _idxDefProp = -1;
HRETURN( hr ); }
//
// Description:
// Retrieves the PROPID of the property.
//
// Return Values:
// S_OK
// Success!
//
// E_POINTER
// ppropidOut is invalid.
//
HRESULT CPropertyCacheItem::GetPropId( PROPID * ppropidOut ) { TraceFunc( "" );
HRESULT hr;
if ( NULL == ppropidOut ) goto InvalidPointer;
*ppropidOut = _propid;
hr = S_OK;
Cleanup: HRETURN( hr );
InvalidPointer: hr = THR( E_POINTER ); goto Cleanup; }
//
// Description:
// Sets the VARTYPE of the property.
//
// Return Values:
// S_OK
// Success!
//
HRESULT CPropertyCacheItem::SetDefaultVarType( VARTYPE vtIn ) { TraceFunc( "" );
HRESULT hr = S_OK;
_vt = vtIn;
HRETURN( hr ); }
//
// Description:
// Retrieves the VARTYPE of the property.
//
// Return Values:
// S_OK
// Success!
//
// E_POINTER
// pvtOut is invalid.
//
HRESULT CPropertyCacheItem::GetDefaultVarType( VARTYPE * pvtOut ) { TraceFunc( "" );
HRESULT hr;
if ( NULL != pvtOut ) { switch ( _vt ) { case VT_VECTOR | VT_VARIANT: Assert( _propvar.capropvar.cElems == 2 ); *pvtOut = _propvar.capropvar.pElems[ 1 ].vt; hr = S_OK; break;
case VT_VECTOR | VT_LPSTR: *pvtOut = VT_LPSTR; hr = S_OK; break;
case VT_VECTOR | VT_LPWSTR: *pvtOut = VT_LPWSTR; hr = S_OK; break;
default: *pvtOut = _vt; hr = S_OK; break; } } else { hr = E_POINTER; }
HRETURN( hr ); }
//
// Description:
// Stores the Code Page for the property value.
//
// Return Values:
// S_OK
// Success!
//
HRESULT CPropertyCacheItem::SetCodePage( UINT uCodePageIn ) { TraceFunc( "" );
HRESULT hr = S_OK;
_uCodePage = uCodePageIn;
HRETURN( hr ); }
//
// Description:
// Retrieves the Code Page for the property value.
//
// Return Values:
// S_OK
// Success!
//
// E_POINTER
// puCodePageOut is NULL.
//
HRESULT CPropertyCacheItem::GetCodePage( UINT * puCodePageOut ) { TraceFunc( "" );
HRESULT hr;
if ( NULL != puCodePageOut ) { *puCodePageOut = _uCodePage; hr = S_OK; } else { hr = THR( E_POINTER ); }
HRETURN( hr ); }
//
// Description:
// Retrieves the property name for this property to be display in the UI.
// The pointer handed out does not need to be freed.
//
// Return Values:
// S_OK
// Success!
//
// E_POINTER
// ppwszOut is NULL.
//
// E_UNEXPECTED
// Need to call SetPropertyUIHelper( ) before calling this method.
//
// HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR)
// The resource string is malformed.
//
// other HRESULTs
//
HRESULT CPropertyCacheItem::GetPropertyTitle( LPCWSTR * ppwszOut ) { TraceFunc( "" );
HRESULT hr;
if ( NULL == ppwszOut ) goto InvalidPointer;
*ppwszOut = NULL;
if ( NULL == _ppui ) goto UnexpectedState;
hr = THR( _ppui->GetDisplayName( _fmtid, _propid, PUIFNF_DEFAULT, _wszTitle, ARRAYSIZE(_wszTitle) ) ); // Even if this fails, the buffer will still be valid and empty.
*ppwszOut = _wszTitle;
hr = S_OK;
Cleanup: HRETURN( hr );
InvalidPointer: hr = THR( E_POINTER ); goto Cleanup;
UnexpectedState: hr = THR( E_UNEXPECTED ); goto Cleanup; }
//
// Description:
// Retrieves the property name for this property to be display in the UI.
// The pointer handed out does not need to be freed.
//
// Return Values:
// S_OK
// Success!
//
// E_POINTER
// ppwszOut is NULL.
//
// E_UNEXPECTED
// Need to call SetPropertyUIHelper( ) before calling this method.
//
// HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR)
// The resource string is malformed.
//
// other HRESULTs
//
HRESULT CPropertyCacheItem::GetPropertyDescription( LPCWSTR * ppwszOut ) { TraceFunc( "" );
HRESULT hr;
if ( NULL == ppwszOut ) goto InvalidPointer;
*ppwszOut = NULL;
if ( NULL == _ppui ) goto UnexpectedState;
hr = THR( _ppui->GetPropertyDescription( _fmtid, _propid, _wszDesc, ARRAYSIZE(_wszDesc) ) ); // if it failed, the buffer will still be valid and empty.
*ppwszOut = _wszDesc;
hr = S_OK;
Cleanup: HRETURN( hr );
InvalidPointer: hr = THR( E_POINTER ); goto Cleanup;
UnexpectedState: hr = THR( E_UNEXPECTED ); goto Cleanup; }
//
// Description:
// Retrieves the help information about a property. The pointer handed
// out to the help file does not need to be freed.
//
// Return Values:
// S_OK
// Success!
//
// E_POINTER
// ppwszFileOut or puHelpIDOut is NULL.
//
// E_UNEXPECTED
// Need to call SetPropertyUIHelper( ) before calling this method.
//
// other HRESULTs
//
HRESULT CPropertyCacheItem::GetPropertyHelpInfo( LPCWSTR * ppwszFileOut , UINT * puHelpIDOut ) { TraceFunc( "" );
HRESULT hr;
if (( NULL == ppwszFileOut ) || ( NULL == puHelpIDOut )) goto InvalidPointer;
*ppwszFileOut = NULL; *puHelpIDOut = 0;
if ( NULL == _ppui ) goto UnexpectedState;
hr = THR( _ppui->GetHelpInfo( _fmtid, _propid, _wszHelpFile, ARRAYSIZE(_wszHelpFile), puHelpIDOut ) ); if ( FAILED( hr ) ) goto Cleanup;
*ppwszFileOut = _wszHelpFile;
hr = S_OK;
Cleanup: HRETURN( hr );
InvalidPointer: hr = THR( E_POINTER ); goto Cleanup;
UnexpectedState: hr = THR( E_UNEXPECTED ); goto Cleanup; }
//
// Description:
// Retrieves a LPWSTR the to a buffer (own by the property) that can
// used to display the property as a string. The pointer handed out
// does not need to be freed.
//
// Return Values:
// S_OK
// Success!
//
// E_POINTER
// ppwszOut is NULL.
//
// E_UNEXPECTED
// Need to call SetPropertyUIHelper( ) before calling this method.
//
// other HRESULTs
//
HRESULT CPropertyCacheItem::GetPropertyStringValue( LPCWSTR * ppwszOut ) { TraceFunc( "" );
HRESULT hr;
if ( NULL == ppwszOut ) goto InvalidPointer;
*ppwszOut = NULL;
if ( NULL == _ppui ) goto UnexpectedState;
//
// If the property has been marked to indicate multiple values, then
// return the "< multiple values >" string.
//
if ( _fMultiple ) { EnsureMultipleStringLoaded( ); *ppwszOut = _szMultipleString; hr = S_OK; goto Cleanup; }
if ( ( VT_VECTOR | VT_VARIANT ) == _vt ) { Assert( 2 == _propvar.capropvar.cElems );
hr = THR( _ppui->FormatForDisplay( _fmtid, _propid, &_propvar.capropvar.pElems[ 1 ], PUIFFDF_DEFAULT, _wszValue, ARRAYSIZE(_wszValue) ) ); if ( FAILED( hr ) ) goto Cleanup; } else { hr = THR( _ppui->FormatForDisplay( _fmtid, _propid, &_propvar, PUIFFDF_DEFAULT, _wszValue, ARRAYSIZE(_wszValue) ) ); if ( FAILED( hr ) ) goto Cleanup; }
*ppwszOut = _wszValue;
hr = S_OK;
Cleanup: HRETURN( hr );
InvalidPointer: hr = THR( E_POINTER ); goto Cleanup;
UnexpectedState: hr = THR( E_UNEXPECTED ); goto Cleanup; }
//
// Description:
// Retrieves the Image Index for a property.
//
// Return Values:
// S_OK
// Success!
//
// E_POINTER
// piImageOut is NULL.
//
HRESULT CPropertyCacheItem::GetImageIndex( int * piImageOut ) { TraceFunc( "" );
HRESULT hr;
if ( NULL == piImageOut ) goto InvalidPointer;
// Initlize to read-only
*piImageOut = PTI_PROP_READONLY;
if ( !_fReadOnly ) { // don't wrap - this can fail
hr = FindDefPropertyIndex( ); if ( S_OK == hr ) { if ( !g_rgDefPropertyItems[ _idxDefProp ].fReadOnly ) { *piImageOut = PTI_PROP_READWRITE; } } }
hr = S_OK;
Cleanup: HRETURN( hr );
InvalidPointer: hr = THR( E_POINTER ); goto Cleanup; }
//
// Description:
// Retrieves the Property Folder IDentifer (PFID) for this property.
//
// Return Values:
// S_OK
// Success!
//
// S_FALSE
// Call succeeded, but there isn't a PFID for this property.
//
// E_POINTER
// ppdifOut is NULL.
//
HRESULT CPropertyCacheItem::GetPFID( const PFID ** ppPFIDOut ) { TraceFunc( "" );
HRESULT hr;
if ( NULL == ppPFIDOut ) goto InvalidPointer;
*ppPFIDOut = NULL;
// don't wrap - this can fail.
hr = FindDefPropertyIndex( ); if ( S_OK == hr ) { *ppPFIDOut = g_rgDefPropertyItems[ _idxDefProp ].ppfid; } if ( NULL == *ppPFIDOut ) { hr = S_FALSE; }
Cleanup: HRETURN( hr );
InvalidPointer: hr = THR( E_POINTER ); goto Cleanup; }
//
// Description:
// Retrieves the CLSID of the control to CoCreate( ) to edit this property.
// The object must support the IEditVariantsInPlace interface. This method
// will return S_FALSE (pclsidOut will be CLSID_NULL) is the property is
// read-only.
//
// Return Values:
// S_OK
// Success!
//
// S_FALSE
// Success, but the CLSID is CLSID_NULL.
//
// E_POINTER
// pclsidOut is NULL.
//
// other HRESULTs
//
HRESULT CPropertyCacheItem::GetControlCLSID( CLSID * pclsidOut ) { TraceFunc( "" );
HRESULT hr;
if ( NULL == pclsidOut ) goto InvalidPointer;
// don't wrap - this can fail.
hr = FindDefPropertyIndex( ); if ( FAILED( hr ) ) goto Cleanup;
//
// If it is read-only, return S_FALSE and a CLSID of CLSID_NULL.
//
if ( g_rgDefPropertyItems[ _idxDefProp ].fReadOnly ) { *pclsidOut = CLSID_NULL; hr = S_FALSE; goto Cleanup; }
*pclsidOut = *g_rgDefPropertyItems[ _idxDefProp ].pclsidControl;
if ( CLSID_NULL == *pclsidOut ) { hr = S_FALSE; }
Cleanup: HRETURN( hr );
InvalidPointer: hr = THR( E_POINTER ); goto Cleanup; }
//
// Description:
// Retrieve the current property value in the form on a variant. If the
// property is backed by multiple sources, then S_FALSE is returned and
// the variant is empty.
//
// Return Value:
// S_OK
// Success!
//
// S_FALSE
// Multiple value property. Variant is empty.
//
// E_POINTER
// ppvarOut is NULL.
//
// E_FAIL
// Property is READ-ONLY.
//
// other HRESULTs.
//
STDMETHODIMP CPropertyCacheItem::GetPropertyValue( PROPVARIANT ** ppvarOut ) { TraceFunc( "" );
HRESULT hr;
if ( NULL == ppvarOut ) goto InvalidPointer;
*ppvarOut = &_propvar;
hr = S_OK;
Cleanup: HRETURN( hr );
InvalidPointer: hr = THR( E_POINTER ); goto Cleanup; }
//
// Description:
// Marks the property as being dirty.
//
// Return Values:
// S_OK
// Success!
//
// other HRESULTs.
//
STDMETHODIMP CPropertyCacheItem::MarkDirty( void ) { TraceFunc( "" );
HRESULT hr = S_OK;
_fDirty = TRUE; _fMultiple = FALSE;
HRETURN( hr ); }
//
// Description:
// Checks to see if the property has been marked dirty.
//
// Return Values:
// S_OK
// Success and the property is dirty.
//
// S_FALSE
// Success and the proprety is clean.
//
STDMETHODIMP CPropertyCacheItem::IsDirty( void ) { TraceFunc( "" );
HRESULT hr;
if ( _fDirty ) { hr = S_OK; } else { hr = S_FALSE; }
HRETURN( hr ); }
//
// Description:
// Marks the property read-only.
//
// Return Values:
// S_OK
// Success!
//
STDMETHODIMP CPropertyCacheItem::MarkReadOnly( void ) { TraceFunc( "" );
HRESULT hr = S_OK;
_fReadOnly = TRUE;
HRETURN( hr ); }
//
// Description:
// Retrieves an array of pointer to an array of strings that are
// zero-indexed. This is used for properties that have well-known
// enumerated states that are indexed (such as "Status").
//
// Return Values:
// S_OK
// Success!
//
// S_FALSE
// Proprety doesn't support enumerated states.
//
// E_INVALIDARG
// ppDefValOut is NULL.
//
// other HRESULTs.
//
STDMETHODIMP CPropertyCacheItem::GetStateStrings( DEFVAL ** ppDefValOut ) { TraceFunc( "" );
HRESULT hr; ULONG idx; ULONG idxEnd;
//
// Check parameters.
//
if ( NULL == ppDefValOut ) goto InvalidPointer;
*ppDefValOut = NULL;
if ( NULL == _ppui ) goto UnexpectedState;
// don't wrap - this can fail.
hr = FindDefPropertyIndex( ); if ( FAILED( hr ) ) goto Cleanup;
if ( !g_rgDefPropertyItems[ _idxDefProp ].fEnumeratedValues ) { hr = S_FALSE; goto Cleanup; }
if ( NULL != _pDefVals ) { *ppDefValOut = _pDefVals; hr = S_OK; goto Cleanup; }
AssertMsg( NULL != g_rgDefPropertyItems[ _idxDefProp ].pDefVals, "Why did one mark this property as ENUM, but provide no items?" );
//
// Since we moved all the string in SHELL32, we need to use our table
// enumerate the property values to retrieve all the strings. Since
// our table is read-only, we need to allocate a copy of the DEFVALs
// for this property and have PropertyUI fill in the blanks.
//
_pDefVals = (DEFVAL *) TraceAlloc( HEAP_ZERO_MEMORY, sizeof(DEFVAL) * g_rgDefPropertyItems[ _idxDefProp ].cDefVals ); if ( NULL == _pDefVals ) goto OutOfMemory;
CopyMemory( _pDefVals, g_rgDefPropertyItems[ _idxDefProp ].pDefVals, sizeof(DEFVAL) * g_rgDefPropertyItems[ _idxDefProp ].cDefVals );
idxEnd = g_rgDefPropertyItems[ _idxDefProp ].cDefVals - 1; // the last entry is always { 0, NULL }
for ( idx = 0; idx < idxEnd; idx ++ ) { PROPVARIANT propvar;
propvar.vt = g_rgDefPropertyItems[ _idxDefProp ].vt; propvar.ulVal = g_rgDefPropertyItems[ _idxDefProp ].pDefVals[ idx ].ulVal;
_pDefVals[ idx ].pszName = (LPTSTR) TraceAlloc( HEAP_ZERO_MEMORY, sizeof(_wszValue) ); if ( NULL == _pDefVals[ idx ].pszName ) goto OutOfMemory;
hr = THR( _ppui->FormatForDisplay( _fmtid, _propid, &propvar, PUIFFDF_DEFAULT, _pDefVals[ idx ].pszName, ARRAYSIZE(_wszValue) ) ); if ( FAILED( hr ) ) goto Cleanup; }
*ppDefValOut = _pDefVals; hr = S_OK;
Cleanup: HRETURN( hr );
InvalidPointer: hr = THR( E_POINTER ); goto Cleanup;
OutOfMemory: hr = E_OUTOFMEMORY; goto Cleanup;
UnexpectedState: hr = THR( E_UNEXPECTED ); goto Cleanup; }
//
// Description:
// Marks a property as having multiple values. This should only be called
// when multiple source documents have been selected and the values are
// all different.
//
// Return Value:
// S_OK
// Success!
//
HRESULT CPropertyCacheItem::MarkMultiple( void ) { TraceFunc( "" );
HRESULT hr = S_OK;
_fMultiple = TRUE; PropVariantClear( &_propvar );
HRETURN( hr ); }
|