IPaper::Save

L'obiettivo principale di questo codice di esempio è il modo in cui COPaper può caricare e salvare i dati della carta nei file composti. Le implementazioni del metodo IPaper, Load e Save vengono illustrate in dettaglio.

Di seguito è riportato il metodo IPaper::Save da 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 questa relazione client e server, COPaper non crea il file composto usato per archiviare i dati di carta. Per entrambi i metodi Save and Load , il client passa un puntatore dell'interfaccia IStorage per un file composto esistente. Usa quindi IStorage per scrivere e leggere i dati in tale file composto. In IPaper::Salva in precedenza vengono archiviati diversi tipi di dati.

ClSID per DllPaper, CLSID_DllPaper, viene serializzato e archiviato in un flusso com speciale controllato dall'oggetto di archiviazione denominato "\001CompObj". La funzione di servizio WriteClassStg esegue questa risorsa di archiviazione. Questi dati CLSID archiviati possono essere usati per associare il contenuto di archiviazione al componente DllPaper creato e che può interpretarlo. In questo esempio l'archiviazione radice viene passata da StoClien e quindi l'intero file composto è associato al componente DllPaper. Questi dati CLSID possono essere recuperati in un secondo momento con una chiamata alla funzione del servizio ReadClassStg .

Poiché DllPaper gestisce i dati modificabili, è anche appropriato registrare un formato degli Appunti nell'archiviazione. La funzione di servizio WriteFmtUserTypeStg viene chiamata per archiviare sia una designazione del formato appunti che un nome leggibile dall'utente per il formato. Il nome leggibile dall'utente è destinato alla visualizzazione gui negli elenchi di selezione. Il nome passato sopra usa una macro, CLIPBDFMT_STR, definita come "DllPaper 1.0" in Paper.h. Questi dati archiviati negli Appunti possono essere recuperati in seguito con una chiamata alla funzione di servizio ReadFmtUserTypeStg. Questa funzione restituisce un valore stringa allocato usando l'allocatore di memoria dell'attività. Il chiamante è responsabile della liberazione della stringa.

Salva quindi crea un flusso nell'archivio per i dati della carta COPaper. Il flusso viene chiamato "PAPERDATA" e viene passato usando la macro STREAM_PAPERDATA_USTR. Il metodo IStorage::CreateStream richiede che questa stringa sia in Unicode. Poiché la stringa è fissa in fase di compilazione, la macro viene definita come Unicode in Paper.h.

#define STREAM_PAPERDATA_USTR L"PAPERDATA"

Il valore 'L' prima della stringa, che indica LONG, ottiene questo risultato.

Il metodo CreateStream crea e apre un flusso all'interno dell'archiviazione specificata. Un puntatore dell'interfaccia IStream per il nuovo flusso viene passato in una variabile del puntatore dell'interfaccia del chiamante. AddRef viene chiamato su questo puntatore di interfaccia all'interno di CreateStream e il chiamante deve rilasciare questo puntatore dopo l'uso. Il metodo CreateStream viene passato numerosi flag di modalità di accesso, come indicato di seguito.

STGM_CREATE | STGM_WRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE

STGM_CREATE crea un nuovo flusso o sovrascrive uno esistente uno dello stesso nome. STGM_WRITE apre il flusso con l'autorizzazione di scrittura. STGM_DIRECT apre il flusso per l'accesso diretto, anziché l'accesso transazionato. STGM_SHARE_EXCLUSIVE apre il file per l'uso esclusivo e non condiviso dal chiamante.

Dopo la creazione del flusso PAPERDATA, l'interfaccia IStream viene usata per scrivere nel flusso. Il metodo IStream::Write viene usato per archiviare prima il contenuto della struttura PAPER_PROPERTIES. Si tratta essenzialmente di un'intestazione delle proprietà all'inizio del flusso. Poiché il numero di versione è la prima cosa nel file, può essere letto in modo indipendente per determinare come gestire i dati che seguono. Se la quantità di dati effettivamente scritti non corrisponde alla quantità richiesta, il metodo Save viene interrotto e restituisce STG_E_CANTSAVE.

Salvare l'intera matrice di dati input penna nel flusso è semplice.

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

Poiché il metodo IStream::Write opera su una matrice di byte, il numero di byte di dati input penna archiviati nella matrice viene calcolato e l'operazione di scrittura inizia all'inizio della matrice. Se la quantità di dati effettivamente scritti non corrisponde alla quantità richiesta, il metodo Save restituisce STG_E_CANTSAVE.

Dopo aver scritto il flusso, il metodo IPaper::Save rilascia il puntatore IStream usato.

Il metodo Save chiama anche il client IPaperSink (nel metodo COPaper internal NotifySinks ) per notificare al client che l'operazione di salvataggio è stata completata. A questo punto il metodo Save restituisce al client chiamante, che in genere rilascia il puntatore IStorage .