枚举复合文件的存储结构
Word和Excel等文件均称为复合文件。这类文件内部有一个“文件系统”,采用“磁盘文件”的组织方式来组织文件内的数据,也称为“文件中的文件系统”。
每个复合文件中有一个“根存储”(类似于文件系统中的“根目录”),根存储之下是若干“子存储”(类似于“子目录”)和“数据流”(类似于“文件”),子存储之下可以再有子存储和数据流……。
下列代码可将任一复合文件的文件结构进行枚举,如配合树型控件(如:CTreeCtrl),可将文件的存储结构清晰的展现出来。
#include <atlconv.h>
void DocFileViewer(LPCTSTR lpszPathName)
{
// lpszPathName: 复合文件存储路径
// COM 初始化
// 如果是MFC程序,可以使用AfxOleInit()替代
::CoInitialize(NULL);
USES_CONVERSION;
LPCTSTR lpFileName = lpszPathName;
HRESULT hr;
IStorage * pStg = NULL;
LPCOLESTR lpwFileName = T2COLE(lpFileName); // 转换T类型为宽字符
hr = ::StgIsStorageFile(lpwFileName); // 是复合文件吗?
if (FAILED(hr))
{
return FALSE;
}
hr = ::StgOpenStorage( // 打开复合文件
lpwFileName, // 文件名称
NULL,
STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
0,
0,
&pStg); // 得到根存储接口指针
EnumStorage(pStg); // 开始枚举
if (pStg)
{
pStg->Release();
}
// COM 释放
// 如果使用了AfxOleInit(),则无需调用该函数
::CoUninitialize();
}
void EnumStorage(IStorage *pStg)
{
USES_CONVERSION;
IEnumSTATSTG * pEnum = NULL; // 枚举器
HRESULT hr;
hr = pStg->EnumElements(0, NULL, 0, &pEnum);
ASSERT(SUCCEEDED(hr));
STATSTG statstg;
IStorage * pStgSub = NULL; // 子存储接口指针
while (pEnum->Next(1, &statstg, NULL) == NOERROR)
{
// statstg.type 保存着对象类型 STGTY_STREAM 或 STGTY_STORAGE
// statstg.pwcsName 保存着对象名称
// ...... 还有时间,长度等很多信息。请查看 MSDN
switch (statstg.type)
{
case STGTY_STORAGE: // 子存储
// ...
hr = pStg->OpenStorage( // 打开子存储
statstg.pwcsName,
NULL,
STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
NULL,
0,
&pStgSub); // 得到子存储接口指针
if (FAILED(hr))
{
return;
}
EnumStorage(pStgSub); // 递归枚举子存储
break;
case STGTY_STREAM: // 数据流
// ...
break;
}
::CoTaskMemFree(statstg.pwcsName); // 释放名称所使用的内存
}
if (pEnum)
{
pEnum->Release();
}
if (pStgSub)
{
pStgSub->Release();
}
}