复合文件简介
一.文件的存贮
- 打开记事本,输入文字并保存,这种文件叫“非结构化”文件;
- 打开电子表格,输入学生姓名和成绩保存,这种文件叫“结构化”文件;
程序中把特定数据按一定结构和顺序写入文件保存,这种文件叫“自定义结构”文件(比如1.bmp文件)。
微软在推出Win3.1后,开始垂涎桌面办公自动化软件领域。微软的OFFICE开发部门各小组,分别开发了WORD和EXCEL等软件,并采用“自定义结构”方式来存贮文件。在激烈的市场竞争下,为打败竞争对手,微软自然地产生了一个念头,如果能在WORD中嵌入EXCEL,用户在购买了WORD软件的情况下,不就没有必要再买LOTUS-123了吗!
这个计划产生后,便开始了实施,这就是COM前身OLE的起源。但立刻就遇到了一个严重的技术问题:需要把WORD产生的DOC文件和EXCEL产生的XLS文件保存在一起。怎么解决?微软是作磁盘操作系统起家的,于是很自然地提出了一个非常完美的设计方案,把磁盘文件的管理方式移植到文件。复合文件,俗称“文件中的文件系统”。连微软当年都没有想到,就这么一个简单想法,居然演变出了COM组件程序设计的方法。可以说,复合文件是COM的基石。
二.复合文件特点
内部使用指针树管理,使用“流对象”保存数据,存贮单元为512字节。一个流保存了一个字节,也要占512字节的文件空间;文件保存在磁盘上,即使一个字节也要占一个“簇”的空间;不同进程或同一进程的不同线程可同时访问一个复合文件的不同部分而互不干扰;复合文件提供了“增量访问”能力。可运行DFView.exe打开一个复合文件。
复合文件和磁盘文件操作非常类似。函数分3类:全局函数,存贮IStorage接口函数,流IStream接口函数。
下面程序段演示了一些基本函数功能和调用方法。
示例一:建立一个复合文件,并在其下建立一个子存贮,在该子存贮中再建立一个流,写入数据。
void SampleCreateDoc()
{
CoInitialize(NULL); //COM初始化
IStorage *pStg = NULL; //根存贮接口指针
IStorage *pSub = NULL; //子存贮接口指针
IStream *pStm = NULL; //流接口指针
HRESULT hr = StgCreateDocfile( //建立复合文件
L"c:\\a.stg", //文件名
STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, //打开方式
0,&pStg); //保留,得到根存贮接口指针
ASSERT(SUCCEEDED(hr)); //为突出重点,简化程序结构,所以使用了断言
hr = pStg->CreateStorage( //建立子存贮
L"SubStg", //子存贮名称
STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,0,0,
&pSub); //取得子存贮接口指针
ASSERT(SUCCEEDED(hr));
hr = pSub->CreateStream( //创建流
L"Stm", //流名称
STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,0,0,
&pStm); //取得流接口指针
ASSERT(SUCCEEDED(hr));
hr = pStm->Write( //向流中写入数据
"Hello", //数据地址
5, //字节长度(注意,没有写入串结尾的\0)
NULL); //不需要返回实际写入的字节长度
ASSERT(SUCCEEDED(hr));
if(pStm) pStm->Release(); //释放流指针
if(pSub) pSub->Release(); //释放子存贮指针
if(pStg) pStg->Release(); //释放根存贮指针
CoUninitialize() //COM释放
}
示例二:打开一个复合文件,枚举其根存贮下的所有对象。
#include <atlconv.h> //ANSI、MBCS、UNICODE转换
void SampleEnum()
{ //假设已做过COM初始化
LPCTSTR lpFileName = TEXT("c:\\a.stg");
HRESULT hr;
IStorage *pStg = NULL;
USES_CONVERSION; //注6
LPCOLESTR lpwFileName = T2COLE(lpFileName); //转换T类型为宽字符
hr = StgIsStorageFile(lpwFileName); //是复合文件吗?
if(FAILED(hr)) return;
hr = StgOpenStorage( //打开复合文件
lpwFileName, //文件名
NULL,STGM_READ|STGM_SHARE_DENY_WRITE,0,0,&pStg); //得到根存贮接口指针
IEnumSTATSTG *pEnum=NULL; //枚举器接口
hr = pStg->EnumElements(0,NULL,0,&pEnum);
ASSERT(SUCCEEDED(hr));
STATSTG statstg;
while(NOERROR == pEnum->Next(1,&statstg,NULL))
{
//statstg.type保存着对象类型STGTY_STREAM或STGTY_STORAGE
//statstg.pwcsName保存着对象名
//...... 还有时间,长度等很多信息
CoTaskMemFree(statstg.pwcsName); //释放名称所使用的内存
}
if(pEnum)pEnum->Release();
if(pStg)pStg->Release();
}
三.复合文件小结
复合文件,结构化存贮,是微软组件思想的起源,在此基础上发展出了持续性、命名、ActiveX、对象嵌入、现场激活等一系列的新技术、新概念。因此,理解和掌握复合文件非常重要,即使在程序中没有全面使用组件技术,复合文件技术也是可单独被应用的。
注:可用DFView.exe打开DOC文件进行复合文件的浏览(需改名为西文名后才能浏览)。