IPaper::Save
此範例程式碼的主要焦點在於 COPaper 如何在複合檔案中載入和儲存其紙張資料。 詳細討論 IPaper、Load和Save方法實作。
以下是 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 不會建立用來儲存紙張資料的複合檔案。 針對 Save 和 Load 方法,用戶端會傳遞現有複合檔案的 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 指標。