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();
}
}
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();
}
}