Crear un productor de fuente no administrado
En este tema se muestra cómo se utiliza un lenguaje no administrado, como C++, para crear una aplicación que use Sync Framework para generar una fuente RSS a partir de una lista de archivos de una carpeta. La fuente RSS que genera esta aplicación contiene un elemento para cada archivo de la carpeta especificada. Cada elemento de la fuente incluye el contenido de su archivo asociado así como los metadatos de FeedSync acerca del elemento.
En este tema se suponen conocimientos básicos de los conceptos de C++ y COM.
Los ejemplos de este tema se centran en los siguientes componentes de sincronización web de Sync Framework:
Descripción de un productor de fuente
Un productor de fuente es un componente de software que genera una fuente de FeedSync que contiene los elementos proporcionados por un proveedor de sincronización. La aplicación implementa la interfaz IFeedIdConverter para convertir el formato del proveedor de los identificadores al formato de FeedSync, e implementa IFeedItemConverter para convertir el formato del proveedor de los datos de elemento al formato de FeedSync.
Para obtener más información acerca de cómo generar una fuente de FeedSync, vea Generar fuentes RSS y Atom.
Para obtener más información sobre los proveedores de sincronización, vea Implementar un proveedor estándar personalizado.
Requisitos de la compilación
Synchronization.h, FeedSync.h, FileSyncProvider.h: declaraciones de los componentes básicos de Sync Framework, de los componentes de sincronización web y del proveedor de sincronización de archivos.
#include <Synchronization.h> #include <FeedSync.h> #include <FileSyncProvider.h>
Synchronization.lib, FeedSync.lib, FileSyncProvider.lib: bibliotecas de importación.
Ejemplo
En el código de ejemplo de este tema se muestra cómo se utiliza un objeto IFeedProducer para generar una fuente RSS que contiene los elementos que un objeto IFileSyncProvider proporciona. En el ejemplo también se muestra cómo se implementan las interfaces que convierten los identificadores y datos de elemento con el formato del proveedor de sincronización de archivos al formato de FeedSync.
Implementar IFeedIdConverter
Los identificadores que usa un proveedor pueden estar en cualquier formato. Por tanto, Sync Framework requiere que una aplicación implemente la interfaz IFeedIdConverter para convertir al formato de FeedSync el formato de proveedor de los identificadores y viceversa.
Declarar IFeedIdConverter
Agregue IFeedIdConverter
a la lista de herencias de clases.
class CFileSyncProviderIdConverter : public IFeedIdConverter
Agregue los métodos IFeedIdConverter
a la declaración de la clase.
STDMETHOD(GetIdParameters)(
ID_PARAMETERS * pIdParameters);
STDMETHOD(ConvertReplicaIdToString)(
const BYTE * pbReplicaId,
IFeedIdConverterCallback * pCallback);
STDMETHOD(ConvertItemIdToString)(
const BYTE * pbItemId,
IFeedIdConverterCallback * pCallback);
STDMETHOD(ConvertStringToReplicaId)(
LPCWSTR wszStringId,
IFeedIdConverterCallback * pCallback);
STDMETHOD(ConvertStringToItemId)(
LPCWSTR wszStringId,
IFeedIdConverterCallback * pCallback);
STDMETHOD(GenerateAnonymousReplicaId)(
LPCWSTR wszWhen,
ULONG ulSequence,
IFeedIdConverterCallback * pCallback);
Método GetIdParameters
Sync Framework llama a IFeedIdConverter::GetIdParameters para obtener el esquema de formato de identificador que el proveedor utiliza. La implementación de este ejemplo devuelve el esquema de formato de los identificadores recuperado del objeto IFileSyncProvider
. Para consultar el código que recupera y almacena el esquema, vea la sección "Generar una fuente RSS" posteriormente en este tema.
STDMETHODIMP CFileSyncProviderIdConverter::GetIdParameters(
ID_PARAMETERS * pIdParameters)
{
HRESULT hr = E_FAIL;
if (NULL == pIdParameters)
{
return E_POINTER;
}
else
{
*pIdParameters = m_idParams;
return S_OK;
}
return hr;
}
Método ConvertReplicaIdToString
Sync Framework llama a IFeedIdConverter::ConvertReplicaIdToString para convertir un identificador de réplica con el formato del proveedor en una cadena. La representación de cadena del identificador puede tener cualquier formato y se escribe literalmente en la fuente. La implementación de este ejemplo utiliza la función OLE32 StringFromGUID2
para convertir en una cadena WCHAR el GUID del identificador de réplica. Devuelve la cadena resultante utilizando el método IFeedIdConverterCallback::ConvertReplicaIdToStringComplete.
STDMETHODIMP CFileSyncProviderIdConverter::ConvertReplicaIdToString(
const BYTE * pbReplicaId,
IFeedIdConverterCallback * pCallback)
{
HRESULT hr = E_FAIL;
if (NULL == pbReplicaId || NULL == pCallback)
{
hr = E_POINTER;
}
else
{
OLECHAR olestrReplicaId[64];
DWORD cchId = 64;
GUID* pguidReplicaId = (GUID*)pbReplicaId;
int cchCopied = StringFromGUID2(*pguidReplicaId, olestrReplicaId, cchId);
if (0 < cchCopied)
{
hr = pCallback->ConvertReplicaIdToStringComplete(olestrReplicaId);
}
}
return hr;
}
Método ConvertItemIdToString
Sync Framework llama a IFeedIdConverter::ConvertItemIdToString para convertir un identificador de elemento con el formato del proveedor en una cadena. La representación de cadena del identificador puede tener cualquier formato y se escribe literalmente en la fuente. La implementación de este ejemplo convierte en una cadena WCHAR el formato de estructura SYNC_GID de un identificador de elemento. Utiliza la función CRT _ui64tow_s
para convertir en una cadena WCHAR la parte de prefijo de ULONGLONG. También utiliza la función OLE32 StringFromGUID2
para convertir en una cadena WCHAR la parte GUID del identificador. El ejemplo concatena estas dos cadenas en una y devuelve la cadena resultante utilizando IFeedIdConverterCallback::ConvertItemIdToStringComplete.
STDMETHODIMP CFileSyncProviderIdConverter::ConvertItemIdToString(
const BYTE * pbItemId,
IFeedIdConverterCallback * pCallback)
{
HRESULT hr = E_FAIL;
if (NULL == pbItemId || NULL == pCallback)
{
hr = E_POINTER;
}
else
{
SYNC_GID* pgid = (SYNC_GID*)pbItemId;
// Convert the prefix to a string.
errno_t err;
WCHAR wszId[64];
DWORD cchId = 64;
err = _ui64tow_s(pgid->ullGidPrefix, wszId, cchId, 16);
if (0 == err)
{
// Convert the GUID part to a string, appended to the prefix string.
size_t cchPrefix = wcslen(wszId);
int cchCopied = StringFromGUID2(pgid->guidUniqueId, &(wszId[cchPrefix]), cchId - cchPrefix);
if (0 < cchCopied)
{
// Send the converted ID.
hr = pCallback->ConvertItemIdToStringComplete(wszId);
}
}
else
{
hr = HRESULT_FROM_WIN32(err);
}
}
return hr;
}
Método ConvertStringToReplicaId
Sync Framework llama a IFeedIdConverter::ConvertStringToReplicaId para convertir un identificador de réplica con el formato de cadena en el formato del proveedor. La representación de cadena coincide exactamente con el resultados que el método ConvertReplicaIdToString
devolvió a Sync Framework. La implementación de este ejemplo usa la función OLE32 CLSIDFromString
para convertir en un GUID el formato de cadena WCHAR del identificador de réplica. Devuelve el identificador resultante utilizando el método IFeedIdConverterCallback::ConvertStringToReplicaIdComplete.
STDMETHODIMP CFileSyncProviderIdConverter::ConvertStringToReplicaId(
LPCWSTR wszStringId,
IFeedIdConverterCallback * pCallback)
{
HRESULT hr = E_FAIL;
if (NULL == wszStringId || NULL == pCallback)
{
hr = E_POINTER;
}
else
{
GUID guidReplicaId;
hr = CLSIDFromString((LPOLESTR)wszStringId, &guidReplicaId);
if (SUCCEEDED(hr))
{
hr = pCallback->ConvertStringToReplicaIdComplete((BYTE*)&guidReplicaId);
}
}
return hr;
}
Método ConvertStringToItemId
Sync Framework llama a IFeedIdConverter::ConvertStringToItemId para convertir un identificador de elemento con formato de cadena en el formato del proveedor. La representación de cadena coincide exactamente con el resultados que el método ConvertItemIdToString
devolvió a Sync Framework. La implementación de este ejemplo usa la función CRT wcstoui64
para convertir la cadena WCHAR de la parte de prefijo del identificador en un valor ULONGLONG. También usa la función OLE32 CLSIDFromString
para convertir en un GUID la cadena WCHAR de la parte de GUID del identificador. El ejemplo devuelve el identificador resultante con el formato SYNC_GID
usando el método IFeedIdConverterCallback::ConvertStringToItemIdComplete.
STDMETHODIMP CFileSyncProviderIdConverter::ConvertStringToItemId(
LPCWSTR wszStringId,
IFeedIdConverterCallback * pCallback)
{
HRESULT hr = E_FAIL;
if (NULL == wszStringId || NULL == pCallback)
{
hr = E_POINTER;
}
else
{
SYNC_GID gid;
// Convert the prefix from the string.
WCHAR* pwszGuid = NULL;
gid.ullGidPrefix = _wcstoui64(wszStringId, &pwszGuid, 16);
// Convert the GUID part from the string.
hr = CLSIDFromString(pwszGuid, &(gid.guidUniqueId));
if (SUCCEEDED(hr))
{
// Send the converted ID.
hr = pCallback->ConvertStringToItemIdComplete((BYTE*)&gid);
}
}
return hr;
}
Métodos que no se implementan
El método siguiente no se requiere en los escenarios de productor de fuente básicos. Este método puede devolver E_NOTIMPL:
Implementar IFeedItemConverter
Los datos de elemento de un proveedor pueden tener cualquier formato. Por tanto, Sync Framework necesita que una aplicación implemente la interfaz IFeedItemConverter para convertir los datos de elemento con el formato del proveedor en el formato de FeedSync, y viceversa.
Declarar IFeedItemConverter
Agregue IFeedItemConverter
a la lista de herencias de clases.
class CFileSyncProviderItemConverter : public IFeedItemConverter
Agregue los métodos IFeedItemConverter
a la declaración de la clase.
STDMETHOD(ConvertItemDataToXml)(
IUnknown *pItemData,
IFeedItemConverterCallback *pCallback);
STDMETHOD(ConvertItemDataToXmlText)(
IUnknown *pItemData,
IFeedItemConverterCallback *pCallback);
STDMETHOD(ConvertXmlToItemData)(
IUnknown * pItemXml,
IFeedItemConverterCallback *pCallback);
STDMETHOD(ConvertXmlTextToItemData)(
LPCWSTR wszItemXmlText,
IFeedItemConverterCallback *pCallback);
ConvertItemDataToXmlText
Sync Framework llama a IFeedItemConverter::ConvertItemDataToXmlText para convertir los datos de elemento con el formato del proveedor en texto XML. Se llama a ConvertItemDataToXmlText
cuando IFeedItemConverter::ConvertItemDataToXml devuelve E_NOTIMPL. La implementación de este ejemplo espera que los archivos que se generan contengan XML válido para un elemento RSS, en un formato Unicode. A continuación se muestra un ejemplo del contenido del archivo.
<item>
<title>Sample</title>
<description>A sample item.</description>
</item>
IFileSyncProvider
proporciona el contenido de un archivo como un objeto IFileDataRetriever
. La implementación de este ejemplo recibe un objeto IStream
del objeto IFileDataRetriever
y lo utiliza para leer el contenido del archivo en una cadena WCHAR. Devuelve la cadena resultante utilizando el método IFeedItemConverterCallback::ConvertItemDataToXmlTextComplete.
STDMETHODIMP CFileSyncProviderItemConverter::ConvertItemDataToXmlText(
IUnknown *pItemData,
IFeedItemConverterCallback *pCallback)
{
HRESULT hr = E_UNEXPECTED;
if (NULL == pItemData || NULL == pCallback)
{
hr = E_POINTER;
}
else
{
// Get the data retriever implemented by Sync Services for File Systems.
IFileDataRetriever* pItemRetriever = NULL;
hr = pItemData->QueryInterface(__uuidof(pItemRetriever), (void**)&pItemRetriever);
if (SUCCEEDED(hr))
{
// Get the IStream out of the data retriever.
IStream* pItemStream = NULL;
hr = pItemRetriever->GetFileStream(&pItemStream);
if (SUCCEEDED(hr))
{
STATSTG ssFileData = {0};
hr = pItemStream->Stat(&ssFileData, STATFLAG_DEFAULT);
if (SUCCEEDED(hr))
{
// Only handle a maximum file size that will fit in ULONG, for convenience.
ULONG cbFileData = (ULONG)ssFileData.cbSize.QuadPart;
WCHAR* pwszFileData = (WCHAR*)new BYTE[cbFileData + sizeof(WCHAR)]; // include space for NULL terminator
if (NULL == pwszFileData)
{
hr = E_OUTOFMEMORY;
}
else
{
ULONG cbRead;
hr = pItemStream->Read(pwszFileData, cbFileData, &cbRead);
if (cbRead != cbFileData)
{
hr = E_UNEXPECTED;
}
else
{
// Sync Services for FeedSync expects a NULL terminator on the XML string.
pwszFileData[cbFileData / sizeof(WCHAR)] = L'\0';
if (SUCCEEDED(hr))
{
hr = pCallback->ConvertItemDataToXmlTextComplete(pwszFileData);
delete [] pwszFileData;
}
}
}
}
pItemStream->Release();
}
pItemRetriever->Release();
}
}
return hr;
}
Métodos que no se implementan
Los métodos siguientes no se requieren en los escenarios de productor de fuente básicos. Estos métodos pueden devolver E_NOTIMPL:
Generar una fuente RSS
Sync Framework proporciona la interfaz Interfaz IFeedProducer para ayudar a los proveedores a generar elementos a partir de la réplica asociada a una fuente de FeedSync. La implementación de este ejemplo utiliza un objeto IFileSyncProvider
como proveedor y una carpeta del sistema de archivos como réplica. El código de ejemplo da los pasos siguientes para generar la fuente:
Crea un objeto
IFileSyncProvider
y lo configura especificando la carpeta que sincronizar y un filtro que solo incluye los archivos que tienen la extensión .txt.Crea un objeto
IStream
y lo inicializa con una fuente RSS vacía. El código siguiente declara la fuente RSS vacía.const CHAR c_szEmptyRSS[] = "<?xml version=\"1.0\"?>\r\n" "<rss version=\"2.0\" xmlns:sx=\"https://www.microsoft.com/schemas/sse\">\r\n" "\t<channel>\r\n" "\t</channel>\r\n" "</rss>\r\n";
Crea un objeto
IFeedProducer
y llama a su método IFeedProducer::ProduceFeed.Escribe la fuente, que Sync Framework ha escrito en el objeto
IStream
, en un archivo de la carpeta de réplica.
El código siguiente genera la fuente.
HRESULT CFeedSynchronizerDlg::ProduceFeed(CString* pstrSyncFolder, const GUID* pguidReplica)
{
HRESULT hr;
// Create an IFileSyncProvider to represent the folder to produce.
IFileSyncProvider* pFSP = NULL;
hr = CoCreateInstance(CLSID_FileSyncProvider, NULL, CLSCTX_INPROC_SERVER,
IID_IFileSyncProvider, (void**)&pFSP);
if (SUCCEEDED(hr))
{
IFileSyncScopeFilter* pFilter = NULL;
hr = pFSP->CreateNewScopeFilter(&pFilter);
if (SUCCEEDED(hr))
{
// Filter folder contents to only include files with a .txt extension.
hr = pFilter->SetFilenameIncludes(L"*.txt");
// Keep a metadata store file in the same folder we are synchronizing.
CString strMetaPath(*pstrSyncFolder);
strMetaPath.Append(L"\\metadata.dat");
hr = pFSP->Initialize(*pguidReplica, pstrSyncFolder->GetString(),
strMetaPath.GetString(), pstrSyncFolder->GetString(), 0, pFilter, NULL, NULL);
if (SUCCEEDED(hr))
{
// Save the File Sync Provider's ID format schema so we can return it when asked.
hr = pFSP->GetIdParameters(&(m_IdConverter.m_idParams));
if (SUCCEEDED(hr))
{
// Use the IStorage and IStream implementation provided by OLE32.
IStorage* pStg = NULL;
// Create a structured storage object backed by a temporary file.
hr = StgCreateDocfile(NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE
| STGM_DIRECT | STGM_DELETEONRELEASE, 0, &pStg);
if (SUCCEEDED(hr))
{
IStream* pStream = NULL;
// Create an IStream object that can be used to read and write to the IStorage object.
hr = pStg->CreateStream(L"MyRSSStream", STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT
| STGM_CREATE, 0, 0, &pStream);
if (SUCCEEDED(hr))
{
// Initialize the stream with an empty RSS feed. This must be a single-byte CHAR
// (not WCHAR) string and must not contain a NULL terminator or ProduceFeed will
// fail with E_FAIL.
hr = pStream->Write(c_szEmptyRSS, sizeof(c_szEmptyRSS) - 1, NULL);
if (SUCCEEDED(hr))
{
// The stream is currently pointed at the end of the stream, so seek back to the beginning.
LARGE_INTEGER liSeek = {0};
hr = pStream->Seek(liSeek, STREAM_SEEK_SET, NULL);
if (SUCCEEDED(hr))
{
// Create the FeedSync producer object.
IFeedProducerConsumerServices* pFeedSvc = NULL;
hr = CoCreateInstance(CLSID_FeedSyncServices, NULL, CLSCTX_INPROC_SERVER,
IID_IFeedProducerConsumerServices, (void**)&pFeedSvc);
if (SUCCEEDED(hr))
{
IFeedProducer* pFeedProducer = NULL;
hr = pFeedSvc->CreateFeedProducer(&pFeedProducer);
if (SUCCEEDED(hr))
{
// Produce the *.txt items in the specified folder to the stream.
hr = pFeedProducer->ProduceFeed(pFSP, &m_IdConverter, &m_ItemConverter, NULL, pStream);
if (SUCCEEDED(hr))
{
// The stream now contains an RSS feed filled with the contents of the .txt files
// from the specified folder and with FeedSync metadata about each item.
// Save the contents of the stream to a file.
STATSTG stat = {0};
hr = pStream->Stat(&stat, STATFLAG_DEFAULT);
if (SUCCEEDED(hr))
{
ULONG cbFeed = (ULONG)stat.cbSize.QuadPart;
CHAR* pszFeed = new CHAR[cbFeed];
if (NULL == pszFeed)
{
hr = E_OUTOFMEMORY;
}
else
{
// Seek to the beginning of the stream.
hr = pStream->Seek(liSeek, STREAM_SEEK_SET, NULL);
if (SUCCEEDED(hr))
{
// Read the contents of the stream.
hr = pStream->Read(pszFeed, cbFeed, NULL);
if (SUCCEEDED(hr))
{
// Write the contents of the stream to a file.
CString strProducedFeed(*pstrSyncFolder);
strProducedFeed.Append(L"\\ProducedFeed.xml");
CFile fileStream(strProducedFeed.GetString(), CFile::modeCreate | CFile::modeWrite
| CFile::shareDenyNone);
fileStream.Write(pszFeed, cbFeed);
}
}
delete [] pszFeed;
}
}
}
pFeedProducer->Release();
}
pFeedSvc->Release();
}
}
}
pStream->Release();
}
pStg->Release();
}
}
}
pFilter->Release();
}
pFSP->Release();
}
return hr;
}
Pasos siguientes
Ahora que ha creado un productor de FeedSync, podría ser conveniente crear un consumidor de la fuente. Un consumidor de la fuente es un componente de software que extrae los elementos de una fuente de FeedSync y los aplica a una réplica de destino utilizando un proveedor de sincronización. Para obtener más información, vea Consumir fuentes RSS y Atom.
Vea también
Conceptos
Sincronizar fuentes web
Generar fuentes RSS y Atom
Consumir fuentes RSS y Atom
Convertir los identificadores y elementos para fuentes de átomo y RSS
Componentes de sincronización web de Sync Framework