使用 Visual C++ 访问文件的摘要信息属性集






4.57/5 (14投票s)
2006年11月9日
4分钟阅读

132400

1681
如何使用 Visual C++ 访问文件的摘要信息属性集。
引言
Visual C++ 中的这个编程领域有点隐藏。许多专业人士想知道是否可以通过 Visual C++(不使用 .NET)来获取和设置文件的摘要信息,如果可以,那么如何实现?
当您右键单击文本文件或任何其他文件时,在“属性”下会找到一个名为“摘要”的选项卡。在此选项卡中,有许多选项,如标题、主题、作者、关键字和注释。是的,您可以在 Visual C++ 代码中更改这些条目。
在本文中,我将解释完整的过程,最后我将演示如何使用 Visual C++ 6.0 获取和设置文件中“摘要”选项卡中某些属性的值。
代码
在 Ole32.lib 中有一个名为 StgOpenStorageEx
的函数,您可以使用它来打开文件系统中的现有根存储对象。此函数可用于打开复合文件和常规文件。要创建新文件,请使用 StgCreateStorageEx
函数。
打开文件时,系统会根据您在文件类型上指定的 STGFMT
标志以及文件存储所在驱动器的类型来选择结构化存储实现。
当您为现有文件调用 StgOpenStorageEx
时,它会为您提供 IPropertySetStorage
的指针。IPropertySetStorage
接口创建、打开、删除和枚举支持 IPropertyStorage
接口实例的属性集存储。对于我们当前的任务,我们应该熟悉 IPropertyStorage
接口。
IPropertyStorage
接口管理属性存储子对象中的单个属性集;IPropertySetStorage
接口管理此类属性集组的存储。任何文件系统实体都可以支持 IPropertySetStorage
,而 IPropertySetStorage
目前是在 COM 复合文件对象中实现的。
除了 Create
和 Open
方法之外,独立实现还提供了 StgCreatePropStg
和 StgOpenPropStg
辅助函数来创建和打开属性集。这两个函数支持 PROPSETFLAG_UNBUFFERED
值,因此您可以直接将更改写入属性集,而不是将其缓冲在缓存中。
IPropertySetStorage::Create
使用属性集格式标识符 (FMTID) 来引用您将要操作的属性。
下表是我们根据需要可用的预定义属性集格式标识符的列表。
名称 | 值 | 用法 |
FMTID_SummaryInformation |
{F29F85E0-4FF9-1068-AB91-08002B27B3D9} | 摘要信息属性集 |
FMTID_DocSummaryInformation |
{D5CDD502-2E9C-101B-9397-08002B2CF9AE} | 文档摘要信息和用户定义属性集 |
FMTID_UserDefinedProperties |
{D5CDD505-2E9C-101B-9397-08002B2CF9AE} | 文档摘要信息和用户定义属性集 |
这些 FMTID 定义在 UUID.LIB 库文件中,并且声明在 OLE2.H 头文件中。
根据我们当前的任务,我们将使用“FMTID_SummaryInformation
”。
成功创建属性集后,我们将使用 PROPSPEC
结构通过属性标识符 (ID) 或关联的字符串名称来指定属性。
下表列出了摘要信息属性集的字符串属性名称,以及相应的属性标识符和变量类型 (VT) 指示符。名称通常不存储在属性集中,而是从属性 ID 值推断出来的。此处显示的属性 ID 字符串条目对应于 Win32 API 头文件中找到的定义。
名称 | 属性 ID 字符串 | 属性 ID | VT 类型 |
标题 | PIDSI_TITLE |
0x00000002 | VT_LPSTR |
主题 | PIDSI_SUBJECT |
0x00000003 | VT_LPSTR |
作者 | PIDSI_AUTHOR |
0x00000004 | VT_LPSTR |
关键词 | PIDSI_KEYWORDS |
0x00000005 | VT_LPSTR |
注释 | PIDSI_COMMENTS |
0x00000006 | VT_LPSTR |
模板 | PIDSI_TEMPLATE |
0x00000007 | VT_LPSTR |
最后保存者 | PIDSI_LASTAUTHOR |
0x00000008 | VT_LPSTR |
修订号 | PIDSI_REVNUMBER |
0x00000009 | VT_LPSTR |
总编辑时间 | PIDSI_EDITTIME |
0x0000000A | VT_FILETIME (UTC) |
最后打印日期 | PIDSI_LASTPRINTED |
0x0000000B | VT_FILETIME (UTC) |
创建日期/时间(*) | PIDSI_CREATE_DTM |
0x0000000C | VT_FILETIME (UTC) |
最后保存日期/时间(*) | PIDSI_LASTSAVE_DTM |
0x0000000D | VT_FILETIME (UTC) |
页数 字数 字符数 |
|
0x0000000E 0x0000000F 0x00000010 |
|
Thumbnail | PIDSI_THUMBNAIL |
0x00000011 | VT_CF |
创建应用程序名称 | PIDSI_APPNAME |
0x00000012 | VT_LPSTR |
安全 | PIDSI_SECURITY |
0x00000013 | VT_I4 |
* 一些文件传输方法,例如从 BBS 下载,不会正确维护此信息的 文件系统版本。 |
然后,我们将使用另一个结构 'PROPVARIANT
' 来调用 IPropertyStorage
的 ReadMultiple
和 WriteMultiple
方法,以定义属性集中属性的类型标签和值。
这样,我们就可以在文件的“属性”窗口下的“摘要”选项卡中获取和设置属性。下面是完成此任务的完整代码。
在 Visual C++ 6.0 中创建一个新项目(类型为“Win32 应用程序”),项目名称为“SummaryPropPage”。在项目创建步骤 1 中选择“一个简单的 Win32 应用程序”并单击“确定”。现在,复制下面的代码并粘贴到您项目的 .cpp 文件中。
在执行此代码之前,请创建一个文件 C:\Document.txt(因为我使用的是 StgOpenStorageEx
,您可以使用 StgCreateStorageEx
来创建新文件)。现在,您可以执行代码了。
在此代码中,我编写然后读取名为“Document”的文件属性的“摘要”选项卡下的“Title”属性。您可以使用此代码获取和设置“摘要”选项卡下的任何属性,只需更改 PROPSPEC
结构中的属性名称(或使用属性 ID)。
// 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; }