Windows的复合文件技术
今天初步学习了复合文件的一些知识,对于该知识点,由于涉及COM的一些原理,暂时还不理解,这个文档就是将暂时了解的一些内容,组织成一篇学习笔记,供以后参考.
在Microsoft的Word,Excel等应用程序中,保存文件的格式是用复合结构来保存的.复合结构不像一般的文件存储方式(根目录、子目录、文件形式的结构),而是采用根存储(IStorage类型),子存储(IStorage类型),流(IStream)结构的形式来存储文件.
复合文件中的流,是保存数据的空间,存储单位是512字节.
不同进程,不同线程可以访问复合文件的不同部分而不受干扰.这在不同于普通文件的特点之处.
复合文件提供了增量访问的能力,在一个文件中插入某些字节不需要对整个文件进行访问.
在VC6中有一工具提供了查看复合文件的功能,是Doc File View工具.可以用它来查看复合文件的结构内容.
操作复合文件的函数,分为三种类型,WinAPI函数,存储IStorage接口函数,流IStream接口函数.
WINAPI函数:
StgCreateDocFile() 建立复合文件,得到根存储对象
StgOpenStorage() 打开复合文件,得到根存储对象
StgIsStorageFile() 判断文件是否复合文件
IStorage函数
CreateStorage() 在当前存储中建立新存储,得到子存储对象
CreateStream() 在当前存储中建立流,得到流对象
OpenStorage() 打开子存储,得到子存储对象
OpenStream() 打开流,得到流对象
CopyTo() MoveElementTo() DestoryElement() RenameElement() EnumElements() SetElementTimes() SetClass() Stat() Release() 等等
IStream函数
Read() Write() Seek() SetSize() CopyTo() Stat() Clone() Release()
关于这些函数有很好的教程:http://www.vckbase.com/document/viewdoc/?id=1483
MSDN文章上面有一代码,描述了如何具体的创建和设置Storage File的方法,很好的一个例子,值得学习.
例子如下:
在Microsoft的Word,Excel等应用程序中,保存文件的格式是用复合结构来保存的.复合结构不像一般的文件存储方式(根目录、子目录、文件形式的结构),而是采用根存储(IStorage类型),子存储(IStorage类型),流(IStream)结构的形式来存储文件.
复合文件中的流,是保存数据的空间,存储单位是512字节.
不同进程,不同线程可以访问复合文件的不同部分而不受干扰.这在不同于普通文件的特点之处.
复合文件提供了增量访问的能力,在一个文件中插入某些字节不需要对整个文件进行访问.
在VC6中有一工具提供了查看复合文件的功能,是Doc File View工具.可以用它来查看复合文件的结构内容.
操作复合文件的函数,分为三种类型,WinAPI函数,存储IStorage接口函数,流IStream接口函数.
WINAPI函数:
StgCreateDocFile() 建立复合文件,得到根存储对象
StgOpenStorage() 打开复合文件,得到根存储对象
StgIsStorageFile() 判断文件是否复合文件
IStorage函数
CreateStorage() 在当前存储中建立新存储,得到子存储对象
CreateStream() 在当前存储中建立流,得到流对象
OpenStorage() 打开子存储,得到子存储对象
OpenStream() 打开流,得到流对象
CopyTo() MoveElementTo() DestoryElement() RenameElement() EnumElements() SetElementTimes() SetClass() Stat() Release() 等等
IStream函数
Read() Write() Seek() SetSize() CopyTo() Stat() Clone() Release()
关于这些函数有很好的教程:http://www.vckbase.com/document/viewdoc/?id=1483
MSDN文章上面有一代码,描述了如何具体的创建和设置Storage File的方法,很好的一个例子,值得学习.
例子如下:
1/*The EnumAll.cpp sample program dumps all the properties in all property sets of a storage file.*/
2
3//+============================================================================
4//
5// To Build: cl /GX enumall.cpp
6//
7// This sample is a program which dumps all the properties in all property
8// sets of a storage file.
9//
10//+============================================================================
11
12
13#define UNICODE
14#define _UNICODE
15
16#include <stdio.h>
17#include <windows.h>
18
19#pragma comment( lib, "ole32.lib" )
20
21//+----------------------------------------------------------------------------
22//
23// ConvertVarTypeToString
24//
25// Generate a string for a given PROPVARIANT variable type (VT).
26// For the given vt, write the string to pwszType, which is a buffer of
27// size cchType characters.
28//
29//+----------------------------------------------------------------------------
30
31void
32ConvertVarTypeToString( VARTYPE vt, WCHAR *pwszType, ULONG cchType )
33{
34 const WCHAR *pwszModifier;
35
36 // Make sure the output string will be terminated
37 // (wcsncpy doesn't guarantee termination)
38
39 pwszType[ cchType-1 ] = L'\0';
40 --cchType;
41
42 // Stringize the basic type
43
44 switch( vt & VT_TYPEMASK )
45 {
46 case VT_EMPTY:
47 wcsncpy( pwszType, L"VT_EMPTY", cchType );
48 break;
49 case VT_NULL:
50 wcsncpy( pwszType, L"VT_NULL", cchType );
51 break;
52 case VT_I2:
53 wcsncpy( pwszType, L"VT_I2", cchType );
54 break;
55 case VT_I4:
56 wcsncpy( pwszType, L"VT_I4", cchType );
57 break;
58 case VT_I8:
59 wcsncpy( pwszType, L"VT_I8", cchType );
60 break;
61 case VT_UI2:
62 wcsncpy( pwszType, L"VT_UI2", cchType );
63 break;
64 case VT_UI4:
65 wcsncpy( pwszType, L"VT_UI4", cchType );
66 break;
67 case VT_UI8:
68 wcsncpy( pwszType, L"VT_UI8", cchType );
69 break;
70 case VT_R4:
71 wcsncpy( pwszType, L"VT_R4", cchType );
72 break;
73 case VT_R8:
74 wcsncpy( pwszType, L"VT_R8", cchType );
75 break;
76 case VT_CY:
77 wcsncpy( pwszType, L"VT_CY", cchType );
78 break;
79 case VT_DATE:
80 wcsncpy( pwszType, L"VT_DATE", cchType );
81 break;
82 case VT_BSTR:
83 wcsncpy( pwszType, L"VT_BSTR", cchType );
84 break;
85 case VT_ERROR:
86 wcsncpy( pwszType, L"VT_ERROR", cchType );
87 break;
88 case VT_BOOL:
89 wcsncpy( pwszType, L"VT_BOOL", cchType );
90 break;
91 case VT_VARIANT:
92 wcsncpy( pwszType, L"VT_VARIANT", cchType );
93 break;
94 case VT_DECIMAL:
95 wcsncpy( pwszType, L"VT_DECIMAL", cchType );
96 break;
97 case VT_I1:
98 wcsncpy( pwszType, L"VT_I1", cchType );
99 break;
100 case VT_UI1:
101 wcsncpy( pwszType, L"VT_UI1", cchType );
102 break;
103 case VT_INT:
104 wcsncpy( pwszType, L"VT_INT", cchType );
105 break;
106 case VT_UINT:
107 wcsncpy( pwszType, L"VT_UINT", cchType );
108 break;
109 case VT_VOID:
110 wcsncpy( pwszType, L"VT_VOID", cchType );
111 break;
112 case VT_SAFEARRAY:
113 wcsncpy( pwszType, L"VT_SAFEARRAY", cchType );
114 break;
115 case VT_USERDEFINED:
116 wcsncpy( pwszType, L"VT_USERDEFINED", cchType );
117 break;
118 case VT_LPSTR:
119 wcsncpy( pwszType, L"VT_LPSTR", cchType );
120 break;
121 case VT_LPWSTR:
122 wcsncpy( pwszType, L"VT_LPWSTR", cchType );
123 break;
124 case VT_RECORD:
125 wcsncpy( pwszType, L"VT_RECORD", cchType );
126 break;
127 case VT_FILETIME:
128 wcsncpy( pwszType, L"VT_FILETIME", cchType );
129 break;
130 case VT_BLOB:
131 wcsncpy( pwszType, L"VT_BLOB", cchType );
132 break;
133 case VT_STREAM:
134 wcsncpy( pwszType, L"VT_STREAM", cchType );
135 break;
136 case VT_STORAGE:
137 wcsncpy( pwszType, L"VT_STORAGE", cchType );
138 break;
139 case VT_STREAMED_OBJECT:
140 wcsncpy( pwszType, L"VT_STREAMED_OBJECT", cchType );
141 break;
142 case VT_STORED_OBJECT:
143 wcsncpy( pwszType, L"VT_BLOB_OBJECT", cchType );
144 break;
145 case VT_CF:
146 wcsncpy( pwszType, L"VT_CF", cchType );
147 break;
148 case VT_CLSID:
149 wcsncpy( pwszType, L"VT_CLSID", cchType );
150 break;
151 default:
152 _snwprintf( pwszType, cchType, L"Unknown (%d)", vt & VT_TYPEMASK );
153 break;
154 }
155
156 // Adjust cchType for the characters we just added
157
158 cchType -= wcslen(pwszType);
159
160 // Add the type modifiers if present
161
162 if( vt & VT_VECTOR )
163 {
164 pwszModifier = L" | VT_VECTOR";
165 wcsncat( pwszType, pwszModifier, cchType );
166 cchType -= wcslen( pwszModifier );
167 }
168
169 if( vt & VT_ARRAY )
170 {
171 pwszModifier = L" | VT_ARRAY";
172 wcsncat( pwszType, pwszModifier, cchType );
173 cchType -= wcslen( pwszModifier );
174 }
175
176 if( vt & VT_RESERVED )
177 {
178 pwszModifier = L" | VT_RESERVED";
179 wcsncat( pwszType, pwszModifier, cchType );
180 cchType -= wcslen( pwszModifier );
181 }
182
183}
184
185
186//+----------------------------------------------------------------------------
187//
188// ConvertValueToString
189//
190// Generate a string for the value in a given PROPVARIANT structure.
191// The most common types are supported (that is, those that can be displayed
192// with printf). For other types, only an ellipses "" is displayed.
193//
194// The property to string-ize is in propvar, the resulting string goes
195// into pwszValue, which is a buffer with room for cchValue characters
196// (including the string terminator).
197//
198//+----------------------------------------------------------------------------
199
200void
201ConvertValueToString( const PROPVARIANT &propvar,
202 WCHAR *pwszValue,
203 ULONG cchValue )
204{
205 // Make sure the output string will be terminated
206
207 pwszValue[ cchValue - 1 ] = L'\0';
208 --cchValue;
209
210 // Based on the type, put the value into pwszValue as a string.
211
212 switch( propvar.vt )
213 {
214 case VT_EMPTY:
215 wcsncpy( pwszValue, L"", cchValue );
216 break;
217 case VT_NULL:
218 wcsncpy( pwszValue, L"", cchValue );
219 break;
220 case VT_I2:
221 _snwprintf( pwszValue, cchValue, L"%i", propvar.iVal );
222 break;
223 case VT_I4:
224 case VT_INT:
225 _snwprintf( pwszValue, cchValue, L"%li", propvar.lVal );
226 break;
227 case VT_I8:
228 _snwprintf( pwszValue, cchValue, L"%I64i", propvar.hVal );
229 break;
230 case VT_UI2:
231 _snwprintf ( pwszValue, cchValue, L"%u", propvar.uiVal );
232 break;
233 case VT_UI4:
234 case VT_UINT:
235 _snwprintf ( pwszValue, cchValue, L"%lu", propvar.ulVal );
236 break;
237 case VT_UI8:
238 _snwprintf ( pwszValue, cchValue, L"%I64u", propvar.uhVal );
239 break;
240 case VT_R4:
241 _snwprintf ( pwszValue, cchValue, L"%f", propvar.fltVal );
242 break;
243 case VT_R8:
244 _snwprintf ( pwszValue, cchValue, L"%lf", propvar.dblVal );
245 break;
246 case VT_BSTR:
247 _snwprintf ( pwszValue, cchValue, L"\"%s\"", propvar.bstrVal );
248 break;
249 case VT_ERROR:
250 _snwprintf ( pwszValue, cchValue, L"0x%08X", propvar.scode );
251 break;
252 case VT_BOOL:
253 _snwprintf ( pwszValue, cchValue, L"%s",
254 VARIANT_TRUE == propvar.boolVal ? L"True" : L"False" );
255 break;
256 case VT_I1:
257 _snwprintf ( pwszValue, cchValue, L"%i", propvar.cVal );
258 break;
259 case VT_UI1:
260 _snwprintf ( pwszValue, cchValue, L"%u", propvar.bVal );
261 break;
262 case VT_VOID:
263 wcsncpy( pwszValue, L"", cchValue );
264 break;
265 case VT_LPSTR:
266 if( 0 >_snwprintf ( pwszValue, cchValue, L"\"%hs\"", propvar.pszVal ))
267 // String is too big for pwszValue
268 wcsncpy( pwszValue, L"", cchValue );
269 break;
270 case VT_LPWSTR:
271 if( 0 > _snwprintf ( pwszValue, cchValue, L"\"%s\"", propvar.pwszVal ))
272 // String is too big for pwszValue
273 wcsncpy( pwszValue, L"", cchValue );
274 break;
275 case VT_FILETIME:
276 _snwprintf ( pwszValue, cchValue, L"%08x:%08x",
277 propvar.filetime.dwHighDateTime,
278 propvar.filetime.dwLowDateTime );
279 break;
280 case VT_CLSID:
281 pwszValue[0] = L'\0';
282 StringFromGUID2( *propvar.puuid, pwszValue, cchValue );
283 break;
284 default:
285 wcsncpy( pwszValue, L"", cchValue );
286 break;
287 }
288
289}
290
291
292//+----------------------------------------------------------------------------
293//
294// DisplayProperty
295//
296// Dump the ID, name, type, and value of a property.
297//
298//+----------------------------------------------------------------------------
299
300void
301DisplayProperty( const PROPVARIANT &propvar, const STATPROPSTG &statpropstg )
302{
303 WCHAR wsz[ MAX_PATH + 1 ];
304
305 ConvertVarTypeToString( statpropstg.vt, wsz, sizeof(wsz)/sizeof(wsz[0]) );
306
307 wprintf( L" ----------------------------------------------------\n"
308 L" PropID = %-5d VarType = %-23s",
309 statpropstg.propid, wsz );
310
311 if( NULL != statpropstg.lpwstrName )
312 wprintf( L" Name = %s", statpropstg.lpwstrName );
313
314 ConvertValueToString( propvar, wsz, sizeof(wsz)/sizeof(wsz[0]) );
315
316 wprintf( L"\n Value = %s\n", wsz );
317
318}
319
320
321//+----------------------------------------------------------------------------
322//
323// DisplayPropertySet
324//
325// Dump all the properties into a given property set.
326//
327//+----------------------------------------------------------------------------
328
329void
330DisplayPropertySet( FMTID fmtid,
331 const WCHAR *pwszStorageName,
332 IPropertyStorage *pPropStg )
333{
334 IEnumSTATPROPSTG *penum = NULL;
335 HRESULT hr = S_OK;
336 STATPROPSTG statpropstg;
337 PROPVARIANT propvar;
338 PROPSPEC propspec;
339 PROPID propid;
340 WCHAR *pwszFriendlyName = NULL;
341
342 // This string will hold a string-ized FMTID. It needs
343 // to be 38 characters, plus the terminator
344 // character. But just to be safe we'll make
345 // it a little bigger.
346 WCHAR wszFMTID[ 64 ] = { L"" };
347
348 PropVariantInit( &propvar );
349 memset( &statpropstg, 0, sizeof(statpropstg) );
350
351 try
352 {
353 // Display the ID of the property set
354
355 StringFromGUID2( fmtid,
356 wszFMTID,
357 sizeof(wszFMTID)/sizeof(wszFMTID[0]) );
358 wprintf( L"\n Property Set %s\n", wszFMTID );
359
360 // If this is one of common property sets, show which one.
361
362 if( FMTID_SummaryInformation == fmtid )
363 wprintf( L" (SummaryInformation property set)\n" );
364 else if( FMTID_DocSummaryInformation == fmtid )
365 wprintf( L" (DocumentSummaryInformation property set)\n" );
366 else if( FMTID_UserDefinedProperties == fmtid )
367 wprintf( L" (UserDefined property set)\n" );
368
369 // Also display the name of the storage that contains
370 // this property set
371
372 wprintf( L" in \"%s\":\n", pwszStorageName );
373
374 // If this property set has a friendly name, display it now.
375 // (Property names are stored in the special dictionary
376 // property - the name of the property set is indicated by naming
377 // the dictionary property itself.)
378
379 propid = PID_DICTIONARY;
380 pwszFriendlyName = NULL;
381
382 hr = pPropStg->ReadPropertyNames( 1, &propid, &pwszFriendlyName );
383 if( S_OK == hr )
384 {
385 wprintf( L" (Friendly name is \"%s\")\n\n", pwszFriendlyName );
386 CoTaskMemFree( pwszFriendlyName );
387 pwszFriendlyName = NULL;
388 }
389 else
390 wprintf( L"\n" );
391
392 // Get a property enumerator
393
394 hr = pPropStg->Enum( &penum );
395 if( FAILED(hr) ) throw L"Failed IPropertyStorage::Enum";
396
397 // Get the first property in the enumeration
398
399 hr = penum->Next( 1, &statpropstg, NULL );
400
401 // Loop through and display each property. The ‘Next'
402 // call above (and at the bottom of the while loop)
403 // will return S_OK if it returns another property,
404 // S_FALSE if there are no more properties,
405 // and anything else is an error.
406
407 while( S_OK == hr )
408 {
409
410 // Read the property out of the property set
411
412 PropVariantInit( &propvar );
413 propspec.ulKind = PRSPEC_PROPID;
414 propspec.propid = statpropstg.propid;
415
416 hr = pPropStg->ReadMultiple( 1, &propspec, &propvar );
417 if( FAILED(hr) ) throw L"Failed IPropertyStorage::ReadMultiple";
418
419 // Display the property value, type, etc.
420
421 DisplayProperty( propvar, statpropstg );
422
423 // Free buffers that were allocated during the read and
424 // by the enumerator.
425
426 PropVariantClear( &propvar );
427 CoTaskMemFree( statpropstg.lpwstrName );
428 statpropstg.lpwstrName = NULL;
429
430 // Move to the next property in the enumeration
431
432 hr = penum->Next( 1, &statpropstg, NULL );
433 }
434 if( FAILED(hr) ) throw L"Failed IEnumSTATPROPSTG::Next";
435 }
436 catch( LPCWSTR pwszErrorMessage )
437 {
438 wprintf( L"Error in DumpPropertySet: %s (hr = %08x)\n",
439 pwszErrorMessage, hr );
440 }
441
442 if( NULL != penum )
443 penum->Release();
444
445 if( NULL != statpropstg.lpwstrName )
446 CoTaskMemFree( statpropstg.lpwstrName );
447
448 PropVariantClear( &propvar );
449}
450
451
452//+----------------------------------------------------------------------------
453//
454// DisplayPropertySetsInStorage
455//
456// Dump the property sets in the top level of a given storage.
457//
458//+----------------------------------------------------------------------------
459
460void
461DisplayPropertySetsInStorage( const WCHAR *pwszStorageName,
462 IPropertySetStorage *pPropSetStg )
463{
464 IEnumSTATPROPSETSTG *penum = NULL;
465 HRESULT hr = S_OK;
466 IPropertyStorage *pPropStg = NULL;
467 STATPROPSETSTG statpropsetstg;
468
469 try
470 {
471 // Get a property-set enumerator (which only enumerates the property
472 // sets at this level of the storage, not its children).
473
474 hr = pPropSetStg->Enum( &penum );
475 if( FAILED(hr) ) throw L"failed IPropertySetStorage::Enum";
476
477 // Get the first property set in the enumeration.
478 // (The field we're interested in is
479 // statpropsetstg.fmtid, so that we can open the
480 // property set.)
481
482 memset( &statpropsetstg, 0, sizeof(statpropsetstg) );
483 hr = penum->Next( 1, &statpropsetstg, NULL );
484
485 // Loop through all the property sets
486
487 while( S_OK == hr )
488 {
489 // Open the property set
490
491 hr = pPropSetStg->Open( statpropsetstg.fmtid,
492 STGM_READ | STGM_SHARE_EXCLUSIVE,
493 &pPropStg );
494 if( FAILED(hr) ) throw L"failed IPropertySetStorage::Open";
495
496 // Display the properties in the property set
497
498 DisplayPropertySet( statpropsetstg.fmtid,
499 pwszStorageName,
500 pPropStg );
501
502 pPropStg->Release();
503 pPropStg = NULL;
504
505 // Get the FMTID of the next property set in the enumeration.
506
507 hr = penum->Next( 1, &statpropsetstg, NULL );
508
509 }
510 if( FAILED(hr) ) throw L"Failed IEnumSTATPROPSETSTG::Next";
511
512 // Special-case handling for the UserDefined property set:
513 // This property set actually lives inside the well-known
514 // DocumentSummaryInformation property set. It is the only
515 // property set which is allowed to live inside another
516 // (and exists for legacy compatibility). It does not get
517 // included in a normal enumeration, so we have to check for
518 // it explicitely. We'll handle it by looking for it when
519 // we reach the end of the enumerator.
520
521 hr = pPropSetStg->Open( FMTID_UserDefinedProperties,
522 STGM_READ | STGM_SHARE_EXCLUSIVE,
523 &pPropStg );
524 if( SUCCEEDED(hr) )
525 {
526 DisplayPropertySet( FMTID_UserDefinedProperties,
527 pwszStorageName,
528 pPropStg );
529 pPropStg = NULL;
530 }
531
532 }
533 catch( LPCWSTR pwszErrorMessage )
534 {
535 wprintf( L"Error in DumpPropertySetsInStorage: %s (hr = %08x)\n",
536 pwszErrorMessage, hr );
537 }
538
539 if( NULL != pPropStg )
540 pPropStg->Release();
541 if( NULL != penum )
542 penum->Release();
543}
544
545
546//+----------------------------------------------------------------------------
547//
548// DisplayStorageTree
549//
550// Dump all the property sets in the given storage and recursively in
551// all its children.
552//
553//+----------------------------------------------------------------------------
554
555void
556DisplayStorageTree( const WCHAR *pwszStorageName, IStorage *pStg )
557{
558 IPropertySetStorage *pPropSetStg = NULL;
559 IStorage *pStgChild = NULL;
560 WCHAR *pwszChildStorageName = NULL;
561 IEnumSTATSTG *penum = NULL;
562 HRESULT hr = S_OK;
563 STATSTG statstg;
564
565 memset( &statstg, 0, sizeof(statstg) );
566
567 try
568 {
569 // Dump the property sets at this storage level
570
571 hr = pStg->QueryInterface( IID_IPropertySetStorage,
572 reinterpret_cast<void**>(&pPropSetStg) );
573 if( FAILED(hr) )
574 throw L"Failed IStorage::QueryInterface(IID_IPropertySetStorage)";
575
576 DisplayPropertySetsInStorage( pwszStorageName, pPropSetStg );
577
578 // Get an enumerator for this storage.
579
580 hr = pStg->EnumElements( NULL, NULL, NULL, &penum );
581 if( FAILED(hr) ) throw L"failed IStorage::Enum";
582
583 // Get the name of the first element (stream/storage)
584 // in the enumeration. As usual, ‘Next' will return
585 // S_OK if it returns an element of the enumerator,
586 // S_FALSE if there are no more elements, and an
587 // error otherwise.
588
589 hr = penum->Next( 1, &statstg, 0 );
590
591 // Loop through all the direct children of this storage.
592
593 while( S_OK == hr )
594 {
595 // Check if this is a storage that isn't a property set
596 // (since we already displayed the property sets above).
597 // You can tell if a stream/storage is a property set
598 // because the first character of it's name is the
599 // ‘\005' reserved character.
600
601 if( STGTY_STORAGE == statstg.type
602 &&
603 L'\005' != statstg.pwcsName[0] )
604 {
605 // Yes, this is a normal storage, not a propset.
606 // Open the storage.
607
608 ULONG cchChildStorageName;
609
610 hr = pStg->OpenStorage( statstg.pwcsName,
611 NULL,
612 STGM_READ | STGM_SHARE_EXCLUSIVE,
613 NULL, 0,
614 &pStgChild );
615 if( FAILED(hr) ) throw L"failed IStorage::OpenStorage";
616
617 // Compose a name of the form "Storage\ChildStorage\"
618 // for display purposes. First, allocate it.
619
620 cchChildStorageName = wcslen(pwszStorageName)
621 + wcslen(statstg.pwcsName)
622 + 2 // For the two "\" chars
623 + 1; // For the string terminator
624
625 pwszChildStorageName = new WCHAR[ cchChildStorageName ];
626 if( NULL == pwszChildStorageName )
627 {
628 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
629 throw L"couldn't allocate memory";
630 }
631
632 // Terminate the name
633
634 pwszChildStorageName[ cchChildStorageName-1 ] = L'\0';
635 --cchChildStorageName;
636
637 // Build the name.
638
639 wcsncpy( pwszChildStorageName, pwszStorageName,
640 cchChildStorageName );
641 cchChildStorageName -= wcslen(pwszStorageName);
642
643 wcsncat( pwszChildStorageName, L"\\",
644 cchChildStorageName );
645 cchChildStorageName -= 2;
646
647 wcsncat( pwszChildStorageName, statstg.pwcsName,
648 cchChildStorageName );
649
650 // Dump all the property sets under this child storage.
651
652 DisplayStorageTree( pwszChildStorageName, pStgChild );
653
654 pStgChild->Release();
655 pStgChild = NULL;
656
657 delete pwszChildStorageName;
658 pwszChildStorageName = NULL;
659 }
660
661 // Move on to the next element in the enumeration of this storage.
662
663 CoTaskMemFree( statstg.pwcsName );
664 statstg.pwcsName = NULL;
665
666 hr = penum->Next( 1, &statstg, 0 );
667 }
668 if( FAILED(hr) ) throw L"failed IEnumSTATSTG::Next";
669 }
670 catch( LPCWSTR pwszErrorMessage )
671 {
672 wprintf( L"Error in DumpStorageTree: %s (hr = %08x)\n",
673 pwszErrorMessage, hr );
674 }
675
676 // Clean up before returning.
677
678 if( NULL != statstg.pwcsName )
679 CoTaskMemFree( statstg.pwcsName );
680 if( NULL != pStgChild )
681 pStgChild->Release();
682 if( NULL != pStg )
683 pStg->Release();
684 if( NULL != penum )
685 penum->Release();
686 if( NULL != pwszChildStorageName )
687 delete pwszChildStorageName;
688
689}
690
691
692//+----------------------------------------------------------------------------
693//
694// wmain
695//
696// Dump all the property sets in a file which is a storage.
697//
698//+----------------------------------------------------------------------------
699
700extern "C" void wmain( int cArgs, WCHAR *rgwszArgs[] )
701{
702 HRESULT hr = S_OK;
703 IStorage *pStg = NULL;
704
705 // Display usage information if necessary.
706
707 if( 1 == cArgs
708 ||
709 0 == wcscmp( L"-?", rgwszArgs[1] )
710 ||
711 0 == wcscmp( L"/?", rgwszArgs[1] ))
712 {
713 printf( "\n"
714 "Purpose: Enumerate all properties in all\n"
715 " property sets for a storage file\n"
716 "Usage: PropDump <filename>\n"
717 "E.g.: PropDump word.doc\n"
718 "\n" );
719 exit(0);
720 }
721
722 // Open the root storage.
723
724 hr = StgOpenStorageEx( rgwszArgs[1],
725 STGM_READ | STGM_SHARE_DENY_WRITE,
726 STGFMT_ANY,
727 0,
728 NULL,
729 NULL,
730 IID_IStorage,
731 reinterpret_cast<void**>(&pStg) );
732
733 // Dump all the properties in all the property sets within this
734 // storage.
735
736 if( FAILED(hr) )
737 {
738 wprintf( L"Error: couldn't open storage \"%s\" (hr = %08x)\n",
739 rgwszArgs[1], hr );
740 }
741 else
742 {
743 printf( "\nDisplaying all property sets \n" );
744 DisplayStorageTree( rgwszArgs[1], pStg );
745 pStg->Release();
746 }
747
748
749}
750
751
2
3//+============================================================================
4//
5// To Build: cl /GX enumall.cpp
6//
7// This sample is a program which dumps all the properties in all property
8// sets of a storage file.
9//
10//+============================================================================
11
12
13#define UNICODE
14#define _UNICODE
15
16#include <stdio.h>
17#include <windows.h>
18
19#pragma comment( lib, "ole32.lib" )
20
21//+----------------------------------------------------------------------------
22//
23// ConvertVarTypeToString
24//
25// Generate a string for a given PROPVARIANT variable type (VT).
26// For the given vt, write the string to pwszType, which is a buffer of
27// size cchType characters.
28//
29//+----------------------------------------------------------------------------
30
31void
32ConvertVarTypeToString( VARTYPE vt, WCHAR *pwszType, ULONG cchType )
33{
34 const WCHAR *pwszModifier;
35
36 // Make sure the output string will be terminated
37 // (wcsncpy doesn't guarantee termination)
38
39 pwszType[ cchType-1 ] = L'\0';
40 --cchType;
41
42 // Stringize the basic type
43
44 switch( vt & VT_TYPEMASK )
45 {
46 case VT_EMPTY:
47 wcsncpy( pwszType, L"VT_EMPTY", cchType );
48 break;
49 case VT_NULL:
50 wcsncpy( pwszType, L"VT_NULL", cchType );
51 break;
52 case VT_I2:
53 wcsncpy( pwszType, L"VT_I2", cchType );
54 break;
55 case VT_I4:
56 wcsncpy( pwszType, L"VT_I4", cchType );
57 break;
58 case VT_I8:
59 wcsncpy( pwszType, L"VT_I8", cchType );
60 break;
61 case VT_UI2:
62 wcsncpy( pwszType, L"VT_UI2", cchType );
63 break;
64 case VT_UI4:
65 wcsncpy( pwszType, L"VT_UI4", cchType );
66 break;
67 case VT_UI8:
68 wcsncpy( pwszType, L"VT_UI8", cchType );
69 break;
70 case VT_R4:
71 wcsncpy( pwszType, L"VT_R4", cchType );
72 break;
73 case VT_R8:
74 wcsncpy( pwszType, L"VT_R8", cchType );
75 break;
76 case VT_CY:
77 wcsncpy( pwszType, L"VT_CY", cchType );
78 break;
79 case VT_DATE:
80 wcsncpy( pwszType, L"VT_DATE", cchType );
81 break;
82 case VT_BSTR:
83 wcsncpy( pwszType, L"VT_BSTR", cchType );
84 break;
85 case VT_ERROR:
86 wcsncpy( pwszType, L"VT_ERROR", cchType );
87 break;
88 case VT_BOOL:
89 wcsncpy( pwszType, L"VT_BOOL", cchType );
90 break;
91 case VT_VARIANT:
92 wcsncpy( pwszType, L"VT_VARIANT", cchType );
93 break;
94 case VT_DECIMAL:
95 wcsncpy( pwszType, L"VT_DECIMAL", cchType );
96 break;
97 case VT_I1:
98 wcsncpy( pwszType, L"VT_I1", cchType );
99 break;
100 case VT_UI1:
101 wcsncpy( pwszType, L"VT_UI1", cchType );
102 break;
103 case VT_INT:
104 wcsncpy( pwszType, L"VT_INT", cchType );
105 break;
106 case VT_UINT:
107 wcsncpy( pwszType, L"VT_UINT", cchType );
108 break;
109 case VT_VOID:
110 wcsncpy( pwszType, L"VT_VOID", cchType );
111 break;
112 case VT_SAFEARRAY:
113 wcsncpy( pwszType, L"VT_SAFEARRAY", cchType );
114 break;
115 case VT_USERDEFINED:
116 wcsncpy( pwszType, L"VT_USERDEFINED", cchType );
117 break;
118 case VT_LPSTR:
119 wcsncpy( pwszType, L"VT_LPSTR", cchType );
120 break;
121 case VT_LPWSTR:
122 wcsncpy( pwszType, L"VT_LPWSTR", cchType );
123 break;
124 case VT_RECORD:
125 wcsncpy( pwszType, L"VT_RECORD", cchType );
126 break;
127 case VT_FILETIME:
128 wcsncpy( pwszType, L"VT_FILETIME", cchType );
129 break;
130 case VT_BLOB:
131 wcsncpy( pwszType, L"VT_BLOB", cchType );
132 break;
133 case VT_STREAM:
134 wcsncpy( pwszType, L"VT_STREAM", cchType );
135 break;
136 case VT_STORAGE:
137 wcsncpy( pwszType, L"VT_STORAGE", cchType );
138 break;
139 case VT_STREAMED_OBJECT:
140 wcsncpy( pwszType, L"VT_STREAMED_OBJECT", cchType );
141 break;
142 case VT_STORED_OBJECT:
143 wcsncpy( pwszType, L"VT_BLOB_OBJECT", cchType );
144 break;
145 case VT_CF:
146 wcsncpy( pwszType, L"VT_CF", cchType );
147 break;
148 case VT_CLSID:
149 wcsncpy( pwszType, L"VT_CLSID", cchType );
150 break;
151 default:
152 _snwprintf( pwszType, cchType, L"Unknown (%d)", vt & VT_TYPEMASK );
153 break;
154 }
155
156 // Adjust cchType for the characters we just added
157
158 cchType -= wcslen(pwszType);
159
160 // Add the type modifiers if present
161
162 if( vt & VT_VECTOR )
163 {
164 pwszModifier = L" | VT_VECTOR";
165 wcsncat( pwszType, pwszModifier, cchType );
166 cchType -= wcslen( pwszModifier );
167 }
168
169 if( vt & VT_ARRAY )
170 {
171 pwszModifier = L" | VT_ARRAY";
172 wcsncat( pwszType, pwszModifier, cchType );
173 cchType -= wcslen( pwszModifier );
174 }
175
176 if( vt & VT_RESERVED )
177 {
178 pwszModifier = L" | VT_RESERVED";
179 wcsncat( pwszType, pwszModifier, cchType );
180 cchType -= wcslen( pwszModifier );
181 }
182
183}
184
185
186//+----------------------------------------------------------------------------
187//
188// ConvertValueToString
189//
190// Generate a string for the value in a given PROPVARIANT structure.
191// The most common types are supported (that is, those that can be displayed
192// with printf). For other types, only an ellipses "" is displayed.
193//
194// The property to string-ize is in propvar, the resulting string goes
195// into pwszValue, which is a buffer with room for cchValue characters
196// (including the string terminator).
197//
198//+----------------------------------------------------------------------------
199
200void
201ConvertValueToString( const PROPVARIANT &propvar,
202 WCHAR *pwszValue,
203 ULONG cchValue )
204{
205 // Make sure the output string will be terminated
206
207 pwszValue[ cchValue - 1 ] = L'\0';
208 --cchValue;
209
210 // Based on the type, put the value into pwszValue as a string.
211
212 switch( propvar.vt )
213 {
214 case VT_EMPTY:
215 wcsncpy( pwszValue, L"", cchValue );
216 break;
217 case VT_NULL:
218 wcsncpy( pwszValue, L"", cchValue );
219 break;
220 case VT_I2:
221 _snwprintf( pwszValue, cchValue, L"%i", propvar.iVal );
222 break;
223 case VT_I4:
224 case VT_INT:
225 _snwprintf( pwszValue, cchValue, L"%li", propvar.lVal );
226 break;
227 case VT_I8:
228 _snwprintf( pwszValue, cchValue, L"%I64i", propvar.hVal );
229 break;
230 case VT_UI2:
231 _snwprintf ( pwszValue, cchValue, L"%u", propvar.uiVal );
232 break;
233 case VT_UI4:
234 case VT_UINT:
235 _snwprintf ( pwszValue, cchValue, L"%lu", propvar.ulVal );
236 break;
237 case VT_UI8:
238 _snwprintf ( pwszValue, cchValue, L"%I64u", propvar.uhVal );
239 break;
240 case VT_R4:
241 _snwprintf ( pwszValue, cchValue, L"%f", propvar.fltVal );
242 break;
243 case VT_R8:
244 _snwprintf ( pwszValue, cchValue, L"%lf", propvar.dblVal );
245 break;
246 case VT_BSTR:
247 _snwprintf ( pwszValue, cchValue, L"\"%s\"", propvar.bstrVal );
248 break;
249 case VT_ERROR:
250 _snwprintf ( pwszValue, cchValue, L"0x%08X", propvar.scode );
251 break;
252 case VT_BOOL:
253 _snwprintf ( pwszValue, cchValue, L"%s",
254 VARIANT_TRUE == propvar.boolVal ? L"True" : L"False" );
255 break;
256 case VT_I1:
257 _snwprintf ( pwszValue, cchValue, L"%i", propvar.cVal );
258 break;
259 case VT_UI1:
260 _snwprintf ( pwszValue, cchValue, L"%u", propvar.bVal );
261 break;
262 case VT_VOID:
263 wcsncpy( pwszValue, L"", cchValue );
264 break;
265 case VT_LPSTR:
266 if( 0 >_snwprintf ( pwszValue, cchValue, L"\"%hs\"", propvar.pszVal ))
267 // String is too big for pwszValue
268 wcsncpy( pwszValue, L"", cchValue );
269 break;
270 case VT_LPWSTR:
271 if( 0 > _snwprintf ( pwszValue, cchValue, L"\"%s\"", propvar.pwszVal ))
272 // String is too big for pwszValue
273 wcsncpy( pwszValue, L"", cchValue );
274 break;
275 case VT_FILETIME:
276 _snwprintf ( pwszValue, cchValue, L"%08x:%08x",
277 propvar.filetime.dwHighDateTime,
278 propvar.filetime.dwLowDateTime );
279 break;
280 case VT_CLSID:
281 pwszValue[0] = L'\0';
282 StringFromGUID2( *propvar.puuid, pwszValue, cchValue );
283 break;
284 default:
285 wcsncpy( pwszValue, L"", cchValue );
286 break;
287 }
288
289}
290
291
292//+----------------------------------------------------------------------------
293//
294// DisplayProperty
295//
296// Dump the ID, name, type, and value of a property.
297//
298//+----------------------------------------------------------------------------
299
300void
301DisplayProperty( const PROPVARIANT &propvar, const STATPROPSTG &statpropstg )
302{
303 WCHAR wsz[ MAX_PATH + 1 ];
304
305 ConvertVarTypeToString( statpropstg.vt, wsz, sizeof(wsz)/sizeof(wsz[0]) );
306
307 wprintf( L" ----------------------------------------------------\n"
308 L" PropID = %-5d VarType = %-23s",
309 statpropstg.propid, wsz );
310
311 if( NULL != statpropstg.lpwstrName )
312 wprintf( L" Name = %s", statpropstg.lpwstrName );
313
314 ConvertValueToString( propvar, wsz, sizeof(wsz)/sizeof(wsz[0]) );
315
316 wprintf( L"\n Value = %s\n", wsz );
317
318}
319
320
321//+----------------------------------------------------------------------------
322//
323// DisplayPropertySet
324//
325// Dump all the properties into a given property set.
326//
327//+----------------------------------------------------------------------------
328
329void
330DisplayPropertySet( FMTID fmtid,
331 const WCHAR *pwszStorageName,
332 IPropertyStorage *pPropStg )
333{
334 IEnumSTATPROPSTG *penum = NULL;
335 HRESULT hr = S_OK;
336 STATPROPSTG statpropstg;
337 PROPVARIANT propvar;
338 PROPSPEC propspec;
339 PROPID propid;
340 WCHAR *pwszFriendlyName = NULL;
341
342 // This string will hold a string-ized FMTID. It needs
343 // to be 38 characters, plus the terminator
344 // character. But just to be safe we'll make
345 // it a little bigger.
346 WCHAR wszFMTID[ 64 ] = { L"" };
347
348 PropVariantInit( &propvar );
349 memset( &statpropstg, 0, sizeof(statpropstg) );
350
351 try
352 {
353 // Display the ID of the property set
354
355 StringFromGUID2( fmtid,
356 wszFMTID,
357 sizeof(wszFMTID)/sizeof(wszFMTID[0]) );
358 wprintf( L"\n Property Set %s\n", wszFMTID );
359
360 // If this is one of common property sets, show which one.
361
362 if( FMTID_SummaryInformation == fmtid )
363 wprintf( L" (SummaryInformation property set)\n" );
364 else if( FMTID_DocSummaryInformation == fmtid )
365 wprintf( L" (DocumentSummaryInformation property set)\n" );
366 else if( FMTID_UserDefinedProperties == fmtid )
367 wprintf( L" (UserDefined property set)\n" );
368
369 // Also display the name of the storage that contains
370 // this property set
371
372 wprintf( L" in \"%s\":\n", pwszStorageName );
373
374 // If this property set has a friendly name, display it now.
375 // (Property names are stored in the special dictionary
376 // property - the name of the property set is indicated by naming
377 // the dictionary property itself.)
378
379 propid = PID_DICTIONARY;
380 pwszFriendlyName = NULL;
381
382 hr = pPropStg->ReadPropertyNames( 1, &propid, &pwszFriendlyName );
383 if( S_OK == hr )
384 {
385 wprintf( L" (Friendly name is \"%s\")\n\n", pwszFriendlyName );
386 CoTaskMemFree( pwszFriendlyName );
387 pwszFriendlyName = NULL;
388 }
389 else
390 wprintf( L"\n" );
391
392 // Get a property enumerator
393
394 hr = pPropStg->Enum( &penum );
395 if( FAILED(hr) ) throw L"Failed IPropertyStorage::Enum";
396
397 // Get the first property in the enumeration
398
399 hr = penum->Next( 1, &statpropstg, NULL );
400
401 // Loop through and display each property. The ‘Next'
402 // call above (and at the bottom of the while loop)
403 // will return S_OK if it returns another property,
404 // S_FALSE if there are no more properties,
405 // and anything else is an error.
406
407 while( S_OK == hr )
408 {
409
410 // Read the property out of the property set
411
412 PropVariantInit( &propvar );
413 propspec.ulKind = PRSPEC_PROPID;
414 propspec.propid = statpropstg.propid;
415
416 hr = pPropStg->ReadMultiple( 1, &propspec, &propvar );
417 if( FAILED(hr) ) throw L"Failed IPropertyStorage::ReadMultiple";
418
419 // Display the property value, type, etc.
420
421 DisplayProperty( propvar, statpropstg );
422
423 // Free buffers that were allocated during the read and
424 // by the enumerator.
425
426 PropVariantClear( &propvar );
427 CoTaskMemFree( statpropstg.lpwstrName );
428 statpropstg.lpwstrName = NULL;
429
430 // Move to the next property in the enumeration
431
432 hr = penum->Next( 1, &statpropstg, NULL );
433 }
434 if( FAILED(hr) ) throw L"Failed IEnumSTATPROPSTG::Next";
435 }
436 catch( LPCWSTR pwszErrorMessage )
437 {
438 wprintf( L"Error in DumpPropertySet: %s (hr = %08x)\n",
439 pwszErrorMessage, hr );
440 }
441
442 if( NULL != penum )
443 penum->Release();
444
445 if( NULL != statpropstg.lpwstrName )
446 CoTaskMemFree( statpropstg.lpwstrName );
447
448 PropVariantClear( &propvar );
449}
450
451
452//+----------------------------------------------------------------------------
453//
454// DisplayPropertySetsInStorage
455//
456// Dump the property sets in the top level of a given storage.
457//
458//+----------------------------------------------------------------------------
459
460void
461DisplayPropertySetsInStorage( const WCHAR *pwszStorageName,
462 IPropertySetStorage *pPropSetStg )
463{
464 IEnumSTATPROPSETSTG *penum = NULL;
465 HRESULT hr = S_OK;
466 IPropertyStorage *pPropStg = NULL;
467 STATPROPSETSTG statpropsetstg;
468
469 try
470 {
471 // Get a property-set enumerator (which only enumerates the property
472 // sets at this level of the storage, not its children).
473
474 hr = pPropSetStg->Enum( &penum );
475 if( FAILED(hr) ) throw L"failed IPropertySetStorage::Enum";
476
477 // Get the first property set in the enumeration.
478 // (The field we're interested in is
479 // statpropsetstg.fmtid, so that we can open the
480 // property set.)
481
482 memset( &statpropsetstg, 0, sizeof(statpropsetstg) );
483 hr = penum->Next( 1, &statpropsetstg, NULL );
484
485 // Loop through all the property sets
486
487 while( S_OK == hr )
488 {
489 // Open the property set
490
491 hr = pPropSetStg->Open( statpropsetstg.fmtid,
492 STGM_READ | STGM_SHARE_EXCLUSIVE,
493 &pPropStg );
494 if( FAILED(hr) ) throw L"failed IPropertySetStorage::Open";
495
496 // Display the properties in the property set
497
498 DisplayPropertySet( statpropsetstg.fmtid,
499 pwszStorageName,
500 pPropStg );
501
502 pPropStg->Release();
503 pPropStg = NULL;
504
505 // Get the FMTID of the next property set in the enumeration.
506
507 hr = penum->Next( 1, &statpropsetstg, NULL );
508
509 }
510 if( FAILED(hr) ) throw L"Failed IEnumSTATPROPSETSTG::Next";
511
512 // Special-case handling for the UserDefined property set:
513 // This property set actually lives inside the well-known
514 // DocumentSummaryInformation property set. It is the only
515 // property set which is allowed to live inside another
516 // (and exists for legacy compatibility). It does not get
517 // included in a normal enumeration, so we have to check for
518 // it explicitely. We'll handle it by looking for it when
519 // we reach the end of the enumerator.
520
521 hr = pPropSetStg->Open( FMTID_UserDefinedProperties,
522 STGM_READ | STGM_SHARE_EXCLUSIVE,
523 &pPropStg );
524 if( SUCCEEDED(hr) )
525 {
526 DisplayPropertySet( FMTID_UserDefinedProperties,
527 pwszStorageName,
528 pPropStg );
529 pPropStg = NULL;
530 }
531
532 }
533 catch( LPCWSTR pwszErrorMessage )
534 {
535 wprintf( L"Error in DumpPropertySetsInStorage: %s (hr = %08x)\n",
536 pwszErrorMessage, hr );
537 }
538
539 if( NULL != pPropStg )
540 pPropStg->Release();
541 if( NULL != penum )
542 penum->Release();
543}
544
545
546//+----------------------------------------------------------------------------
547//
548// DisplayStorageTree
549//
550// Dump all the property sets in the given storage and recursively in
551// all its children.
552//
553//+----------------------------------------------------------------------------
554
555void
556DisplayStorageTree( const WCHAR *pwszStorageName, IStorage *pStg )
557{
558 IPropertySetStorage *pPropSetStg = NULL;
559 IStorage *pStgChild = NULL;
560 WCHAR *pwszChildStorageName = NULL;
561 IEnumSTATSTG *penum = NULL;
562 HRESULT hr = S_OK;
563 STATSTG statstg;
564
565 memset( &statstg, 0, sizeof(statstg) );
566
567 try
568 {
569 // Dump the property sets at this storage level
570
571 hr = pStg->QueryInterface( IID_IPropertySetStorage,
572 reinterpret_cast<void**>(&pPropSetStg) );
573 if( FAILED(hr) )
574 throw L"Failed IStorage::QueryInterface(IID_IPropertySetStorage)";
575
576 DisplayPropertySetsInStorage( pwszStorageName, pPropSetStg );
577
578 // Get an enumerator for this storage.
579
580 hr = pStg->EnumElements( NULL, NULL, NULL, &penum );
581 if( FAILED(hr) ) throw L"failed IStorage::Enum";
582
583 // Get the name of the first element (stream/storage)
584 // in the enumeration. As usual, ‘Next' will return
585 // S_OK if it returns an element of the enumerator,
586 // S_FALSE if there are no more elements, and an
587 // error otherwise.
588
589 hr = penum->Next( 1, &statstg, 0 );
590
591 // Loop through all the direct children of this storage.
592
593 while( S_OK == hr )
594 {
595 // Check if this is a storage that isn't a property set
596 // (since we already displayed the property sets above).
597 // You can tell if a stream/storage is a property set
598 // because the first character of it's name is the
599 // ‘\005' reserved character.
600
601 if( STGTY_STORAGE == statstg.type
602 &&
603 L'\005' != statstg.pwcsName[0] )
604 {
605 // Yes, this is a normal storage, not a propset.
606 // Open the storage.
607
608 ULONG cchChildStorageName;
609
610 hr = pStg->OpenStorage( statstg.pwcsName,
611 NULL,
612 STGM_READ | STGM_SHARE_EXCLUSIVE,
613 NULL, 0,
614 &pStgChild );
615 if( FAILED(hr) ) throw L"failed IStorage::OpenStorage";
616
617 // Compose a name of the form "Storage\ChildStorage\"
618 // for display purposes. First, allocate it.
619
620 cchChildStorageName = wcslen(pwszStorageName)
621 + wcslen(statstg.pwcsName)
622 + 2 // For the two "\" chars
623 + 1; // For the string terminator
624
625 pwszChildStorageName = new WCHAR[ cchChildStorageName ];
626 if( NULL == pwszChildStorageName )
627 {
628 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
629 throw L"couldn't allocate memory";
630 }
631
632 // Terminate the name
633
634 pwszChildStorageName[ cchChildStorageName-1 ] = L'\0';
635 --cchChildStorageName;
636
637 // Build the name.
638
639 wcsncpy( pwszChildStorageName, pwszStorageName,
640 cchChildStorageName );
641 cchChildStorageName -= wcslen(pwszStorageName);
642
643 wcsncat( pwszChildStorageName, L"\\",
644 cchChildStorageName );
645 cchChildStorageName -= 2;
646
647 wcsncat( pwszChildStorageName, statstg.pwcsName,
648 cchChildStorageName );
649
650 // Dump all the property sets under this child storage.
651
652 DisplayStorageTree( pwszChildStorageName, pStgChild );
653
654 pStgChild->Release();
655 pStgChild = NULL;
656
657 delete pwszChildStorageName;
658 pwszChildStorageName = NULL;
659 }
660
661 // Move on to the next element in the enumeration of this storage.
662
663 CoTaskMemFree( statstg.pwcsName );
664 statstg.pwcsName = NULL;
665
666 hr = penum->Next( 1, &statstg, 0 );
667 }
668 if( FAILED(hr) ) throw L"failed IEnumSTATSTG::Next";
669 }
670 catch( LPCWSTR pwszErrorMessage )
671 {
672 wprintf( L"Error in DumpStorageTree: %s (hr = %08x)\n",
673 pwszErrorMessage, hr );
674 }
675
676 // Clean up before returning.
677
678 if( NULL != statstg.pwcsName )
679 CoTaskMemFree( statstg.pwcsName );
680 if( NULL != pStgChild )
681 pStgChild->Release();
682 if( NULL != pStg )
683 pStg->Release();
684 if( NULL != penum )
685 penum->Release();
686 if( NULL != pwszChildStorageName )
687 delete pwszChildStorageName;
688
689}
690
691
692//+----------------------------------------------------------------------------
693//
694// wmain
695//
696// Dump all the property sets in a file which is a storage.
697//
698//+----------------------------------------------------------------------------
699
700extern "C" void wmain( int cArgs, WCHAR *rgwszArgs[] )
701{
702 HRESULT hr = S_OK;
703 IStorage *pStg = NULL;
704
705 // Display usage information if necessary.
706
707 if( 1 == cArgs
708 ||
709 0 == wcscmp( L"-?", rgwszArgs[1] )
710 ||
711 0 == wcscmp( L"/?", rgwszArgs[1] ))
712 {
713 printf( "\n"
714 "Purpose: Enumerate all properties in all\n"
715 " property sets for a storage file\n"
716 "Usage: PropDump <filename>\n"
717 "E.g.: PropDump word.doc\n"
718 "\n" );
719 exit(0);
720 }
721
722 // Open the root storage.
723
724 hr = StgOpenStorageEx( rgwszArgs[1],
725 STGM_READ | STGM_SHARE_DENY_WRITE,
726 STGFMT_ANY,
727 0,
728 NULL,
729 NULL,
730 IID_IStorage,
731 reinterpret_cast<void**>(&pStg) );
732
733 // Dump all the properties in all the property sets within this
734 // storage.
735
736 if( FAILED(hr) )
737 {
738 wprintf( L"Error: couldn't open storage \"%s\" (hr = %08x)\n",
739 rgwszArgs[1], hr );
740 }
741 else
742 {
743 printf( "\nDisplaying all property sets \n" );
744 DisplayStorageTree( rgwszArgs[1], pStg );
745 pStg->Release();
746 }
747
748
749}
750
751