65.9K
CodeProject is changing. Read more.
Home

Access the Summary Information Property Set of a file using Visual C++

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.57/5 (14 votes)

Nov 9, 2006

4 min read

viewsIcon

131291

downloadIcon

1681

How to access the Summary Information Property Set of a file, using Visual C++.

Introduction

This area of programming in Visual C++ is a little bit hidden. Many professionals want to know if it is possible to get and set the summary information of a file through Visual C++ (without using .NET), and if yes, then how?

When you right click a text file or any other file, you will find under Properties, a tab called 'Summary'. Inside this tab, there are many options like Title, Subject, Author, Keywords, and Comments. Yes, it is possible that you can change these entries from within your code in Visual C++.

In this article, I will explain the complete process and finally I will demonstrate this, to get and set the value of some property in the Summary tab of a file, using Visual C++ 6.0

The code

There is a function named StgOpenStorageEx available in Ole32.lib, and you can use this to open an existing root storage object in the file system. This function can be used to open compound files and regular files. To create a new file, use the StgCreateStorageEx function.

When you open a file, the system selects a structured storage implementation depending on which STGFMT flag you specify on the file type and on the type of drive where the file is stored.

When you call StgOpenStorageEx for an existing file, it will give you the pointer of IPropertySetStorage. The IPropertySetStorage interface creates, opens, deletes, and enumerates property set storages that support instances of the IPropertyStorage interface. For our current task, we should be familiar with the interface IPropertyStorage.

The IPropertyStorage interface manages a single property set in a property storage subobject; and the IPropertySetStorage interface manages the storage of groups of such property sets. Any file system entity can support IPropertySetStorage that is currently implemented in the COM compound file object.

The stand-alone implementation also provides the StgCreatePropStg and StgOpenPropStg helper functions, in addition to the Create and Open methods, to create and open property sets. These two functions add support the PROPSETFLAG_UNBUFFERED value so you can write changes directly to the property set instead of buffering them in a cache.

IPropertySetStorage::Create use property set Format Identifiers (FMTIDs) of the property which you are going to refer.

Below is the table of Predefined Property Set Format Identifiers which we can use according to our requirements:

Name Value Usage
FMTID_SummaryInformation {F29F85E0-4FF9-1068-AB91-08002B27B3D9} The Summary Information Property Set
FMTID_DocSummaryInformation {D5CDD502-2E9C-101B-9397-08002B2CF9AE} The DocumentSummaryInformation and UserDefined Property Sets
FMTID_UserDefinedProperties {D5CDD505-2E9C-101B-9397-08002B2CF9AE} The DocumentSummaryInformation and UserDefined Property Sets

These FMTIDs are defined in the UUID.LIB library file and the declaration is available in the OLE2.H header file.

According to our current task, we will use ‘FMTID_SummaryInformation’.

After create the property set successfully, we will use a structure ‘PROPSPEC’ to specify a property either by its property identifier (ID) or the associated string name.

The following table lists the string property names for the Summary Information property set, along with the respective property identifiers and variable type (VT) indicators. The names are not typically stored in the property set, but are inferred from the Property ID value. The Property ID String entries shown here correspond to the definitions found in the Win32 API header files.

Name Property ID string Property ID VT type
Title PIDSI_TITLE 0x00000002 VT_LPSTR
Subject PIDSI_SUBJECT 0x00000003 VT_LPSTR
Author PIDSI_AUTHOR 0x00000004 VT_LPSTR
Keywords PIDSI_KEYWORDS 0x00000005 VT_LPSTR
Comments PIDSI_COMMENTS 0x00000006 VT_LPSTR
Template PIDSI_TEMPLATE 0x00000007 VT_LPSTR
Last Saved By PIDSI_LASTAUTHOR 0x00000008 VT_LPSTR
Revision Number PIDSI_REVNUMBER 0x00000009 VT_LPSTR
Total Editing Time PIDSI_EDITTIME 0x0000000A VT_FILETIME (UTC)
Last Printed PIDSI_LASTPRINTED 0x0000000B VT_FILETIME (UTC)
Create Time/Date( (*)) PIDSI_CREATE_DTM 0x0000000C VT_FILETIME (UTC)
Last saved Time/Date( (*)) PIDSI_LASTSAVE_DTM 0x0000000D VT_FILETIME (UTC)

Number of Pages

Number of Words

Number of Characters

PIDSI_PAGECOUNT

PIDSI_WORDCOUNT

PIDSI_CHARCOUNT

0x0000000E

0x0000000F

0x00000010

VT_I4

VT_I4

VT_I4

Thumbnail PIDSI_THUMBNAIL 0x00000011 VT_CF
Name of Creating Application PIDSI_APPNAME 0x00000012 VT_LPSTR
Security PIDSI_SECURITY 0x00000013 VT_I4

