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.
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <ole2.h>
#pragma comment( lib, "ole32.lib" )
const FMTID PropSetfmtid ={
0xf29f85e0,
0x4ff9,
0x1068,
{0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9 }
};
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
IPropertySetStorage *pPropSetStg = NULL;
IPropertyStorage *pPropStg = NULL;
PROPSPEC propspec;
PROPVARIANT propWrite;
PROPVARIANT propRead;
HRESULT hr = S_OK;
hr = StgOpenStorageEx( L"C:\\Document.txt",
STGM_DIRECT|STGM_SHARE_EXCLUSIVE|
STGM_READWRITE,
STGFMT_ANY,
0,
NULL,
NULL,
IID_IPropertySetStorage,
reinterpret_cast<void**>(&pPropSetStg) );
if( FAILED(hr) )
throw L"Failed StgOpenStorageEx";
hr = pPropSetStg->Create( PropSetfmtid, NULL,
PROPSETFLAG_DEFAULT,
STGM_CREATE|STGM_READWRITE|
STGM_SHARE_EXCLUSIVE,
&pPropStg );
if( FAILED(hr) )
throw L"Failed IPropertySetStorage::Open";
propspec.ulKind = PRSPEC_PROPID;
propspec.propid = 0x00000002;
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;
hr = pPropSetStg->Open( PropSetfmtid,
STGM_READ|STGM_SHARE_EXCLUSIVE,
&pPropStg );
if( FAILED(hr) )
throw L"Failed IPropertySetStorage::Open";
hr = pPropStg->ReadMultiple( 1, &propspec, &propRead );
if( FAILED(hr) )
throw L"Failed IPropertyStorage::ReadMultiple";
char* str = new char [wcslen(propRead.pwszVal) + 1];
wsprintfA ( str, "%S", propRead.pwszVal);
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;
}