|  | All Products | Support | Search | microsoft.com Home
Support Home | Search Support | Browse | Products | Downloads | Contact Us | Site Help |

# HOWTO: Read Compound Document Properties Directly with VC++

The information in this article applies to:

- Microsoft Visual C++, 32-bit Editions, version 5.0
- Microsoft Office 97 for Windows

## SUMMARY

You can retrieve compound document properties from a document using standard interfaces without the server running or even being installed. For instance, you can retrieve built-in document properties such as Author, Last Modified Time, and Page Count properties of a Microsoft Office 97 document as well as other custom document properties.

## MORE INFORMATION

The following steps illustrate how you can build a compound document property viewer with Microsoft Visual C++. The example is a Win32 Console Application project, and can be modified to suit your needs.

### Steps to Create Sample

1. Create a new Win32 Console Application project, and call it PropDump.

2. Add a new file called main.cpp to your project.

3. Copy the following code into main.cpp:

          #include <stdio.h>
          #include <windows.h>

          // Dumps simple PROPVARIANT values.
          void DumpPropVariant(PROPVARIANT *pPropVar) {
             // Don't iterate arrays, just inform as an array.
             if(pPropVar->vt & VT_ARRAY) {
                printf("(Array)\n");
                return;
             }
             // Don't handle byref for simplicity, just inform byref.
             if(pPropVar->vt & VT_BYREF) {
                printf("(ByRef)\n");
                return;
             }

             // Switch types.
             switch(pPropVar->vt) {
             case VT_EMPTY:
                printf("(VT_EMPTY)\n");
                break;
             case VT_NULL:
                printf("(VT_NULL)\n");
                break;
             case VT_BLOB:
                printf("(VT_BLOB)\n");
                break;
             case VT_BOOL:
                printf("%s (VT_BOOL)\n",
                   pPropVar->boolVal ? "TRUE/YES" : "FALSE/NO");
                break;
             case VT_I2: // 2-byte signed int.
                printf("%d (VT_I2)\n", (int)pPropVar->iVal);
                break;
             case VT_I4: // 4-byte signed int.
                printf("%d (VT_I4)\n", (int)pPropVar->lVal);
                break;
             case VT_R4: // 4-byte real.
                printf("%.2lf (VT_R4)\n", (double)pPropVar->fltVal);
                break;
             case VT_R8: // 8-byte real.
                printf("%.2lf (VT_R8)\n", (double)pPropVar->dblVal);
                break;
             case VT_BSTR: // OLE Automation string.
                {
                   // Translate into ASCII.
                   char dbcs[1024];
                   char *pbstr = (char *)pPropVar->bstrVal;
                   int i = wcstombs(
                      dbcs, pPropVar->bstrVal, *((DWORD *)(pbstr-4)));
                   dbcs[i] = 0;
                   printf("%s (VT_BSTR)\n", dbcs);
                }
                break;
             case VT_LPSTR: // Null-terminated string.
                {
                   printf("%s (VT_LPSTR)\n", pPropVar->pszVal);
                }
                break;
             case VT_FILETIME:
                {
                   char *dayPre[] =
                                {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};

                   FILETIME lft;
                   FileTimeToLocalFileTime(&pPropVar->filetime, &lft);
                   SYSTEMTIME lst;
                   FileTimeToSystemTime(&lft, &lst);

                   printf("%02d:%02d.%02d %s, %s %02d/%02d/%d (VT_FILETIME)\n",
                      1+(lst.wHour-1)%12, lst.wMinute, lst.wSecond,
                      (lst.wHour>=12) ? "pm" : "am",
                      dayPre[lst.wDayOfWeek%7],
                      lst.wMonth, lst.wDay, lst.wYear);
                }
                break;
             case VT_CF: // Clipboard format.
                printf("(Clipboard format)\n");

                break;
             default: // Unhandled type, consult wtypes.h's VARENUM structure.
                printf("(Unhandled type: 0x%08lx)\n", pPropVar->vt);
                break;
             }
          }

          // Dump's built-in properties of a property storage.
          void DumpBuiltInProps(IPropertySetStorage *pPropSetStg) {
             printf("\n==================================================\n");
             printf("BuiltInProperties Properties...\n");
             printf("==================================================\n");

             IPropertyStorage *pPropStg = NULL;
             HRESULT hr;

             // Open summary information, getting an IpropertyStorage.
             hr = pPropSetStg->Open(FMTID_SummaryInformation,
                STGM_READ | STGM_SHARE_EXCLUSIVE, &pPropStg);
             //hr = pPropSetStg->Open(FMTID_UserDefinedProperties,
                //STGM_READ | STGM_SHARE_EXCLUSIVE, &pPropStg);
             if(FAILED(hr)) {
                printf("No Summary-Information.\n");
                return;
             }
             // Array of PIDSI's you are interested in.
             struct pidsiStruct {
                char *name;
                long pidsi;
             } pidsiArr[] = {
                {"Title",            PIDSI_TITLE}, // VT_LPSTR
                {"Subject",          PIDSI_SUBJECT}, // ...
                {"Author",           PIDSI_AUTHOR},
                {"Keywords",         PIDSI_KEYWORDS},
                {"Comments",         PIDSI_COMMENTS},
                {"Template",         PIDSI_TEMPLATE},
                {"LastAuthor",       PIDSI_LASTAUTHOR},
                {"Revision Number",  PIDSI_REVNUMBER},
                {"Edit Time",        PIDSI_EDITTIME}, // VT_FILENAME (UTC)
                {"Last printed",     PIDSI_LASTPRINTED}, // ...
                {"Created",          PIDSI_CREATE_DTM},
                {"Last Saved",       PIDSI_LASTSAVE_DTM},
                {"Page Count",       PIDSI_PAGECOUNT}, // VT_I4
                {"Word Count",       PIDSI_WORDCOUNT}, // ...
                {"Char Count",       PIDSI_CHARCOUNT},
                {"Thumpnail",        PIDSI_THUMBNAIL}, // VT_CF
                {"AppName",          PIDSI_APPNAME}, // VT_LPSTR
                {"Doc Security",     PIDSI_DOC_SECURITY}, // VT_I4
                {0, 0}
             };
             // Count elements in pidsiArr.
             int nPidsi = 0;
             for(nPidsi=0; pidsiArr[nPidsi].name; nPidsi++);

             // Initialize PROPSPEC for the properties you want.
             PROPSPEC *pPropSpec = new PROPSPEC [nPidsi];
             PROPVARIANT *pPropVar = new PROPVARIANT [nPidsi];

             for(int i=0; i<nPidsi; i++) {
                ZeroMemory(&pPropSpec[i], sizeof(PROPSPEC));
                pPropSpec[i].ulKind = PRSPEC_PROPID;
                pPropSpec[i].propid = pidsiArr[i].pidsi;
             }

             // Read properties.
             hr = pPropStg->ReadMultiple(nPidsi, pPropSpec, pPropVar);

             if(FAILED(hr)) {
                printf("IPropertyStg::ReadMultiple() failed w/error %08lx\n",
                       hr);
             }
             else {
                // Dump properties.
                for(i=0; i<nPidsi; i++) {
                   printf("%16s: ", pidsiArr[i].name);
                   DumpPropVariant(pPropVar + i);
                }
             }

             // De-allocate memory.
             delete [] pPropVar;
             delete [] pPropSpec;

             // Release obtained interface.
             pPropStg->Release();

          }

          // Dump's custom properties of a property storage.
          void DumpCustomProps(IPropertySetStorage *pPropSetStg) {
             printf("\n==================================================\n");
             printf("Custom Properties...\n");
             printf("==================================================\n");

             IPropertyStorage *pPropStg = NULL;
             HRESULT hr;
             IEnumSTATPROPSTG *pEnumProp;

             // Open User-Defined-Properties, getting an IpropertyStorage.
             hr = pPropSetStg->Open(FMTID_UserDefinedProperties,
                STGM_READ | STGM_SHARE_EXCLUSIVE, &pPropStg);
             if(FAILED(hr)) {
                printf("No User Defined Properties.\n");
                return;
             }

             // Get property enumerator.
             hr = pPropStg->Enum(&pEnumProp);
             if(FAILED(hr)) {
                pPropStg->Release();
                printf("Couldn't enumerate custom properties.\n");
                return;
             }

             // Enumerate properties.
             STATPROPSTG sps;
             ULONG fetched;
             PROPSPEC propSpec[1];
             PROPVARIANT propVar[1];
             while(pEnumProp->Next(1, &sps, &fetched) == S_OK) {
                // Build a PROPSPEC for this property.
                ZeroMemory(&propSpec[0], sizeof(PROPSPEC));
                propSpec[0].ulKind = PRSPEC_PROPID;
                propSpec[0].propid = sps.propid;

                // Read this property.
                hr = pPropStg->ReadMultiple(1, &propSpec[0], &propVar[0]);
                if(!FAILED(hr)) {
                   // Translate Prop name into ASCII.
                   char dbcs[1024];
                   char *pbstr = (char *)sps.lpwstrName;
                   int i = wcstombs(dbcs, sps.lpwstrName,
                                    *((DWORD *)(pbstr-4)));
                   dbcs[i] = 0;

                   // Dump this property.
                   printf("%16s: ", dbcs);
                   DumpPropVariant(&propVar[0]);
                }
             }

             // Release obtained interface.
             pEnumProp->Release();
             pPropStg->Release();

          }

          // Dump's custom and built-in properties of a compound document.
          void DumpProps(char *filename) {
             // Translate filename to Unicode.
             WCHAR wcFilename[1024];
             int i = mbstowcs(wcFilename, filename, strlen(filename));
             wcFilename[i] = 0;

             IStorage *pStorage = NULL;
             IPropertySetStorage *pPropSetStg = NULL;
             HRESULT hr;

             // Open the document as an OLE compound document.
             hr = ::StgOpenStorage(wcFilename, NULL,
                STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0, &pStorage);

             if(FAILED(hr)) {
                if(hr == STG_E_FILENOTFOUND)
                   printf("File not found.");
                else if(hr == STG_E_FILEALREADYEXISTS)
                   printf("Not a compound file.");
                else
                   printf("StgOpenStorage() failed w/error %08lx", hr);
                return;
             }

             // Obtain the IPropertySetStorage interface.
             hr = pStorage->QueryInterface(
                     IID_IPropertySetStorage, (void **)&pPropSetStg);
             if(FAILED(hr)) {
                printf("QI for IPropertySetStorage failed w/error %08lx", hr);
                pStorage->Release();
                return;
             }

             // Dump properties.
             DumpBuiltInProps(pPropSetStg);
             DumpCustomProps(pPropSetStg);

             // Release obtained interfaces.
             pPropSetStg->Release();
             pStorage->Release();
          }

          // Program entry-point.
          void main(int argc, char **argv) {
             // Validate arguments.
             if(argc != 2) {
                char *pName = strrchr(argv[0], '.');
                printf("- OLE Document Property Viewer\n");
                printf("- Usage: %s filename", pName+1);
                return;
             }

             // Pass filename to the subroutine.
             DumpProps(argv[1]);
          }

