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 에서 여러 유형의 데이터가 저장됩니다.
CLSID_DllPaper DllPaper용 CLSID는 직렬화되어 "\001CompObj"라는 스토리지 개체 내의 특별한 COM 제어 스트림에 저장됩니다. WriteClassStg 서비스 함수는 이 스토리지를 수행합니다. 이 저장된 CLSID 데이터를 사용하여 스토리지 콘텐츠를 만들고 해석할 수 있는 DllPaper 구성 요소와 연결할 수 있습니다. 이 샘플에서 루트 스토리지는 StoClien에 의해 전달되므로 전체 복합 파일은 DllPaper 구성 요소와 연결됩니다. 이 CLSID 데이터는 나중에 ReadClassStg 서비스 함수를 호출하여 검색할 수 있습니다.
DllPaper는 편집 가능한 데이터를 처리하므로 스토리지에 클립보드 형식을 기록하는 것도 적절합니다. WriteFmtUserTypeStg 서비스 함수는 클립보드 형식 지정과 사용자가 읽을 수 있는 형식 이름을 모두 저장하기 위해 호출됩니다. 사용자가 읽을 수 있는 이름은 선택 목록에 GUI 표시를 위한 것입니다. 위에 전달된 이름은 paper.h에서 "DllPaper 1.0"으로 정의된 매크로 CLIPBDFMT_STR 사용합니다. 이 저장된 클립보드 데이터는 나중에 서비스 함수 ReadFmtUserTypeStg를 호출하여 검색할 수 있습니다. 이 함수는 작업 메모리 할당자를 사용하여 할당된 문자열 값을 반환합니다. 호출자는 문자열을 해제해야 합니다.
다음으로 저장 하면 COPaper 용지 데이터의 스토리지에 스트림이 만들어집니다. 스트림을 "PAPERDATA"라고 하며 STREAM_PAPERDATA_USTR 매크로를 사용하여 전달됩니다. IStorage::CreateStream 메서드를 사용하려면 이 문자열이 유니코드에 있어야 합니다. 문자열은 컴파일 시간에 고정되므로 매크로는 Paper.h에서 유니코드로 정의됩니다.
#define STREAM_PAPERDATA_USTR L"PAPERDATA"
LONG을 나타내는 문자열 앞의 'L'이 이 작업을 수행합니다.
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 메서드는 클라이언트 IPaperSink(COPaper 내부 NotifySinks 메서드)를 호출하여 클라이언트에 저장 작업이 완료되었음을 알립니다. 이 시점에서 Save 메서드는 일반적으로 IStorage 포인터를 해제하는 호출 클라이언트로 돌아갑니다.