枚举复合文件的存储结构 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(); }}