4. Compile the program.

To run the example, you should copy the PropDump.exe file to a directory in your default path; for instance c:\Windows\ or c:\Windows\Command\. Then, in a directory containing a compound document file, type PropDump followed by the name of the file. You should see output similar to the following:

BuiltInProperties Properties...

               Title: MyTitle (VT_LPSTR)
             Subject: MySubject (VT_LPSTR)
              Author: MyAuthor (VT_LPSTR)
            Keywords: MyKeywords (VT_LPSTR)
            Comments: MyComments (VT_LPSTR)
            Template: Normal (VT_LPSTR)
          LastAuthor: Me (VT_LPSTR)
     Revision Number: 8 (VT_LPSTR)
           Edit Time: 01:05.47 pm, Mon 01/01/1601 (VT_FILETIME)
        Last printed: (VT_EMPTY)
             Created: 01:42.00 pm, Fri 05/29/1998 (VT_FILETIME)
          Last Saved: 12:31.00 pm, Mon 06/01/1998 (VT_FILETIME)
          Page Count: 1 (VT_I4)
          Word Count: 3 (VT_I4)
          Char Count: 19 (VT_I4)
           Thumpnail: (VT_EMPTY)
             AppName: Microsoft Word 8.0 (VT_LPSTR)
        Doc Security: 0 (VT_I4)

