今天的工程要用到复合文档,查了一下MSDN,没有介绍如何使用。上网查了一下,相关的资料少之又少,而且还不完整,于是想起我的电脑中存有一份DELPHI的文档,里面有介绍过如何在DELPHI下读写复合文档。虽然是DELPHI写的,但都是用SDK,转为C++应该不难。(复合文档也叫做结构化文件)

读写复合文档主要用到其中的几个函数就可以了

  1. 先用 StgCreateDocfile 函数创建一个复合文档
     
     
    C
     
    1
    2
    3
    4
    5
    6
    HRESULT StgCreateDocfile(
        constWCHAR*pwcsName,   // 指向复合文档路径的指针
        DWORD grfMode,           // 指定访问模式
        DWORD reserved,          // 保留参数,必须为0
        IStorage**ppstgOpen     // 返回一个新的IStorage指针
    );

     
  2. 然后调用 IStorage::CreateStorage 创建一个子IStorage
     
     
     
     
     
    C++
     
    1
    2
    3
    4
    5
    6
    7
    HRESULT CreateStorage(
        constWCHAR*pwcsName,// 子IStorage的名称
        DWORD grfMode,   // 指定访问模式
        DWORD reserved1,// 保留参数,必须为0
        DWORD reserved2,// 保留参数,必须为0
        IStorage**ppstg  // 当函数执行成功后,返回一个新的IStorage指针,如果执行失败则返回NULL
    );
  3. 再调用 IStorage::CreateStream 创建一个子IStream
     
     
     
     
     
    C++
     
    1
    2
    3
    4
    5
    6
    7
    HRESULT CreateStream(
        constWCHAR*pwcsName,// 子IStream的名称
        DWORD grfMode,   // 指定访问模式
        DWORD reserved1,// 保留参数,必须为0
        DWORD*reserved2,   // 保留参数,必须为0
        IStream*ppstm    // 当函数执行成功时返回一个新的IStream接口指针,否则返回NULL
    );

     
  4. 最后调用 IStream::Write/IStream::Read 来写、读复合文档(实际上调用 ISequentialStream::Write/ISequentialStream::Read)
     
     
     
     
     
    C++
     
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    HRESULT Write(
        voidconst*pv,   // 指向要写入的数据的缓冲区的首地址
        ULONG cb,    // 要写入的字节数
        ULONG*pcbWritten    // 实际写入的字节数
    );
    HRESULT Read(
        void*pv,   // 指向用于存放读入数据的缓冲区首地址
        ULONG cb,    // 要读入的字节数
        ULONG*pcbRead   // 实际读入的字节数
    );

    其中第三个参数(pcbWritten/pcbRead)可以指定为NULL,如果不知道要读入的数据大小,可以指定一个较大的数

更多资料可以查一下MSDN

CreateStorage用于创建一个子IStorage,相当于创建一个目录(复合文档类似于树形图,可以创建无限级目录)

下面代码用于写复合文档

 
 
 
 
 
C++
 
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
 
    // 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的值判断,也可以用函数返回值判断

再来读取我们的文档,也用到几个函数

 
 
 
 
 
C++
 
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读出数据

 
 
 
 
 
C++
 
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下读写复合文档

相关文章:

  • 2021-04-24
  • 2021-09-27
  • 2022-12-23
  • 2021-12-25
  • 2022-01-29
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-06-10
  • 2022-12-23
  • 2022-02-12
  • 2021-07-24
相关资源
相似解决方案