* Some methods of file transfer, such as a download from a BBS, do not maintain the file system version of this information correctly.

Then, we will use another structure ‘PROPVARIANT’ for calling the ReadMultiple and WriteMultiple methods of IPropertyStorage to define the type tag and the value of a property in a property set.

So in this way, we can get and set properties in the summary tab under the Properties window of a file. Below is the complete code to accomplish this task.

Make a new project in Visual C++ 6.0 (type ‘Win32 Application’) named ‘SummaryPropPage’. Select ‘A simple Win32 application’ in the project creation step 1 and click OK. Now, copy the code below and paste it in your project’s .cpp file.

Before executing this code, make a file C:\Document.txt (because I use StgOpenStorageEx, you can use StgCreateStorageEx to create new one). Now, you are ready to execute the code.

In this code, I write and then read the ‘Title’ property under the Summary tab of the properties of a file named ‘Document’. You can use this code to get and set any property under the Summary tab, just change the property name (or you can use the property ID) in the PROPSPEC structure.

// SummaryPropPage.cpp : Defines the entry point
// for the application.
//

#include "stdafx.h"

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


// Implicitly link ole32.dll
#pragma comment( lib, "ole32.lib" )



const FMTID PropSetfmtid ={
/* F29F85E0-4FF9-1068-AB91-08002B27B3D9 */
        0xf29f85e0,
        0x4ff9,
        0x1068,
        {0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9 }
        };



int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
     // TODO: Place code here.

    IPropertySetStorage *pPropSetStg = NULL;
    IPropertyStorage *pPropStg = NULL;
    PROPSPEC propspec; 
    PROPVARIANT propWrite; 
    PROPVARIANT propRead;
    HRESULT hr = S_OK;


    // Open a file and a property set within it.
    hr = StgOpenStorageEx( L"C:\\Document.txt",
                    STGM_DIRECT|STGM_SHARE_EXCLUSIVE|
                    STGM_READWRITE,
                    STGFMT_ANY,
                    // STGFMT_STORAGE //Structured 
                                      //Storage property sets
                    // STGFMT_FILE  //NTFS file system 
                                    //property sets
                    0,
                    NULL,
                    NULL,
                    IID_IPropertySetStorage,
                    reinterpret_cast<void**>(&pPropSetStg) );


    if( FAILED(hr) ) 
    throw L"Failed StgOpenStorageEx";

/*    
    hr = pPropSetStg->Open( PropSetfmtid, 
                            STGM_WRITE|
                            STGM_SHARE_EXCLUSIVE,
                            &pPropStg );
*/
    
    hr = pPropSetStg->Create( PropSetfmtid, NULL, 
                            PROPSETFLAG_DEFAULT,
                            STGM_CREATE|STGM_READWRITE|
                            STGM_SHARE_EXCLUSIVE,
                            &pPropStg );

    if( FAILED(hr) ) 
    throw L"Failed IPropertySetStorage::Open";


    //we can identify any property through its Name or its ID
//    propspec.ulKind = PRSPEC_LPWSTR;
//    propspec.lpwstr = L"Title";

    propspec.ulKind = PRSPEC_PROPID;
    propspec.propid  = 0x00000002;


    //specify the value of property
    propWrite.vt = VT_LPWSTR;
    propWrite.pwszVal = L"this value set through code";


    hr = pPropStg->WriteMultiple( 1, &propspec, 
         &propWrite, PID_FIRST_USABLE );

    if( FAILED(hr) ) 
    throw L"Failed IPropertyStorage::WriteMultiple";


    pPropStg->Release(); 
    pPropStg = NULL;


    //again open the property set

    hr = pPropSetStg->Open( PropSetfmtid, 
         STGM_READ|STGM_SHARE_EXCLUSIVE,
         &pPropStg );

    if( FAILED(hr) ) 
    throw L"Failed IPropertySetStorage::Open";


    // Read the property back and validate it
    hr = pPropStg->ReadMultiple( 1, &propspec, &propRead );
    if( FAILED(hr) ) 
    throw L"Failed IPropertyStorage::ReadMultiple";


    char* str = new char [wcslen(propRead.pwszVal) + 1];
    //    the "%S" will implicitly convert UNICODE to ANSI.
    wsprintfA ( str, "%S", propRead.pwszVal); 

    //if you want to display
    //    MessageBox(NULL,str,"Reading Value",MB_OK);

    if( hr == S_FALSE )
       throw L"Property didn't exist after " 
             L"reopening the property set"; 
    else if( propWrite.vt != propRead.vt )
       throw L"Property types didn't match " 
             L"after reopening the property set";
    else if( wcscmp( propWrite.pwszVal, propRead.pwszVal ) != 0 )
       throw L"Property values didn't match" 
             L" after reopening the property set";
    else
       wprintf( L"Success\n" );

    return 0;
}