閱讀英文

共用方式為


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_DllPaper的 CLSID 會序列化,並儲存在名為 「\001CompObj」 的儲存體物件內的特殊 COM 控制資料流程中。 WriteClassStg服務函式會執行此儲存體。 這個預存的 CLSID 資料可用來建立儲存體內容與建立的 DllPaper 元件產生關聯,並可解譯它。 在此範例中,根儲存體會由 StoClien傳遞,因此整個複合檔案會與 DllPaper 元件相關聯。 稍後可以使用 ReadClassStg 服務函式的呼叫來擷取此 CLSID 資料。

因為 DllPaper 會處理可編輯的資料,所以也適合在儲存體中記錄剪貼簿格式。 呼叫 WriteFmtUserTypeStg服務函式來儲存剪貼簿格式指定和使用者可讀取的格式名稱。 使用者可讀取的名稱適用于選取清單中顯示 GUI。 上面傳遞的名稱會使用宏CLIPBDFMT_STR,其定義為 Paper.h 中的 「DllPaper 1.0」。 此儲存的剪貼簿資料稍後可以使用對 Service 函式 ReadFmtUserTypeStg的呼叫來擷取。 此函式會傳回使用工作記憶體配置器配置的字串值。 呼叫端負責釋放字串。

儲存 下一步會在 COPaper 紙張資料的儲存體中建立資料流程。 資料流程稱為 「PAPERDATA」,並使用STREAM_PAPERDATA_USTR宏傳遞。 IStorage::CreateStream方法要求此字串位於 Unicode 中。 由於字串在編譯時期是固定的,所以宏會在 Paper.h 中定義為 Unicode。

#define STREAM_PAPERDATA_USTR L"PAPERDATA"

字串之前的 'L' 表示 LONG 可達成此目的。

CreateStream方法會在指定的儲存體內建立並開啟資料流程。 新資料流程的 IStream 介面指標會在呼叫端介面指標變數中傳遞。 AddRef 會在 CreateStream內的這個介面指標上呼叫,而且呼叫端在使用之後必須釋放此指標。 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 內部 NotifySinks 方法中的用戶端IPaperSink (,) 通知用戶端儲存作業已完成。 此時 ,Save 方法會返回呼叫用戶端,這通常會釋放 IStorage 指標。