IPaper::Save

此示例代码的主要重点是 COPaper 如何在复合文件中加载和保存其纸张数据。 详细讨论了 IPaperLoadSave 方法实现。

下面是 Paper.cpp 中的 IPaper::Save 方法。

STDMETHODIMP COPaper::CImpIPaper::Save(
                 SHORT nLockKey,
                 IStorage* pIStorage)
  {
    HRESULT hr = E_FAIL;
    IStream* pIStream;
    ULONG ulToWrite, ulWritten;

    if (OwnThis())
    {
      if (m_bLocked && m_cLockKey == nLockKey && NULL != pIStorage)
      {
        // Use the COM service to mark this compound file as one 
        // that is handled by our server component, DllPaper.
        WriteClassStg(pIStorage, CLSID_DllPaper);

        // Use the COM Service to write user-readable clipboard 
        // format into the compound file.
        WriteFmtUserTypeStg(pIStorage, m_ClipBdFmt, 
                            TEXT(CLIPBDFMT_STR));

        // Create the stream to be used for the actual paper data.
        // Call it "PAPERDATA".
        hr = pIStorage->CreateStream(
               STREAM_PAPERDATA_USTR,
        STGM_CREATE | STGM_WRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
               0,
               0,
               &pIStream);
        if (SUCCEEDED(hr))
        {
          // Obtained a stream. Write data to it.
          // First, write PAPER_PROPERTIES structure.
          m_PaperProperties.lInkArraySize = m_lInkDataEnd+1;
          m_PaperProperties.crWinColor = m_crWinColor;
          m_PaperProperties.WinRect.right = m_WinRect.right;
          m_PaperProperties.WinRect.bottom = m_WinRect.bottom;
          ulToWrite = sizeof(PAPER_PROPERTIES);
          hr = pIStream->Write(&m_Paper_Properties, ulToWrite, 
                               &ulWritten);
          if (SUCCEEDED(hr) && ulToWrite != ulWritten)
            hr = STG_E_CANTSAVE;
          if (SUCCEEDED(hr))
          {
            // Now, write the complete array of Ink Data.
            ulToWrite = m_PaperProperties.lInkArraySize * 
                                                     sizeof(INKDATA);
            hr = pIStream->Write(m_paInkData, ulToWrite, &ulWritten);
            if (SUCCEEDED(hr) && ulToWrite != ulWritten)
              hr = STG_E_CANTSAVE;
          }

          // Release the stream.
          pIStream->Release();
        }
      }

      UnOwnThis();
    }

    // Notify all other connected clients that Paper is now saved.
    if (SUCCEEDED(hr))
      m_pBackObj->NotifySinks(PAPER_EVENT_SAVED, 0, 0, 0, 0);

    return hr;
  }

在此客户端和服务器关系中,COPaper 不会创建用于存储纸张数据的复合文件。 对于 SaveLoad 方法,客户端会传递现有复合文件的 IStorage 接口指针。 然后,它使用 IStorage 在该复合文件中写入和读取数据。 在 上面的 IPaper::Save 中,存储了多种类型的数据。

DllPaper 的 CLSID(CLSID_DllPaper)序列化并存储在名为“\001CompObj”的存储对象中的特殊 COM 控制流中。 WriteClassStg 服务函数执行此存储。 此存储的 CLSID 数据可用于将存储内容与创建的 DllPaper 组件相关联,并且可以对其进行解释。 在此示例中,根存储由 StoClien 传递,因此整个复合文件与 DllPaper 组件相关联。 稍后可以使用对 ReadClassStg 服务函数的调用来检索此 CLSID 数据。

由于 DllPaper 处理可编辑的数据,因此还适合在存储中记录剪贴板格式。 调用 WriteFmtUserTypeStg 服务函数来存储剪贴板格式指定和格式的用户可读名称。 用户可读名称用于在选择列表中显示 GUI。 上面传递的名称使用宏CLIPBDFMT_STR,该宏在 Paper.h 中定义为“DllPaper 1.0”。 稍后可以通过调用服务函数 ReadFmtUserTypeStg 来检索此存储的剪贴板数据。 此函数返回使用任务内存分配器分配的字符串值。 调用方负责释放字符串。

保存 下一步会在存储中为 COPaper 纸张数据创建流。 流称为“PAPERDATA”,使用STREAM_PAPERDATA_USTR宏传递。 IStorage::CreateStream 方法要求此字符串位于 Unicode 中。 由于字符串在编译时是固定的,因此该宏在 Paper.h 中定义为 Unicode。

#define STREAM_PAPERDATA_USTR L"PAPERDATA"

字符串前的“L”表示 LONG,可实现此目的。

CreateStream 方法创建并打开指定存储中的流。 新流的 IStream 接口指针在调用方接口指针变量中传递。 在 CreateStream 中的此接口指针上调用 AddRef,调用方在使用后必须释放此指针。 CreateStream 方法传递了许多访问模式标志,如下所示。

STGM_CREATE |STGM_WRITE |STGM_DIRECT |STGM_SHARE_EXCLUSIVE

STGM_CREATE 创建新的流或覆盖现有同名流。 STGM_WRITE 打开具有写入权限的流。 STGM_DIRECT 打开用于直接访问的流,而不是事务访问。 STGM_SHARE_EXCLUSIVE 打开文件供调用方独占的非共享使用。

成功创建 PAPERDATA 流后, IStream 接口用于写入流。 IStream::Write 方法用于首先存储PAPER_PROPERTIES结构的内容。 这实质上是流前面的属性标头。 由于版本号是文件中的第一件事,因此可以独立读取,以确定如何处理后面的数据。 如果实际写入的数据量不等于所请求的量,则会中止 Save 方法,并返回STG_E_CANTSAVE。

将整个墨迹数据数组保存到流很简单。

// Now write the complete array of Ink Data.
  ulToWrite = m_PaperProperties.lInkArraySize * sizeof(INKDATA);
  hr = pIStream->Write(m_paInkData, ulToWrite, &ulWritten);
  if (SUCCEEDED(hr) && ulToWrite != ulWritten)
    hr = STG_E_CANTSAVE;

由于 IStream::Write 方法对字节数组进行操作,因此计算数组中存储墨迹数据的字节数,并且写入操作从数组的开头开始。 如果实际写入的数据量不等于所请求的量, Save 方法将返回STG_E_CANTSAVE。

写入流后, IPaper::Save 方法会释放它正在使用的 IStream 指针。

Save 方法还调用 COPaper 内部 NotifySink 方法中的客户端 IPaperSink () ,以通知客户端保存操作已完成。 此时 ,Save 方法将返回到调用客户端,该客户端通常会释放 IStorage 指针。