COM编程之复合文件

1. 原理:磁盘文件组织方式与复合文件组织方式:

2. 工具:VC自带的“复合文件浏览器”:DFView(没有实现国际化,文件全路径名不得出现中文)

3. 方法:

WIN API 函数

功能说明

StgCreateDocfile() 建立一个复合文件,得到根存储对象
StgOpenStorage() 打开一个复合文件,得到根存储对象
StgIsStorageFile() 判断一个文件是否是复合文件

 

IStorage 函数

功能说明

CreateStorage() 在当前存储中建立新存储,得到子存储对象
CreateStream() 在当前存储中建立新流,得到流对象
OpenStorage() 打开子存储,得到子存储对象
OpenStream() 打开流,得到流对象
CopyTo() 复制存储下的所有对象到目标存储中,该函数可以实现“整理文件,释放碎片空间”的功能
MoveElementTo() 移动对象到目标存储中
DestoryElement() 删除对象
RenameElement() 重命名对象
EnumElements() 枚举当前存储中所有的对象
SetElementTimes() 修改对象的时间
SetClass() 在当前存储中建立一个特殊的流对象,用来保存CLSID(注5)
Stat() 取得当前存储中的系统信息
Release() 关闭存储对象
 

IStream 函数

功能说明

Read() 从流中读取数据
Write() 向流中写入数据
Seek() 定位读写位置
SetSize() 设置流尺寸。如果预先知道大小,那么先调用这个函数,可以提高性能
CopyTo() 复制流数据到另一个流对象中
Stat() 取得当前流中的系统信息
Clone() 克隆一个流对象,方便程序中的不同模块操作同一个流对象
Release() 关闭流对象
 
WIN API 补充函数 功能说明
WriteClassStg() 写CLSID到存储中,同IStorage::SetClass()
ReadClassStg() 读出WriteClassStg()写入的CLSID,相当于简化调用IStorage::Stat()
WriteClassStm() 写CLSID到流的开始位置
ReadClassStm() 读出WriteClassStm()写入的CLSID
WriteFmtUserTypeStg() 写入用户指定的剪贴板格式和名称到存储中
ReadFmtUserTypeStg() 读出WriteFmtUserTypeStg()写入的信息。方便应用程序快速判断是否是它需要的格式数据。
CreateStreamOnHGlobal() 内存句柄 HGLOBAL 转换为流对象
GetHGlobalFromStream() 取得CreateStreamOnHGlobal()调用中使用的内存句柄

4. 源码1:写一个复合文件

::CoInitialize(NULL); // COM 初始化 // 如果是MFC程序,可以使用AfxOleInit()替代 HRESULT hr; // 函数执行返回值 IStorage *pStg = NULL; // 根存储接口指针 IStorage *pSub = NULL; // 子存储接口指针 IStream *pStm = NULL; // 流接口指针 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 释放 // 如果使用 AfxOleInit(),则不调用该函数

5. 源码2:读一个复合文件

    需要加入头文件:

#include <atlbase.h>   #include <atlconv.h>

   函数体:

::CoInitialize(NULL); // COM 初始化 LPCTSTR lpFileName = _T( "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) ) { //我们从stststg结构体重查看流内容,具体的内容可以看MSDN ::CoTaskMemFree( statstg.pwcsName ); // 释放名称所使用的内存 } if( pEnum ) pEnum->Release(); if( pStg ) pStg->Release(); ::CoUninitialize() // COM 释放

6. 扩展

   MsgEx.db为QQ聊天记录的内容,我们可以用DFView查看,但是是十六进制的,要经过解码

你可能感兴趣的:(编程,api,浏览器,null,存储,mfc)