EnumAll Sample
The EnumAll.cpp sample application puts all properties in all property sets of a storage file.
//+===================================================================
//
// To Build: cl /GX enumall.cpp
//
// The following code example dumps all the properties in all property
// sets of a storage file.
//
//+===================================================================
#define UNICODE
#define _UNICODE
#include <stdio.h>
#include <windows.h>
#pragma comment( lib, "ole32.lib" )
//+-------------------------------------------------------------------
//
// ConvertVarTypeToString
//
// Generate a string for a given PROPVARIANT variable type (VT).
// For the given vt, write the string to pwszType, which is a buffer
// of size cchType characters.
//
//+-------------------------------------------------------------------
void
ConvertVarTypeToString( VARTYPE vt, WCHAR *pwszType, ULONG cchType )
{
const WCHAR *pwszModifier;
// Ensure that the output string is terminated
// (wcsncpy does not guarantee termination)
pwszType[ cchType-1 ] = L'\0';
--cchType;
// Create a string using the basic type.
switch( vt & VT_TYPEMASK )
{
case VT_EMPTY:
wcsncpy_s( pwszType, cchType, L"VT_EMPTY", cchType );
break;
case VT_NULL:
wcsncpy_s( pwszType, cchType, L"VT_NULL", cchType );
break;
case VT_I2:
wcsncpy_s( pwszType, cchType, L"VT_I2", cchType );
break;
case VT_I4:
wcsncpy_s( pwszType, cchType, L"VT_I4", cchType );
break;
case VT_I8:
wcsncpy_s( pwszType, cchType, L"VT_I8", cchType );
break;
case VT_UI2:
wcsncpy_s( pwszType, cchType, L"VT_UI2", cchType );
break;
case VT_UI4:
wcsncpy_s( pwszType, cchType, L"VT_UI4", cchType );
break;
case VT_UI8:
wcsncpy_s( pwszType, cchType, L"VT_UI8", cchType );
break;
case VT_R4:
wcsncpy_s( pwszType, cchType, L"VT_R4", cchType );
break;
case VT_R8:
wcsncpy_s( pwszType, cchType, L"VT_R8", cchType );
break;
case VT_CY:
wcsncpy_s( pwszType, cchType, L"VT_CY", cchType );
break;
case VT_DATE:
wcsncpy_s( pwszType, cchType, L"VT_DATE", cchType );
break;
case VT_BSTR:
wcsncpy_s( pwszType, cchType, L"VT_BSTR", cchType );
break;
case VT_ERROR:
wcsncpy_s( pwszType, cchType, L"VT_ERROR", cchType );
break;
case VT_BOOL:
wcsncpy_s( pwszType, cchType, L"VT_BOOL", cchType );
break;
case VT_VARIANT:
wcsncpy_s( pwszType, cchType, L"VT_VARIANT", cchType );
break;
case VT_DECIMAL:
wcsncpy_s( pwszType, cchType, L"VT_DECIMAL", cchType );
break;
case VT_I1:
wcsncpy_s( pwszType, cchType, L"VT_I1", cchType );
break;
case VT_UI1:
wcsncpy_s( pwszType, cchType, L"VT_UI1", cchType );
break;
case VT_INT:
wcsncpy_s( pwszType, cchType, L"VT_INT", cchType );
break;
case VT_UINT:
wcsncpy_s( pwszType, cchType, L"VT_UINT", cchType );
break;
case VT_VOID:
wcsncpy_s( pwszType, cchType, L"VT_VOID", cchType );
break;
case VT_SAFEARRAY:
wcsncpy_s( pwszType, cchType, L"VT_SAFEARRAY", cchType );
break;
case VT_USERDEFINED:
wcsncpy_s( pwszType, cchType, L"VT_USERDEFINED", cchType );
break;
case VT_LPSTR:
wcsncpy_s( pwszType, cchType, L"VT_LPSTR", cchType );
break;
case VT_LPWSTR:
wcsncpy_s( pwszType, cchType, L"VT_LPWSTR", cchType );
break;
case VT_RECORD:
wcsncpy_s( pwszType, cchType, L"VT_RECORD", cchType );
break;
case VT_FILETIME:
wcsncpy_s( pwszType, cchType, L"VT_FILETIME", cchType );
break;
case VT_BLOB:
wcsncpy_s( pwszType, cchType, L"VT_BLOB", cchType );
break;
case VT_STREAM:
wcsncpy_s( pwszType, cchType, L"VT_STREAM", cchType );
break;
case VT_STORAGE:
wcsncpy_s( pwszType, cchType, L"VT_STORAGE", cchType );
break;
case VT_STREAMED_OBJECT:
wcsncpy_s( pwszType, cchType, L"VT_STREAMED_OBJECT", cchType );
break;
case VT_STORED_OBJECT:
wcsncpy_s( pwszType, cchType, L"VT_BLOB_OBJECT", cchType );
break;
case VT_CF:
wcsncpy_s( pwszType, cchType, L"VT_CF", cchType );
break;
case VT_CLSID:
wcsncpy_s( pwszType, cchType, L"VT_CLSID", cchType );
break;
default:
_snwprintf_s( pwszType, cchType, cchType, L"Unknown (%d)",
vt & VT_TYPEMASK );
break;
}
// Adjust cchType for the added characters.
cchType -= wcslen(pwszType);
// Add the type modifiers, if present.
if( vt & VT_VECTOR )
{
pwszModifier = L" | VT_VECTOR";
wcsncat_s( pwszType, cchType, pwszModifier, cchType );
cchType -= wcslen( pwszModifier );
}
if( vt & VT_ARRAY )
{
pwszModifier = L" | VT_ARRAY";
wcsncat_s( pwszType, cchType, pwszModifier, cchType );
cchType -= wcslen( pwszModifier );
}
if( vt & VT_RESERVED )
{
pwszModifier = L" | VT_RESERVED";
wcsncat_s( pwszType, cchType, pwszModifier, cchType );
cchType -= wcslen( pwszModifier );
}
}
//+-------------------------------------------------------------------
//
// ConvertValueToString
//
// Generate a string for the value in a given PROPVARIANT structure.
// The most common types are supported; that is, those that can be
// displayed with printf. For other types, only an ellipses (...)
// is displayed.
//
// The property to create a string from is in propvar, the resulting
// string is placed into pwszValue, which is a buffer with space for
// cchValue characters (including the string terminator).
//
//+-------------------------------------------------------------------
void
ConvertValueToString( const PROPVARIANT &propvar,
WCHAR *pwszValue,
ULONG cchValue )
{
// Ensure that the output string is terminated
pwszValue[ cchValue - 1 ] = L'\0';
--cchValue;
// Based on the type, put the value into pwszValue as a string.
switch( propvar.vt )
{
case VT_EMPTY:
wcsncpy_s( pwszValue, cchValue, L"", cchValue );
break;
case VT_NULL:
wcsncpy_s( pwszValue, cchValue, L"", cchValue );
break;
case VT_I2:
_snwprintf_s( pwszValue, cchValue, cchValue, L"%i", propvar.iVal );
break;
case VT_I4:
case VT_INT:
_snwprintf_s( pwszValue, cchValue, cchValue, L"%li", propvar.lVal );
break;
case VT_I8:
_snwprintf_s( pwszValue, cchValue, cchValue, L"%I64i", propvar.hVal );
break;
case VT_UI2:
_snwprintf_s( pwszValue, cchValue, cchValue, L"%u", propvar.uiVal );
break;
case VT_UI4:
case VT_UINT:
_snwprintf_s( pwszValue, cchValue, cchValue, L"%lu", propvar.ulVal );
break;
case VT_UI8:
_snwprintf_s( pwszValue, cchValue, cchValue, L"%I64u", propvar.uhVal );
break;
case VT_R4:
_snwprintf_s( pwszValue, cchValue, cchValue, L"%f", propvar.fltVal );
break;
case VT_R8:
_snwprintf_s( pwszValue, cchValue, cchValue, L"%lf", propvar.dblVal );
break;
case VT_BSTR:
_snwprintf_s( pwszValue, cchValue, cchValue, L"\"%s\"",
propvar.bstrVal );
break;
case VT_ERROR:
_snwprintf_s( pwszValue, cchValue, cchValue, L"0x%08X", propvar.scode );
break;
case VT_BOOL:
_snwprintf_s( pwszValue, cchValue, cchValue, L"%s",
VARIANT_TRUE == propvar.boolVal ? L"True" : L"False" );
break;
case VT_I1:
_snwprintf_s( pwszValue, cchValue, cchValue, L"%i", propvar.cVal );
break;
case VT_UI1:
_snwprintf_s( pwszValue, cchValue, cchValue, L"%u", propvar.bVal );
break;
case VT_VOID:
wcsncpy_s( pwszValue, cchValue, L"", cchValue );
break;
case VT_LPSTR:
if( 0 > _snwprintf_s( pwszValue, cchValue, cchValue,
L"\"%hs\"", propvar.pszVal ))
// String is too large for pwszValue
wcsncpy_s( pwszValue, cchValue, L"...", cchValue );
break;
case VT_LPWSTR:
if( 0 > _snwprintf_s( pwszValue, cchValue, cchValue,
L"\"%s\"", propvar.pwszVal ))
// String is too large for pwszValue
wcsncpy_s( pwszValue, cchValue, L"...", cchValue );
break;
case VT_FILETIME:
_snwprintf_s( pwszValue, cchValue, cchValue, L"%08x:%08x",
propvar.filetime.dwHighDateTime,
propvar.filetime.dwLowDateTime );
break;
case VT_CLSID:
pwszValue[0] = L'\0';
StringFromGUID2( *propvar.puuid, pwszValue, cchValue );
break;
default:
wcsncpy_s( pwszValue, cchValue, L"...", cchValue );
break;
}
}
//+-------------------------------------------------------------------
//
// DisplayProperty
//
// Dump the ID, name, type, and value of a property.
//
//+-------------------------------------------------------------------
void
DisplayProperty( const PROPVARIANT &propvar,
const STATPROPSTG &statpropstg )
{
WCHAR wsz[ MAX_PATH + 1 ];
ConvertVarTypeToString( statpropstg.vt, wsz,
sizeof(wsz)/sizeof(wsz[0]) );
wprintf( L" -------------------------------------------------\n"
L" PropID = %-5d VarType = %-23s",
statpropstg.propid, wsz );
if( NULL != statpropstg.lpwstrName )
wprintf( L" Name = %s", statpropstg.lpwstrName );
ConvertValueToString( propvar, wsz, sizeof(wsz)/sizeof(wsz[0]) );
wprintf( L"\n Value = %s\n", wsz );
}
//+-------------------------------------------------------------------
//
// DisplayPropertySet
//
// Dump all the properties into a given property set.
//
//+-------------------------------------------------------------------
void
DisplayPropertySet( FMTID fmtid,
const WCHAR *pwszStorageName,
IPropertyStorage *pPropStg )
{
IEnumSTATPROPSTG *penum = NULL;
HRESULT hr = S_OK;
STATPROPSTG statpropstg;
PROPVARIANT propvar;
PROPSPEC propspec;
PROPID propid;
WCHAR *pwszFriendlyName = NULL;
// This string will hold a string-formatted FMTID. It must
// be 38 characters, plus the terminator character.
// For best practice, create a moderately longer string.
WCHAR wszFMTID[ 64 ] = { L"" };
PropVariantInit( &propvar );
memset( &statpropstg, 0, sizeof(statpropstg) );
try
{
// Display the ID of the property set.
StringFromGUID2( fmtid,
wszFMTID,
sizeof(wszFMTID)/sizeof(wszFMTID[0]) );
wprintf( L"\n Property Set %s\n", wszFMTID );
// If this is a common property set, display.
if( FMTID_SummaryInformation == fmtid )
wprintf( L" (SummaryInformation property set)\n" );
else if( FMTID_DocSummaryInformation == fmtid )
wprintf( L" (DocumentSummaryInformation property set)\n" );
else if( FMTID_UserDefinedProperties == fmtid )
wprintf( L" (UserDefined property set)\n" );
// Also display the name of the storage that contains
// this property set.
wprintf( L" in \"%s\":\n", pwszStorageName );
// If this property set has a friendly name, display it now.
// (Property names are stored in the special dictionary
// property - the name of the property set is indicated by
// naming the dictionary property itself.)
propid = PID_DICTIONARY;
pwszFriendlyName = NULL;
hr = pPropStg->ReadPropertyNames( 1, &propid,
&pwszFriendlyName );
if( S_OK == hr )
{
wprintf( L" (Friendly name is \"%s\")\n\n",
pwszFriendlyName );
CoTaskMemFree( pwszFriendlyName );
pwszFriendlyName = NULL;
}
else
wprintf( L"\n" );
// Get a property enumerator.
hr = pPropStg->Enum( &penum );
if( FAILED(hr) )
throw L"Failed IPropertyStorage::Enum";
// Get the first property in the enumeration.
hr = penum->Next( 1, &statpropstg, NULL );
// Loop through and display each property. The 'Next'
// call above, and at the bottom of the while loop,
// will return S_OK if it returns another property,
// S_FALSE if there are no more properties,
// and anything else is an error.
while( S_OK == hr )
{
// Read the property out of the property set
PropVariantInit( &propvar );
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = statpropstg.propid;
hr = pPropStg->ReadMultiple( 1, &propspec, &propvar );
if( FAILED(hr) )
throw L"Failed IPropertyStorage::ReadMultiple";
// Display the property value, type, and so on.
DisplayProperty( propvar, statpropstg );
// Free buffers allocated during the read, and
// by the enumerator.
PropVariantClear( &propvar );
CoTaskMemFree( statpropstg.lpwstrName );
statpropstg.lpwstrName = NULL;
// Move to the next property in the enumeration
hr = penum->Next( 1, &statpropstg, NULL );
}
if( FAILED(hr) ) throw L"Failed IEnumSTATPROPSTG::Next";
}
catch( LPCWSTR pwszErrorMessage )
{
wprintf( L"Error in DumpPropertySet: %s (hr = %08x)\n",
pwszErrorMessage, hr );
}
if( NULL != penum )
penum->Release();
if( NULL != statpropstg.lpwstrName )
CoTaskMemFree( statpropstg.lpwstrName );
PropVariantClear( &propvar );
}
//+-------------------------------------------------------------------
//
// DisplayPropertySetsInStorage
//
// Dump the property sets in the top level of a given storage.
//
//+-------------------------------------------------------------------
void
DisplayPropertySetsInStorage( const WCHAR *pwszStorageName,
IPropertySetStorage *pPropSetStg )
{
IEnumSTATPROPSETSTG *penum = NULL;
HRESULT hr = S_OK;
IPropertyStorage *pPropStg = NULL;
STATPROPSETSTG statpropsetstg;
try
{
// Get a property-set enumerator, which only enumerates
// the property sets at this level of the storage, not
// its child objects.
hr = pPropSetStg->Enum( &penum );
if( FAILED(hr) )
throw L"failed IPropertySetStorage::Enum";
// Get the first property set in the enumeration.
// (The field used to open the property set is
// statpropsetstg.fmtid.
memset( &statpropsetstg, 0, sizeof(statpropsetstg) );
hr = penum->Next( 1, &statpropsetstg, NULL );
// Loop through all the property sets.
while( S_OK == hr )
{
// Open the property set.
hr = pPropSetStg->Open( statpropsetstg.fmtid,
STGM_READ | STGM_SHARE_EXCLUSIVE,
&pPropStg );
if( FAILED(hr) )
throw L"failed IPropertySetStorage::Open";
// Display the properties in the property set.
DisplayPropertySet( statpropsetstg.fmtid,
pwszStorageName,
pPropStg );
pPropStg->Release();
pPropStg = NULL;
// Get the FMTID of the next property set in the
// enumeration.
hr = penum->Next( 1, &statpropsetstg, NULL );
}
if( FAILED(hr) ) throw L"Failed IEnumSTATPROPSETSTG::Next";
// Special-case handling for the UserDefined property set:
// This property set actually lives inside the well-known
// DocumentSummaryInformation property set. It is the only
// property set which is allowed to live inside another
// (and exists for legacy compatibility). It does not get
// included in a normal enumeration, so verify that it is
// done explicitly. Look for it when the end of the
// enumerator is reached.
hr = pPropSetStg->Open( FMTID_UserDefinedProperties,
STGM_READ | STGM_SHARE_EXCLUSIVE,
&pPropStg );
if( SUCCEEDED(hr) )
{
DisplayPropertySet( FMTID_UserDefinedProperties,
pwszStorageName,
pPropStg );
pPropStg->Release();
pPropStg = NULL;
}
}
catch( LPCWSTR pwszErrorMessage )
{
wprintf( L"Error in DumpPropertySetsInStorage:
%s (hr = %08x)\n",
pwszErrorMessage, hr );
}
if( NULL != pPropStg )
pPropStg->Release();
if( NULL != penum )
penum->Release();
}
//+-------------------------------------------------------------------
//
// DisplayStorageTree
//
// Dump all the property sets in the given storage and recursively in
// all its child objects.
//
//+-------------------------------------------------------------------
void
DisplayStorageTree( const WCHAR *pwszStorageName, IStorage *pStg )
{
IPropertySetStorage *pPropSetStg = NULL;
IStorage *pStgChild = NULL;
WCHAR *pwszChildStorageName = NULL;
IEnumSTATSTG *penum = NULL;
HRESULT hr = S_OK;
STATSTG statstg;
memset( &statstg, 0, sizeof(statstg) );
try
{
// Dump the property sets at this storage level
hr = pStg->QueryInterface( IID_IPropertySetStorage,
reinterpret_cast<void**>(&pPropSetStg) );
if( FAILED(hr) )
throw
L"Failed IStorage::QueryInterface(IID_IPropertySetStorage)";
DisplayPropertySetsInStorage( pwszStorageName, pPropSetStg );
// Get an enumerator for this storage.
hr = pStg->EnumElements( NULL, NULL, NULL, &penum );
if( FAILED(hr) ) throw L"failed IStorage::Enum";
// Get the name of the first element (stream/storage)
// in the enumeration. As usual, 'Next' will return
// S_OK if it returns an element of the enumerator,
// S_FALSE if there are no more elements, and an
// error otherwise.
hr = penum->Next( 1, &statstg, 0 );
// Loop through all the child objects of this storage.
while( S_OK == hr )
{
// Verify that this is a storage that is not a property
// set, because the property sets are displayed above).
// If the first character of its name is the '\005'
// reserved character, it is a stream /storage property
// set.
if( STGTY_STORAGE == statstg.type
&&
L'\005' != statstg.pwcsName[0] )
{
// Indicates normal storage, not a propset.
// Open the storage.
ULONG cchChildStorageName;
hr = pStg->OpenStorage( statstg.pwcsName,
NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE,
NULL, 0,
&pStgChild );
if( FAILED(hr) )
throw L"failed IStorage::OpenStorage";
// Compose a name of the form
// "Storage\ChildStorage\..." for display purposes.
// First, allocate it.
cchChildStorageName = wcslen(pwszStorageName)
+ wcslen(statstg.pwcsName)
+ 2 // For two "\" chars
+ 1; // For string terminator
pwszChildStorageName =
new WCHAR[ cchChildStorageName ];
if( NULL == pwszChildStorageName )
{
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
throw L"couldn't allocate memory";
}
// Terminate the name.
pwszChildStorageName[ cchChildStorageName-1 ] = L'\0';
--cchChildStorageName;
// Build the name.
wcsncpy_s( pwszChildStorageName, cchChildStorageName,
pwszStorageName, cchChildStorageName );
cchChildStorageName -= wcslen(pwszStorageName);
wcsncat_s( pwszChildStorageName, cchChildStorageName,
L"\\", cchChildStorageName );
cchChildStorageName -= 2;
wcsncat_s( pwszChildStorageName, cchChildStorageName,
statstg.pwcsName, cchChildStorageName );
// Dump all property sets under this child storage.
DisplayStorageTree( pwszChildStorageName, pStgChild );
pStgChild->Release();
pStgChild = NULL;
delete[] pwszChildStorageName;
pwszChildStorageName = NULL;
}
// Move to the next element in the enumeration of
// this storage.
CoTaskMemFree( statstg.pwcsName );
statstg.pwcsName = NULL;
hr = penum->Next( 1, &statstg, 0 );
}
if( FAILED(hr) ) throw L"failed IEnumSTATSTG::Next";
}
catch( LPCWSTR pwszErrorMessage )
{
wprintf( L"Error in DumpStorageTree: %s (hr = %08x)\n",
pwszErrorMessage, hr );
}
// Cleanup before returning.
if( NULL != statstg.pwcsName )
CoTaskMemFree( statstg.pwcsName );
if( NULL != pStgChild )
pStgChild->Release();
if( NULL != pStg )
pStg->Release();
if( NULL != penum )
penum->Release();
if( NULL != pwszChildStorageName )
delete[] pwszChildStorageName;
}
//+-------------------------------------------------------------------
//
// wmain
//
// Dump all the property sets in a file which is a storage.
//
//+-------------------------------------------------------------------
extern "C" void wmain( int cArgs, WCHAR *rgwszArgs[] )
{
HRESULT hr = S_OK;
IStorage *pStg = NULL;
// Display usage information if necessary.
if( 1 == cArgs
||
0 == wcscmp( L"-?", rgwszArgs[1] )
||
0 == wcscmp( L"/?", rgwszArgs[1] ))
{
printf( "\n"
"Purpose: Enumerate all properties in all\n"
" property sets for a storage file\n"
"Usage: PropDump <filename>\n"
"E.g.: PropDump word.doc\n"
"\n" );
exit(0);
}
// Open the root storage.
hr = StgOpenStorageEx( rgwszArgs[1],
STGM_READ | STGM_SHARE_DENY_WRITE,
STGFMT_ANY,
0,
NULL,
NULL,
IID_IStorage,
reinterpret_cast<void**>(&pStg) );
// Dump all the properties in all the property sets within this
// storage.
if( FAILED(hr) )
{
wprintf( L"Error: couldn't open storage \"%s\"
(hr = %08x)\n",
rgwszArgs[1], hr );
}
else
{
printf( "\nDisplaying all property sets ...\n" );
DisplayStorageTree( rgwszArgs[1], pStg );
pStg->Release();
}
}