Поделиться через


IPaper::Save

Основное внимание в этом примере кода уделяется тому, как COPaper может загружать и сохранять свои бумажные данные в составных файлах. Подробно рассматриваются реализации методов IPaper, Load и Save .

Ниже приведен метод IPaper::Save из Paper.cpp.

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, CLSID_DllPaper, сериализуется и хранится в специальном потоке, управляемом COM, в объекте хранилища с именем "\001CompObj". Это хранилище выполняется с помощью функции службы WriteClassStg . Эти сохраненные данные CLSID можно использовать для связывания содержимого хранилища с компонентом DllPaper, который создал и может интерпретировать его. В этом примере корневое хранилище передается StoClien, поэтому весь составной файл связан с компонентом DllPaper. Эти данные CLSID можно получить позже с помощью вызова функции службы ReadClassStg .

Так как DllPaper имеет дело с редактируемыми данными, также целесообразно записать формат буфера обмена в хранилище. Функция-служба WriteFmtUserTypeStg вызывается для хранения обозначения формата буфера обмена и имени, доступного для чтения пользователем. Доступное для чтения имя предназначено для отображения графического пользовательского интерфейса в списках выбора. Имя, переданное выше, использует макрос, CLIPBDFMT_STR, который определен как "DllPaper 1.0" в Paper.h. Эти сохраненные данные буфера обмена можно получить позже с помощью вызова функции службы ReadFmtUserTypeStg. Эта функция возвращает строковое значение, выделенное с помощью распределителя памяти задач. Вызывающий объект отвечает за освобождение строки.

Save next создает поток в хранилище для данных бумаги COPaper. Поток называется "PAPERDATA" и передается с помощью макроса STREAM_PAPERDATA_USTR. Метод IStorage::CreateStream требует, чтобы эта строка была в Юникоде. Так как строка фиксируется во время компиляции, макрос определяется как Юникод в Paper.h.

#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 также вызывает клиент IPaperSink (во внутреннем методе COPaper NotifySinks), чтобы уведомить клиента о завершении операции сохранения. На этом этапе метод Save возвращается вызывающей клиенту, который, как правило, освобождает указатель IStorage .