IPaper::Speichern

Der Hauptfokus in diesem Beispielcode liegt darin, wie COPaper seine Papierdaten in zusammengesetzten Dateien laden und speichern kann. Die Implementierungen der IPaper-, Load- und Save-Methode werden ausführlich erläutert.

Es folgt die IPaper::Save-Methode aus 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;
  }

In dieser Client- und Serverbeziehung erstellt COPaper nicht die zusammengesetzte Datei, die zum Speichern von Papierdaten verwendet wird. Sowohl für die Save - als auch für die Load-Methode übergibt der Client einen IStorage-Schnittstellenzeiger für eine vorhandene zusammengesetzte Datei. Anschließend wird der IStorage verwendet, um Daten in dieser zusammengesetzten Datei zu schreiben und zu lesen. In IPaper::Save oben werden mehrere Datentypen gespeichert.

Die CLSID für DllPaper, CLSID_DllPaper, wird serialisiert und in einem speziellen COM-gesteuerten Stream innerhalb des Speicherobjekts mit dem Namen "\001CompObj" gespeichert. Die WriteClassStg-Dienstfunktion führt diesen Speicher aus. Diese gespeicherten CLSID-Daten können verwendet werden, um den Speicherinhalt der DllPaper-Komponente zuzuordnen, die sie erstellt und interpretieren kann. In diesem Beispiel wird der Stammspeicher von StoClien übergeben, und daher wird die gesamte zusammengesetzte Datei der DllPaper-Komponente zugeordnet. Diese CLSID-Daten können später mit einem Aufruf der ReadClassStg-Dienstfunktion abgerufen werden.

Da DllPaper sich mit bearbeitbaren Daten befasst, ist es auch angebracht, ein Zwischenablageformat im Speicher aufzuzeichnen. Die WriteFmtUserTypeStg-Dienstfunktion wird aufgerufen, um sowohl eine Bezeichnung für das Zwischenablageformat als auch einen benutzerlesbaren Namen für das Format zu speichern. Der benutzerlesbare Name ist für die GUI-Anzeige in Auswahllisten vorgesehen. Der oben übergebene Name verwendet ein Makro, CLIPBDFMT_STR, das in Paper.h als "DllPaper 1.0" definiert ist. Diese gespeicherten Zwischenablagedaten können später mit einem Aufruf der Dienstfunktion ReadFmtUserTypeStg abgerufen werden. Diese Funktion gibt einen Zeichenfolgenwert zurück, der mithilfe des Aufgabenspeicherzuweisungsmoduls zugeordnet wird. Der Aufrufer ist für das Freigeben der Zeichenfolge verantwortlich.

Save next erstellt einen Stream im Speicher für die COPaper-Papierdaten. Der Stream heißt "PAPERDATA" und wird mit dem STREAM_PAPERDATA_USTR Makro übergeben. Die IStorage::CreateStream-Methode erfordert, dass diese Zeichenfolge in Unicode enthalten ist. Da die Zeichenfolge zur Kompilierzeit behoben wird, wird das Makro in Paper.h als Unicode definiert.

#define STREAM_PAPERDATA_USTR L"PAPERDATA"

Das "L" vor der Zeichenfolge, der LONG angibt, erreicht dies.

Die CreateStream-Methode erstellt und öffnet einen Stream im angegebenen Speicher. Ein IStream-Schnittstellenzeiger für den neuen Stream wird in einer Aufruferschnittstellenzeigervariable übergeben. AddRef wird auf diesem Schnittstellenzeiger in CreateStream aufgerufen, und der Aufrufer muss diesen Zeiger nach der Verwendung freigeben. Die CreateStream-Methode wird wie folgt mit zahlreichen Zugriffsmodusflags übergeben.

STGM_CREATE | STGM_WRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE

STGM_CREATE erstellt einen neuen Stream oder überschreibt einen vorhandenen Stream mit demselben Namen. STGM_WRITE öffnet den Stream mit Schreibberechtigung. STGM_DIRECT öffnet den Stream für direkten Zugriff, im Gegensatz zum transaktionsierten Zugriff. STGM_SHARE_EXCLUSIVE öffnet die Datei zur exklusiven, nicht freigegebenen Verwendung durch den Aufrufer.

Nachdem der PAPERDATA-Stream erfolgreich erstellt wurde, wird die IStream-Schnittstelle verwendet, um in den Stream zu schreiben. Die IStream::Write-Methode wird verwendet, um zunächst den Inhalt der PAPER_PROPERTIES-Struktur zu speichern. Dies ist im Wesentlichen ein Eigenschaftenheader an der Vorderseite des Datenstroms. Da die Versionsnummer die erste Sache in der Datei ist, kann sie unabhängig gelesen werden, um zu bestimmen, wie mit den folgenden Daten umgegangen werden soll. Wenn die tatsächlich geschriebene Datenmenge nicht dem angeforderten Betrag entspricht, wird die Save-Methode abgebrochen und gibt STG_E_CANTSAVE zurück.

Das speichern des gesamten Arrays von Freihanddaten im Stream ist einfach.

// 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;

Da die IStream::Write-Methode für ein Bytearray arbeitet, wird die Anzahl der Bytes der gespeicherten Freihanddaten im Array berechnet, und der Schreibvorgang beginnt am Anfang des Arrays. Wenn die tatsächlich geschriebene Datenmenge nicht dem angeforderten Betrag entspricht, gibt die Save-Methode STG_E_CANTSAVE zurück.

Nachdem der Stream geschrieben wurde, gibt die IPaper::Save-Methode den verwendeten IStream-Zeiger frei.

Die Save-Methode ruft auch den IPaperSink-Client auf (in der internen COPaper NotifySinks-Methode), um den Client darüber zu informieren, dass der Speichervorgang abgeschlossen ist. An diesem Punkt kehrt die Save-Methode an den aufrufenden Client zurück, wodurch in der Regel der IStorage-Zeiger freigegeben wird.