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的方法,很好的一个例子,值得学习.


例子如下:

  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        if0 >_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        if0 > _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, 0sizeof(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, 0sizeof(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, 0sizeof(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    if1 == 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
posted @ 2007-01-04 20:41  shipfi  阅读(3734)  评论(0编辑  收藏  举报