IPaper::Opslaan

De belangrijkste focus in deze voorbeeldcode is hoe COPaper de papiergegevens in samengestelde bestanden kan laden en opslaan. De IPaper, Loaden Save method implementations worden uitvoerig besproken.

Hier volgt de methode IPaper::Save uit 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 deze client- en serverrelatie maakt COPaper niet het samengestelde bestand dat wordt gebruikt voor het opslaan van papieren gegevens. Voor zowel de methoden Opslaan als Laden geeft de client een IStorage interfaceaanwijzer door voor een bestaand samengesteld bestand. Vervolgens wordt de IStorage- gebruikt om gegevens in dat samengestelde bestand te schrijven en te lezen. In IPaper::Save hierboven worden verschillende typen gegevens opgeslagen.

De CLSID voor DllPaper, CLSID_DllPaper, wordt geserialiseerd en opgeslagen in een speciale DOOR COM beheerde stroom binnen het opslagobject met de naam \001CompObj. De servicefunctie WriteClassStg voert deze opslag uit. Deze opgeslagen CLSID-gegevens kunnen worden gebruikt om de opslaginhoud te koppelen aan het DllPaper-onderdeel dat is gemaakt en deze kan interpreteren. In dit voorbeeld wordt de hoofdopslag doorgegeven door StoClienen wordt het hele samengestelde bestand dus gekoppeld aan het dllPaper-onderdeel. Deze CLSID-gegevens kunnen later worden opgehaald met een aanroep naar de ReadClassStg-servicefunctie.

Omdat DllPaper omgaat met bewerkbare gegevens, is het ook geschikt om een klembordindeling op te nemen in de opslag. De servicefunctie WriteFmtUserTypeStg wordt aangeroepen om zowel de aanduiding van de klembordindeling als een door de gebruiker leesbare naam voor de indeling op te slaan. De door de gebruiker leesbare naam is bedoeld voor gui-weergave in selectielijsten. De hierboven doorgegeven naam maakt gebruik van een macro, CLIPBDFMT_STR, die is gedefinieerd als DllPaper 1.0 in Paper.h. Deze opgeslagen klembordgegevens kunnen later worden opgehaald met een aanroep naar de servicefunctie ReadFmtUserTypeStg. Deze functie retourneert een tekenreekswaarde die wordt toegewezen met behulp van de taakgeheugentoewijzing. De beller is verantwoordelijk voor het vrijmaken van de tekenreeks.

Opslaan maakt vervolgens een stroom in de opslag voor de copaperpapiergegevens. De stroom heet 'PAPERDATA' en wordt doorgegeven met behulp van de STREAM_PAPERDATA_USTR macro. De methode IStorage::CreateStream vereist dat deze tekenreeks zich in Unicode bevindt. Omdat de tekenreeks tijdens het compileren is opgelost, wordt de macro gedefinieerd als Unicode in Paper.h.

#define STREAM_PAPERDATA_USTR L"PAPERDATA"

De 'L' vóór de tekenreeks, die LONG aangeeft, bereikt dit.

De methode CreateStream maakt en opent een stream binnen de opgegeven opslag. Een IStream interfaceaanwijzer voor de nieuwe stream wordt doorgegeven in een aanroeperinterfaceaanwijzervariabele. AddRef wordt aangeroepen op deze interfaceaanwijzer binnen CreateStreamen de aanroeper moet deze aanwijzer vrijgeven nadat deze is gebruikt. De methode CreateStream wordt als volgt doorgegeven aan talloze markeringen voor de toegangsmodus.

STGM_CREATE | STGM_WRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE

STGM_CREATE maakt een nieuwe stream of overschrijft een bestaande stroom met dezelfde naam. STGM_WRITE de stream opent met schrijfmachtigingen. STGM_DIRECT opent de stream voor directe toegang, in plaats van transacted access. STGM_SHARE_EXCLUSIVE opent het bestand voor exclusief, niet-gedeeld gebruik door de beller.

Nadat de PAPERDATA-stream is gemaakt, wordt de IStream-interface gebruikt om naar de stream te schrijven. De methode IStream::Write wordt gebruikt om eerst de inhoud van de PAPER_PROPERTIES-structuur op te slaan. Dit is in feite een eigenschappenheader aan de voorkant van de stream. Omdat het versienummer het eerste is in het bestand, kan het onafhankelijk worden gelezen om te bepalen hoe de volgende gegevens moeten worden verwerkt. Als de hoeveelheid gegevens die daadwerkelijk is geschreven niet gelijk is aan het aangevraagde bedrag, wordt de methode Opslaan afgebroken en wordt STG_E_CANTSAVE geretourneerd.

Het opslaan van de hele matrix met inktgegevens in de stroom is eenvoudig.

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

Omdat de methode IStream::Write werkt op een bytematrix, wordt het aantal bytes aan opgeslagen inktgegevens in de matrix berekend en begint de schrijfbewerking aan het begin van de matrix. Als de hoeveelheid gegevens die daadwerkelijk is geschreven niet gelijk is aan het aangevraagde bedrag, retourneert de methode Opslaan STG_E_CANTSAVE.

Nadat de stream is geschreven, geeft de methode IPaper::Save de IStream- aanwijzer die deze gebruikte.

De methode Opslaan roept ook de client aan IPaperSink- (in de interne methode COPaper NotifySinks) om de client op de hoogte te stellen dat de opslagbewerking is voltooid. Op dit moment keert de methode Opslaan terug naar de aanroepende client, die doorgaans de IStorage- aanwijzer vrijgeeft.