mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
737 lines
20 KiB
737 lines
20 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1991 - 2000.
|
|
//
|
|
// File: propfilt.cxx
|
|
//
|
|
// Contents: Code to filter properties on files
|
|
//
|
|
// Classes: COLEPropertyEnum
|
|
// CDocStatPropertyEnum
|
|
//
|
|
// History: 93-Oct-18 DwightKr Created
|
|
// 94-May-23 DwightKr Converted OFS to use OLE interfaces
|
|
// 95-Feb-07 KyleP Rewrote
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <pch.cxx>
|
|
#pragma hdrstop
|
|
|
|
#include <propspec.hxx>
|
|
#include <ciguid.hxx>
|
|
#include <tgrow.hxx>
|
|
|
|
#include "propfilt.hxx"
|
|
|
|
static CFullPropSpec psAttr( guidStorage, PID_STG_ATTRIBUTES );
|
|
static CFullPropSpec psSize( guidStorage, PID_STG_SIZE );
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: CheckResult
|
|
//
|
|
// Synopsis: DebugOut and Throw on an error
|
|
//
|
|
// Arguments: [hr] -- result code to be tested
|
|
// [pcError] -- debugout string for debug builds
|
|
//
|
|
// History 95-Dec-19 dlee created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
static inline void CheckResult(
|
|
HRESULT hr,
|
|
char * pcError )
|
|
{
|
|
if ( !SUCCEEDED( hr ) )
|
|
{
|
|
ciDebugOut(( DEB_IERROR, pcError, hr ));
|
|
THROW( CException( hr ) );
|
|
}
|
|
} //CheckResult
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocStatPropertyEnum::CDocStatPropertyEnum, public
|
|
//
|
|
// Synopsis: Constructor
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
CDocStatPropertyEnum::CDocStatPropertyEnum( ICiCOpenedDoc * Document )
|
|
: _PropertyStorage( 0 )
|
|
{
|
|
//
|
|
// Safely get property storage
|
|
//
|
|
{
|
|
IPropertyStorage *PropertyStorage;
|
|
HRESULT hr = Document->GetStatPropertyEnum( &PropertyStorage );
|
|
|
|
CheckResult( hr, "Could not get stat property storage %x\n" );
|
|
|
|
_PropertyStorage.Set( PropertyStorage );
|
|
}
|
|
|
|
//
|
|
// Safely get property enumerator
|
|
//
|
|
{
|
|
IEnumSTATPROPSTG *PropertyEnum;
|
|
HRESULT hr = _PropertyStorage->Enum( &PropertyEnum );
|
|
|
|
CheckResult( hr, "Could not get property enum %x\n" );
|
|
|
|
_PropertyEnum.Set( PropertyEnum );
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocStatPropertyEnum::~CDocStatPropertyEnum, public
|
|
//
|
|
// Synopsis: Destructor
|
|
//
|
|
// History: 93-Nov-27 DwightKr Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
CDocStatPropertyEnum::~CDocStatPropertyEnum()
|
|
{
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocStatPropertyEnum::GetPropertySetLocale, public
|
|
//
|
|
// Synopsis: Get locale if available
|
|
//
|
|
// Arguments: [locale] - Locale
|
|
//
|
|
// Returns: HRESULT from property storage
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
HRESULT CDocStatPropertyEnum::GetPropertySetLocale(LCID & locale)
|
|
{
|
|
return ::GetPropertySetLocale(_PropertyStorage.GetPointer(), locale);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocStatPropertyEnum::CacheVariant, public
|
|
//
|
|
// Synopsis: Load a specfied variant into the cache
|
|
//
|
|
// Arguments: [propid] - PROPID specifying what to load
|
|
//
|
|
// Returns: HRESULT from property storage
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
HRESULT CDocStatPropertyEnum::CacheVariant( PROPID propid )
|
|
{
|
|
//
|
|
// Set up propspec for property read
|
|
//
|
|
|
|
PROPSPEC prspec;
|
|
prspec.ulKind = PRSPEC_PROPID;
|
|
prspec.propid = propid;
|
|
|
|
//
|
|
// Read out the property value
|
|
//
|
|
|
|
PROPVARIANT var;
|
|
HRESULT hr = _PropertyStorage->ReadMultiple( 1, &prspec, &var );
|
|
|
|
if (!SUCCEEDED( hr )) {
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Set up storage variant
|
|
//
|
|
|
|
switch ( var.vt )
|
|
{
|
|
case VT_I8:
|
|
_varCurrent.SetI8( var.hVal );
|
|
break;
|
|
|
|
case VT_LPWSTR:
|
|
_varCurrent.SetLPWSTR( var.pwszVal );
|
|
|
|
if ( !_varCurrent.IsValid() )
|
|
return E_OUTOFMEMORY;
|
|
|
|
break;
|
|
|
|
case VT_UI4:
|
|
_varCurrent.SetUI4( var.lVal );
|
|
break;
|
|
|
|
case VT_FILETIME:
|
|
_varCurrent.SetFILETIME( var.filetime );
|
|
break;
|
|
|
|
default:
|
|
ciDebugOut(( DEB_IERROR, "Unknown storage type %x\n", var.vt ));
|
|
break;
|
|
}
|
|
|
|
PropVariantClear( &var );
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CDocStatPropertyEnum::Next, public
|
|
//
|
|
// Synopsis: Move to next property
|
|
//
|
|
// Arguments: [ps] -- Property Specification returned
|
|
//
|
|
// Returns: Property value, or 0 if end of properties.
|
|
//
|
|
// History: 95-Feb-07 KyleP Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
CStorageVariant const * CDocStatPropertyEnum::Next( CFullPropSpec & ps )
|
|
{
|
|
//
|
|
// Get the next property in the enumeration
|
|
//
|
|
|
|
STATPROPSTG StatPropStg;
|
|
ULONG cFetched;
|
|
HRESULT hr = _PropertyEnum->Next( 1, &StatPropStg, &cFetched );
|
|
|
|
//
|
|
// If we failed or there were no more to fetch, then we're done
|
|
//
|
|
|
|
if (!SUCCEEDED( hr ) || 0 == cFetched)
|
|
return 0;
|
|
|
|
Win4Assert( NULL == StatPropStg.lpwstrName );
|
|
|
|
//
|
|
// Load the property returned by the enumeration
|
|
//
|
|
|
|
hr = CacheVariant( StatPropStg.propid );
|
|
|
|
if (!SUCCEEDED( hr ))
|
|
return 0;
|
|
|
|
//
|
|
// Set guid and propid for full property set
|
|
//
|
|
|
|
ps.SetPropSet( guidStorage );
|
|
ps.SetProperty( StatPropStg.propid );
|
|
|
|
//
|
|
// Return the cached property
|
|
//
|
|
|
|
return &_varCurrent;
|
|
} //Next
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: COLEPropertySetEnum::Next, public
|
|
//
|
|
// Synopsis: Move to next property set
|
|
//
|
|
// Returns: Pointer to GUID of property set. 0 if at end of sets.
|
|
//
|
|
// History 95-Dec-19 dlee created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
GUID const * COLEPropertySetEnum::Next()
|
|
{
|
|
if ( 0 == _xPropSetEnum.GetPointer() )
|
|
return 0;
|
|
|
|
// Have we exhaused the current buffer of property sets? If so, then
|
|
// load up another buffer.
|
|
|
|
if ( _iPropSet == _cPropSets )
|
|
{
|
|
_iPropSet = 0;
|
|
_cPropSets = 0;
|
|
|
|
HRESULT hr = _xPropSetEnum->Next( cMaxSetsCached,
|
|
_aPropSets,
|
|
&_cPropSets );
|
|
|
|
CheckResult( hr, "PropSetEnum->Next failed hr = 0x%x\n" );
|
|
}
|
|
|
|
if ( _cPropSets > 0 )
|
|
{
|
|
_iPropSet++;
|
|
return &_aPropSets[ _iPropSet - 1 ].fmtid;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
} //Next
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: COLEPropertySetEnum::COLEPropertySetEnum, public
|
|
//
|
|
// Synopsis: Opens a storage, property set storage, and enumerator
|
|
//
|
|
// Arguments: [Document] -- opened document
|
|
//
|
|
// History 95-Dec-19 dlee created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
COLEPropertySetEnum::COLEPropertySetEnum(
|
|
ICiCOpenedDoc *Document )
|
|
: _cPropSets( 0 ),
|
|
_iPropSet( 0 ),
|
|
_fIsStorage( FALSE )
|
|
{
|
|
IPropertySetStorage * pPropertySetStorage;
|
|
HRESULT hr = Document->GetPropertySetEnum( &pPropertySetStorage );
|
|
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
if (FILTER_S_NO_PROPSETS != hr)
|
|
{
|
|
_fIsStorage = TRUE;
|
|
|
|
// Save away property set storage
|
|
|
|
_xPropSetStg.Set( pPropertySetStorage );
|
|
|
|
IEnumSTATPROPSETSTG *pPropSetEnum;
|
|
hr = _xPropSetStg->Enum( &pPropSetEnum );
|
|
|
|
if ( E_NOTIMPL == hr )
|
|
{
|
|
_fIsStorage = FALSE;
|
|
_xPropSetStg.Free();
|
|
}
|
|
else
|
|
{
|
|
CheckResult( hr, "PropSetStg->Enum() failed hr = 0x%x\n" );
|
|
_xPropSetEnum.Set( pPropSetEnum );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// don't raise just because it wasn't a docfile
|
|
|
|
if ( STG_E_FILEALREADYEXISTS != hr )
|
|
CheckResult( hr, "StgOpenStorage() failed hr = 0x%x\n" );
|
|
}
|
|
} //COLEPropertySetEnum
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: COLEPropertyEnum::COLEPropertyEnum, public
|
|
//
|
|
// Synopsis: Constructor for class that enumerates all properties on
|
|
// a docfile.
|
|
//
|
|
// Arguments: [Document] -- opened document
|
|
//
|
|
// History 95-Dec-19 dlee created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
COLEPropertyEnum::COLEPropertyEnum( ICiCOpenedDoc * Document )
|
|
: _pguidCurrent( 0 ),
|
|
_PropSetEnum( Document ),
|
|
_Codepage( CP_ACP ),
|
|
_cValues( 0 ),
|
|
_iCurrent( 0 ),
|
|
_fCustomOfficePropset( FALSE )
|
|
{
|
|
Document->AddRef( );
|
|
_xDocument.Set( Document );
|
|
_pguidCurrent = _PropSetEnum.Next();
|
|
|
|
END_CONSTRUCTION( COLEPropertyEnum );
|
|
} //COLEPropertyEnum
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: COLEPropertyEnum::GetPropertySetLocale, public
|
|
//
|
|
// Synopsis: Gets locale, if any.
|
|
//
|
|
// Arguments: [locale] -- locale, if any
|
|
//
|
|
// History 99-Mar-24 KrishnaN created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
HRESULT COLEPropertyEnum::GetPropertySetLocale(LCID & locale)
|
|
{
|
|
|
|
XInterface<IPropertyStorage> xPropStorage;
|
|
|
|
IPropertyStorage *pPropStg = 0;
|
|
HRESULT hr = _PropSetEnum.GetPSS()->Open( FMTID_UserDefinedProperties,
|
|
STGM_READ |
|
|
STGM_SHARE_EXCLUSIVE,
|
|
&pPropStg );
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
Win4Assert(0 == pPropStg);
|
|
ciDebugOut(( DEB_IERROR, "PropSetStg->Open() failed hr = 0x%x\n", hr ));
|
|
return hr;
|
|
}
|
|
|
|
Win4Assert( 0 != pPropStg );
|
|
|
|
xPropStorage.Set( pPropStg );
|
|
|
|
return ::GetPropertySetLocale(xPropStorage.GetPointer(), locale);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: COLEPropertyEnum::FillCache, private
|
|
//
|
|
// Synopsis: Loads the next property values for the current or next
|
|
// property set.
|
|
//
|
|
// History 95-Dec-19 dlee created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL COLEPropertyEnum::FillCache()
|
|
{
|
|
do
|
|
{
|
|
// all out of property sets?
|
|
|
|
if ( 0 == _pguidCurrent )
|
|
{
|
|
//
|
|
// If we are to filter the custom office propertyset, then do it
|
|
// after we've filtered all other properties.
|
|
//
|
|
if ( _fCustomOfficePropset )
|
|
{
|
|
_fCustomOfficePropset = FALSE;
|
|
_pguidCurrent = &FMTID_UserDefinedProperties;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
FreeCache();
|
|
|
|
//
|
|
// If this is the office property set, then we need to also try
|
|
// to filter the office CUSTOM property set.
|
|
//
|
|
if ( *_pguidCurrent == FMTID_SummaryInformation )
|
|
{
|
|
_fCustomOfficePropset = TRUE;
|
|
}
|
|
|
|
// Open the property storage if not yet open
|
|
|
|
if ( 0 == _xPropStorage.GetPointer() )
|
|
{
|
|
IPropertyStorage *pPropStg;
|
|
HRESULT hr = _PropSetEnum.GetPSS()->Open( *_pguidCurrent,
|
|
STGM_READ |
|
|
STGM_SHARE_EXCLUSIVE,
|
|
&pPropStg );
|
|
|
|
//
|
|
// The special second-section property set will return a
|
|
// special failure code if it doesn't exist. Check for this
|
|
// code and ignore the property set.
|
|
// OLE returns STG_E_FILENOTFOUND and the shell returns E_FAIL
|
|
//
|
|
|
|
if ( ( ( E_FAIL == hr ) || ( STG_E_FILENOTFOUND == hr ) ) &&
|
|
( FMTID_UserDefinedProperties == *_pguidCurrent ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CheckResult( hr, "PropSetStg->Open() failed hr = 0x%x\n" );
|
|
|
|
Win4Assert( 0 != pPropStg );
|
|
|
|
if ( 0 == pPropStg )
|
|
{
|
|
ciDebugOut(( DEB_WARN,
|
|
"IPropertySetStorage::Open( ) returned null property set and hr = 0x%x\n",
|
|
hr ));
|
|
|
|
_pguidCurrent = _PropSetEnum.Next();
|
|
continue;
|
|
}
|
|
|
|
_xPropStorage.Set( pPropStg );
|
|
|
|
//
|
|
// Look for codepage
|
|
//
|
|
|
|
PROPSPEC psCodepage = { PRSPEC_PROPID, PID_CODEPAGE };
|
|
PROPVARIANT varCodepage;
|
|
|
|
hr = _xPropStorage->ReadMultiple( 1, &psCodepage, &varCodepage );
|
|
|
|
if ( SUCCEEDED(hr) && VT_I2 == varCodepage.vt && varCodepage.iVal != CP_WINUNICODE )
|
|
_Codepage = (unsigned short) varCodepage.iVal;
|
|
else
|
|
_Codepage = CP_ACP;
|
|
|
|
|
|
Win4Assert( 0 == _xPropEnum.GetPointer() );
|
|
|
|
IEnumSTATPROPSTG *pPropEnum;
|
|
hr = pPropStg->Enum( &pPropEnum );
|
|
CheckResult( hr, "PropStg->Enum() failed hr = 0x%x\n" );
|
|
_xPropEnum.Set( pPropEnum );
|
|
}
|
|
|
|
HRESULT hr = _xPropEnum->Next( cMaxValuesCached, _aSPS, &_cValues );
|
|
CheckResult( hr, "PropEnum->Next failed sc = 0x%x\n" );
|
|
|
|
if ( _cValues > 0 )
|
|
{
|
|
for ( unsigned i = 0; i < _cValues; i++ )
|
|
{
|
|
if ( 0 != _aSPS[i].lpwstrName)
|
|
{
|
|
_aPropSpec[i].ulKind = PRSPEC_LPWSTR;
|
|
_aPropSpec[i].lpwstr = _aSPS[i].lpwstrName;
|
|
}
|
|
else
|
|
{
|
|
_aPropSpec[i].ulKind = PRSPEC_PROPID;
|
|
_aPropSpec[i].propid = _aSPS[i].propid;
|
|
}
|
|
}
|
|
|
|
hr = _xPropStorage->ReadMultiple( _cValues,
|
|
_aPropSpec,
|
|
_aPropVals );
|
|
|
|
CheckResult( hr, "ReadMultiple failed sc = 0x%x\n" );
|
|
|
|
//
|
|
// HACK #274: Translate the Ole summary information LPSTR in LPWSTR
|
|
// Makes these properties compatible with HTML filter
|
|
// equivalents.
|
|
//
|
|
|
|
if ( FMTID_SummaryInformation == *_pguidCurrent )
|
|
{
|
|
for ( i = 0; i < _cValues; i++ )
|
|
{
|
|
if ( ( VT_LPSTR == _aPropVals[i].Type() ) &&
|
|
( 0 != _aPropVals[i].GetLPSTR() ) )
|
|
{
|
|
// ciDebugOut(( DEB_ITRACE, "Converting \"%s\" to Unicode\n", _aPropVals[i].GetLPSTR() ));
|
|
|
|
unsigned cc = strlen( _aPropVals[i].GetLPSTR() ) + 1;
|
|
|
|
XGrowable<WCHAR> xwcsProp( cc + (cc * 10 / 100) ); // 10% fluff
|
|
|
|
unsigned ccT = 0;
|
|
|
|
while ( 0 == ccT )
|
|
{
|
|
ccT = MultiByteToWideChar( _Codepage,
|
|
0, // precomposed used of the codepage supports it
|
|
_aPropVals[i].GetLPSTR(),
|
|
cc,
|
|
xwcsProp.Get(),
|
|
xwcsProp.Count() );
|
|
|
|
if ( 0 == ccT )
|
|
{
|
|
if ( ERROR_INSUFFICIENT_BUFFER == GetLastError() )
|
|
{
|
|
unsigned ccNeeded = MultiByteToWideChar( _Codepage,
|
|
0, // precomposed used of the codepage supports it
|
|
_aPropVals[i].GetLPSTR(),
|
|
cc,
|
|
0,
|
|
0 );
|
|
Win4Assert( ccNeeded > 0 );
|
|
|
|
xwcsProp.SetSize( ccNeeded );
|
|
}
|
|
else
|
|
{
|
|
ciDebugOut(( DEB_ERROR, "Error %d converting %s to codepage 0x%x\n",
|
|
GetLastError(), _aPropVals[i].GetLPSTR(), _Codepage ));
|
|
|
|
ccT = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ccT != 0 )
|
|
_aPropVals[i].SetLPWSTR( xwcsProp.Get() );
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
// move on to the next property set
|
|
|
|
_xPropEnum.Acquire()->Release();
|
|
_xPropStorage.Acquire()->Release();
|
|
|
|
_pguidCurrent = _PropSetEnum.Next();
|
|
}
|
|
} while ( TRUE );
|
|
|
|
Win4Assert( !"never-never code path" );
|
|
return FALSE;
|
|
} //FillCache
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: COLEPropertyEnum::Next, public
|
|
//
|
|
// Synopsis: Moves to next property
|
|
//
|
|
// Arguments: [ps] -- Content index propspec returned here
|
|
//
|
|
// Returns: Pointer to property value. 0 if at end.
|
|
//
|
|
// History 95-Dec-19 dlee created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
CStorageVariant const * COLEPropertyEnum::Next(
|
|
CFullPropSpec & ps )
|
|
{
|
|
//
|
|
// If we have exhausted the cache, then try to fill it
|
|
//
|
|
|
|
if ( _cValues == _iCurrent && !FillCache() ) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// If the document is being requested, bail out
|
|
//
|
|
|
|
BOOL fInUse;
|
|
_xDocument->IsInUseByAnotherProcess( &fInUse );
|
|
|
|
if ( fInUse )
|
|
{
|
|
ciDebugOut(( DEB_ITRACE, "Oplock broken while filtering OLE properties\n" ));
|
|
QUIETTHROW( CException( STATUS_OPLOCK_BREAK_IN_PROGRESS ) );
|
|
}
|
|
|
|
//
|
|
// Set up the full property spec
|
|
//
|
|
ps.SetPropSet( *_pguidCurrent );
|
|
|
|
if ( PRSPEC_LPWSTR == _aPropSpec[_iCurrent].ulKind )
|
|
ps.SetProperty( _aPropSpec[_iCurrent].lpwstr );
|
|
else
|
|
ps.SetProperty( _aPropSpec[_iCurrent].propid );
|
|
|
|
_iCurrent++;
|
|
|
|
return( (CStorageVariant const *) &_aPropVals[_iCurrent-1] );
|
|
} //Next
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: COLEPropertyEnum::FreeCache, private
|
|
//
|
|
// Synopsis: Frees memory for the loaded properties and their specs
|
|
//
|
|
// History 95-Dec-19 dlee created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void COLEPropertyEnum::FreeCache()
|
|
{
|
|
for ( unsigned i = 0; i < _cValues; i++ )
|
|
{
|
|
PropVariantClear( (PROPVARIANT *) (void *) ( & _aPropVals[ i ] ) );
|
|
if ( PRSPEC_LPWSTR == _aPropSpec[i].ulKind )
|
|
CoTaskMemFree( _aSPS[i].lpwstrName );
|
|
}
|
|
|
|
_iCurrent = 0;
|
|
_cValues = 0;
|
|
} //FreeCache
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: GetPropertySetLocale
|
|
//
|
|
// Synopsis: Reads the locale, if any, from property storage
|
|
//
|
|
// History 99-Mar-24 KrishnaN created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
HRESULT GetPropertySetLocale(IPropertyStorage *pPropStorage, LCID & locale)
|
|
{
|
|
|
|
Win4Assert(0 != pPropStorage );
|
|
|
|
PROPSPEC ps;
|
|
PROPVARIANT prop;
|
|
|
|
ps.ulKind = PRSPEC_PROPID;
|
|
ps.propid = PID_LOCALE;
|
|
|
|
// Get the locale for properties
|
|
HRESULT hr = pPropStorage->ReadMultiple (1,
|
|
&ps,
|
|
&prop);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
if(prop.vt == VT_EMPTY)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
Win4Assert(prop.vt == VT_UI4);
|
|
locale = prop.ulVal;
|
|
// PropVariantClear(&prop);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|