Custom Properties...

       _PID_LINKBASE: (VT_BLOB)
           _PID_GUID: (VT_BLOB)
           CustProp1: CustProp1TextValue (VT_LPSTR)
           CustProp2: 77777 (VT_I4)
           CustProp3: TRUE/YES (VT_BOOL)
           CustProp4: 00:00.00 am, Tue 05/17/1977 (VT_FILETIME)

### Additional Notes

The IPropertyStorage and IPropertySetStorage interfaces were not defined in the original release of COM; thus this sample code requires a system with:

- Windows NT 4.0 or later

      - or -

- Windows 95 with Internet Explorer version 4.0 or later

      - or -

- Windows 95 with DCOM installed

Previous versions of COM specified very little with respect to properties and their usage, but did define a serialized format that allowed developers to store properties and property sets in an IStorage instance. The property identifiers and semantics of a single property set, used for summary information about a document, were also defined. At that time, it was necessary to create and manipulate that structure directly as a data stream. For more information on the property set serialized data format structure, refer to "OLE Serialized Property Set Format" in the Microsoft Developer Network.

## REFERENCES

Microsoft Developer Network: Persistent Property Sets

Microsoft Developer Network: OLE Serialized Property Set Format

(c) Microsoft Corporation 1998, All Rights Reserved. Contributions by Joe Crump, Microsoft Corporation

Additional query words: Excel Word Access Powerpoint Binder documentproperties builtindocumentproperties customdocumentproperties

    Keywords          : kbcode kbCmpDoc kbCOMt kbVC500 kbOffice
    Version           : WINDOWS:97; WINNT:5.0
    Platform          : WINDOWS winnt
    Issue type        : kbhowto

Last Reviewed: November 19, 1998 © 1999 Microsoft Corporation. All rights reserved. Terms of Use.

Article ID: Q186898

Last Reviewed:
November 19, 1998

Provided by Support Online from Microsoft Product Support Services.

Did the information in this article help answer your question?

Yes
No
Did not apply

Please provide additional comments on this article.
(255 character max)