IPaper::Save
El enfoque principal en este código de ejemplo es cómo COPaper puede cargar y guardar sus datos de papel en archivos compuestos. Las implementaciones del método IPaper, Load y Save se describen en detalle.
A continuación se muestra el método IPaper::Save de 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;
}
En esta relación de cliente y servidor, COPaper no crea el archivo compuesto usado para almacenar datos de papel. Para los métodos Save y Load , el cliente pasa un puntero de interfaz IStorage para un archivo compuesto existente. A continuación, usa el IStorage para escribir y leer datos en ese archivo compuesto. En IPaper::Save above, se almacenan varios tipos de datos.
El CLSID para DllPaper, CLSID_DllPaper, se serializa y almacena en una secuencia especial controlada por COM dentro del objeto de almacenamiento denominado "\001CompObj". La función de servicio WriteClassStg realiza este almacenamiento. Estos datos CLSID almacenados se pueden usar para asociar el contenido de almacenamiento con el componente DllPaper que creó y puede interpretarlos. En este ejemplo, StoClien pasa el almacenamiento raíz y, por tanto, todo el archivo compuesto está asociado al componente DllPaper. Estos datos CLSID se pueden recuperar más adelante con una llamada a la función de servicio ReadClassStg .
Dado que DllPaper se ocupa de los datos editables, también es adecuado grabar un formato de Portapapeles en el almacenamiento. Se llama a la función de servicio WriteFmtUserTypeStg para almacenar una designación de formato de Portapapeles y un nombre legible por el usuario para el formato. El nombre legible por el usuario está pensado para la visualización de gui en listas de selección. El nombre pasado anteriormente usa una macro, CLIPBDFMT_STR, que se define como "DllPaper 1.0" en Paper.h. Estos datos almacenados del Portapapeles se pueden recuperar más adelante con una llamada a la función de servicio ReadFmtUserTypeStg. Esta función devuelve un valor de cadena que se asigna mediante el asignador de memoria de tareas. El autor de la llamada es responsable de liberar la cadena.
Guardar a continuación crea una secuencia en el almacenamiento para los datos de papel de COPaper. La secuencia se denomina "PAPERDATA" y se pasa mediante la macro STREAM_PAPERDATA_USTR. El método IStorage::CreateStream requiere que esta cadena esté en Unicode. Dado que la cadena se fija en tiempo de compilación, la macro se define como Unicode en Paper.h.
#define STREAM_PAPERDATA_USTR L"PAPERDATA"
La "L" antes de la cadena, que indica LONG, logra esto.
El método CreateStream crea y abre una secuencia dentro del almacenamiento especificado. Se pasa un puntero de interfaz IStream para la nueva secuencia en una variable de puntero de interfaz de llamada. Se llama a AddRef en este puntero de interfaz dentro de CreateStream y el autor de la llamada debe liberar este puntero después de usarlo. El método CreateStream se pasa numerosas marcas de modo de acceso, como se indica a continuación.
STGM_CREATE | STGM_WRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE
STGM_CREATE crea una nueva secuencia o sobrescribe una existente con el mismo nombre. STGM_WRITE abre la secuencia con permiso de escritura. STGM_DIRECT abre la secuencia para el acceso directo, en lugar del acceso transaccionado. STGM_SHARE_EXCLUSIVE abre el archivo para uso exclusivo y no compartido por el autor de la llamada.
Una vez creada correctamente la secuencia PAPERDATA, la interfaz IStream se usa para escribir en la secuencia. El método IStream::Write se usa para almacenar primero el contenido de la estructura de PAPER_PROPERTIES. Básicamente, se trata de un encabezado de propiedades en la parte delantera de la secuencia. Dado que el número de versión es lo primero en el archivo, se puede leer de forma independiente para determinar cómo tratar los datos siguientes. Si la cantidad de datos escritos realmente no es igual a la cantidad solicitada, se anula el método Save y devuelve STG_E_CANTSAVE.
Guardar toda la matriz de datos de entrada de lápiz en la secuencia es simple.
// 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;
Dado que el método IStream::Write funciona en una matriz de bytes, el número de bytes de datos de entrada de lápiz almacenados en la matriz se calcula y la operación de escritura comienza al principio de la matriz. Si la cantidad de datos realmente escritos no es igual a la cantidad solicitada, el método Save devuelve STG_E_CANTSAVE.
Una vez escrita la secuencia, el método IPaper::Save libera el puntero IStream que estaba usando.
El método Save también llama al IPaperSink del cliente (en el método CoPaper internal NotifySinks) para notificar al cliente que se ha completado la operación de guardado. En este momento, el método Save vuelve al cliente que realiza la llamada, que normalmente liberará el puntero de IStorage .