今天的工程要用到复合文档,查了一下MSDN,没有介绍如何使用。上网查了一下,相关的资料少之又少,而且还不完整,于是想起我的电脑中存有一份DELPHI的文档,里面有介绍过如何在DELPHI下读写复合文档。虽然是DELPHI写的,但都是用SDK,转为C++应该不难。(复合文档也叫做结构化文件)
读写复合文档主要用到其中的几个函数就可以了
- 先用 StgCreateDocfile 函数创建一个复合文档
123456HRESULT StgCreateDocfile (const WCHAR * pwcsName , // 指向复合文档路径的指针DWORD grfMode , // 指定访问模式DWORD reserved , // 保留参数,必须为0IStorage * * ppstgOpen // 返回一个新的IStorage指针) ;
- 然后调用 IStorage::CreateStorage 创建一个子IStorage1234567HRESULT CreateStorage (const WCHAR * pwcsName , // 子IStorage的名称DWORD grfMode , // 指定访问模式DWORD reserved1 , // 保留参数,必须为0DWORD reserved2 , // 保留参数,必须为0IStorage * * ppstg // 当函数执行成功后,返回一个新的IStorage指针,如果执行失败则返回NULL) ;
- 再调用 IStorage::CreateStream 创建一个子IStream1234567HRESULT CreateStream (const WCHAR * pwcsName , // 子IStream的名称DWORD grfMode , // 指定访问模式DWORD reserved1 , // 保留参数,必须为0DWORD * reserved2 , // 保留参数,必须为0IStream * ppstm // 当函数执行成功时返回一个新的IStream接口指针,否则返回NULL) ;
- 最后调用 IStream::Write/IStream::Read 来写、读复合文档(实际上调用 ISequentialStream::Write/ISequentialStream::Read)12345678910HRESULT Write (void const * 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接口指针
)
;
////////////////////////////////////////////////////////////////////////////////
// 写入数据
char
strText
[
]
=
{
"Hello World!"
}
;
ULONG
actWrite
;
int
nLen
=
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
(
const
WCHAR
*
pwcsName
,
// 文件路径
IStorage
*
pstgPriority
,
// 指向前一个打开的文件的storage对象
DWORD
grfMode
,
// 访问模式
SNB
snbExclude
,
// 指向一个SNB的结构体
DWORD
reserved
,
// 保留参数,必须为0
IStorage
*
*
ppstgOpen
// 返回 IStorage 接口
)
;
HRESULT
OpenStorage
(
const
WCHAR
*
pwcsName
,
// 指定子 IStorage 接口的名称
IStorage
*
pstgPriority
,
// 一个已存在的IStorage对象指针,可设为NULL
DWORD
grfMode
,
// 访问模式
SNB
snbExclude
,
// SNB结构体,可设为NULL
DWORD
reserved
,
// 保留参数,必须为0
IStorage
*
*
ppstg
// 返回打开的子 IStorage 接口
)
;
HRESULT
OpenStream
(
const
WCHAR
*
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
)
;
////////////////////////////////////////////////////////////////////////////////
// 读取数据
const
int
nLen
=
255
;
// 准备读入的长度
char
strText
[
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下读写复合文档