IPaper::Save

이 샘플 코드의 주요 초점은 COPaper가 종이 데이터를 복합 파일에 로드하고 저장할 수 있는 방법입니다. IPaper, LoadSave 메서드 구현에 대해 자세히 설명합니다.

다음은 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 에서 여러 유형의 데이터가 저장됩니다.

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 포인터를 해제하는 호출 클라이언트로 돌아갑니다.