MFC下读写复合文档
今天的工程要用到复合文档,查了一下MSDN,没有介绍如何使用。上网查了一下,相关的资料少之又少,而且还不完整,于是想起我的电脑中存有一份DELPHI的文档,里面有介绍过如何在DELPHI下读写复合文档。虽然是DELPHI写的,但都是用SDK,转为C++应该不难。(复合文档也叫做结构化文件)
读写复合文档主要用到其中的几个函数就可以了
- 先用 StgCreateDocfile 函数创建一个复合文档
123456HRESULT StgCreateDocfile(constWCHAR*pwcsName, // 指向复合文档路径的指针DWORD grfMode, // 指定访问模式DWORD reserved, // 保留参数,必须为0IStorage**ppstgOpen // 返回一个新的IStorage指针);
- 然后调用 IStorage::CreateStorage 创建一个子IStorage
1234567HRESULT CreateStorage(constWCHAR*pwcsName,// 子IStorage的名称DWORD grfMode, // 指定访问模式DWORD reserved1,// 保留参数,必须为0DWORD reserved2,// 保留参数,必须为0IStorage**ppstg // 当函数执行成功后,返回一个新的IStorage指针,如果执行失败则返回NULL); - 再调用 IStorage::CreateStream 创建一个子IStream
1234567HRESULT CreateStream(constWCHAR*pwcsName,// 子IStream的名称DWORD grfMode, // 指定访问模式DWORD reserved1,// 保留参数,必须为0DWORD*reserved2, // 保留参数,必须为0IStream*ppstm // 当函数执行成功时返回一个新的IStream接口指针,否则返回NULL);
- 最后调用 IStream::Write/IStream::Read 来写、读复合文档(实际上调用 ISequentialStream::Write/ISequentialStream::Read)
12345678910HRESULT Write(voidconst*pv, // 指向要写入的数据的缓冲区的首地址ULONG cb, // 要写入的字节数ULONG*pcbWritten // 实际写入的字节数);HRESULT Read(void*pv, // 指向用于存放读入数据的缓冲区首地址ULONG cb, // 要读入的字节数ULONG*pcbRead // 实际读入的字节数);
其中第三个参数(pcbWritten/pcbRead)可以指定为NULL,如果不知道要读入的数据大小,可以指定一个较大的数
更多资料可以查一下MSDN
CreateStorage用于创建一个子IStorage,相当于创建一个目录(复合文档类似于树形图,可以创建无限级目录)
下面代码用于写复合文档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
#define FILENAME L"C:\\myfile.stg"
// TODO: Add your control notification handler code here
DWORD dwMode=STGM_WRITE|STGM_CREATE|STGM_SHARE_EXCLUSIVE; // 只写|如果文件存在则替换,不存在则创建|独占
IStorage*pStgRoot,*pStgSub;
IStream*pStream;
pStgRoot=NULL; // 设为NULL,可以知道函数是否执行成功
////////////////////////////////////////////////////////////////////////////////
// 创建复合文档
// 如果文件不存在则创建一个新文件,已经存在则替换原文件
// 创建完成后得到一个新的storage对象,可用于创建一个根目录
StgCreateDocfile(
FILENAME, // 复合文档路径
dwMode, // 请问模式
0, // 必须为0
&pStgRoot // 返回一个新的storage对象
);
////////////////////////////////////////////////////////////////////////////////
// 创建一个根目录,并准备创建一个子目录
pStgRoot->CreateStorage(
L"StgRoot", // 子IStorage的名称
dwMode, // 请问模式
0, // 必须为0
0, // 必须为0
&pStgSub // 返回一个新的storage对象
);
////////////////////////////////////////////////////////////////////////////////
// 创建一个数据流(结点),并准备写入数据
pStgSub->CreateStream(
L"Stm", // 子IStream的名称
dwMode, // 请问模式
0, // 必须为0
0, // 必须为0
&pStream // 返回一个新的IStream接口指针
);
////////////////////////////////////////////////////////////////////////////////
// 写入数据
charstrText[]={"Hello World!"};
ULONG actWrite;
intnLen=strlen(strText); // 字串长度
pStream->Write(
strText, // 要写入的数据
nLen, // 要写入数据的长度
&actWrite // 实际写入长度,也可以设为NULL
);
////////////////////////////////////////////////////////////////////////////////
// 释放资源
// 如果不调用Release()方法,会导致写入不完全、文档体积大、无法正常读取等问题
pStream->Release();
pStgSub->Release();
pStgRoot->Release();
////////////////////////////////////////////////////////////////////////////////
CString strMsg;
strMsg.Format("数据长度:%d , 实际写入长度:%ld",nLen,actWrite);
AfxMessageBox(strMsg);
|
以上代码较简单,没有判断函数是否执行成功,大家可以用pStgRoot,pStgSub,pStream的值判断,也可以用函数返回值判断
再来读取我们的文档,也用到几个函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
HRESULT StgOpenStorage(
constWCHAR*pwcsName, // 文件路径
IStorage*pstgPriority, // 指向前一个打开的文件的storage对象
DWORD grfMode,// 访问模式
SNB snbExclude,// 指向一个SNB的结构体
DWORD reserved, // 保留参数,必须为0
IStorage**ppstgOpen // 返回 IStorage 接口
);
HRESULT OpenStorage(
constWCHAR*pwcsName,// 指定子 IStorage 接口的名称
IStorage*pstgPriority, // 一个已存在的IStorage对象指针,可设为NULL
DWORD grfMode, // 访问模式
SNB snbExclude, // SNB结构体,可设为NULL
DWORD reserved, // 保留参数,必须为0
IStorage**ppstg // 返回打开的子 IStorage 接口
);
HRESULT OpenStream(
constWCHAR*pwcsName, // 指定子 IStream 接口的名称
void*reserved1, // 保留参数,必须为NULL或0
DWORD grfMode,// 访问模式
DWORD reserved2, // 保留参数,必须为0
IStream**ppstm// 返回子 IStream 接口
);
|
最后调用ISequentialStream::Read读出数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
DWORD dwMode=STGM_READ|STGM_SHARE_EXCLUSIVE; // 只读|独占
IStorage*pStgRoot,*pStgSub;
IStream*pStream;
pStgRoot=NULL;
pStgSub=NULL;
pStream=NULL;
////////////////////////////////////////////////////////////////////////////////
// 打开文件
StgOpenStorage(
FILENAME,
NULL,
dwMode,
NULL,
0,
&pStgRoot
);
////////////////////////////////////////////////////////////////////////////////
// 打开一个目录
pStgRoot->OpenStorage(
L"StgRoot", // 注意大小写
NULL,
dwMode,
NULL,
0,
&pStgSub
);
////////////////////////////////////////////////////////////////////////////////
// 准备读取数据
pStgSub->OpenStream(
L"Stm",
NULL,
dwMode,
0,
&pStream
);
////////////////////////////////////////////////////////////////////////////////
// 读取数据
constintnLen=255; // 准备读入的长度
charstrText[nLen]={0}; // 必须指定初始值,否则会显示出乱码,也可以设为 '\0'
ULONG actRead;
pStream->Read(
strText, // 存放放入的数据的缓冲区
nLen, // 要读入数据的长度,如不清楚可以设为较大的数
&actRead // 实际读入的长度
);
////////////////////////////////////////////////////////////////////////////////
// 释放资源
pStream->Release();
pStgSub->Release();
pStgRoot->Release();
////////////////////////////////////////////////////////////////////////////////
AfxMessageBox(strText);
CString strMsg;
strMsg.Format("指定读入数据的长度:%d , 实际读入数据长度:%ld",nLen,actRead);
AfxMessageBox(strMsg);
|
现在已经完成复合文档的读写了,也可以写入其它数据如结构体等,在此就不写出代码了
文章转自 C哥的博客 MFC下读写复合文档