如何创建非托管馈送生成方
本主题说明如何使用非托管语言(如 C++)创建一个应用程序,该应用程序使用 Sync Framework 从文件夹中的一系列文件生成 RSS 馈送。此应用程序生成的 RSS 馈送对于指定文件夹中的每个文件都包含一个项。此馈送中的每个项都包含其关联文件的内容以及有关该项的 FeedSync 元数据。
本主题假定您基本熟悉 C++ 和 COM 概念。
本主题中的示例着重介绍以下 Sync Framework Web 同步组件:
了解馈送生成方
馈送生成方是一个软件组件,它生成 FeedSync 馈送,该馈送包含由同步提供程序提供的项。应用程序实现 IFeedIdConverter 接口以便将 ID 从提供程序的格式转换为 FeedSync 格式,并实现 IFeedItemConverter 以便将项数据从提供程序的格式转换为 FeedSync 格式。
有关生成 FeedSync 馈送的更多信息,请参阅生成 RSS 和 Atom 馈送。
有关同步提供程序的更多信息,请参见实现标准自定义提供程序。
生成要求
Synchronization.h、FeedSync.h、FileSyncProvider.h:Sync Framework 核心组件、Web 同步组件和文件同步提供程序的声明。
#include <Synchronization.h> #include <FeedSync.h> #include <FileSyncProvider.h>
Synchronization.lib、FeedSync.lib、FileSyncProvider.lib:导入库。
示例
本主题中的示例代码说明如何使用 IFeedProducer 对象来生成包含由 IFileSyncProvider 对象提供的项的 RSS 馈送。此示例还说明如何实现将 ID 和项数据从文件同步提供程序的格式转换为 FeedSync 格式的接口。
实现 IFeedIdConverter
提供程序使用的 ID 可以采用任何格式。因此,Sync Framework 要求应用程序实现 IFeedIdConverter 接口,以便在提供程序格式与 FeedSync 格式之间来回转换 ID。
声明 IFeedIdConverter
将 IFeedIdConverter
添加到类继承列表。
class CFileSyncProviderIdConverter : public IFeedIdConverter
将 IFeedIdConverter
方法添加到类声明。
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);
GetIdParameters 方法
Sync Framework 调用 IFeedIdConverter::GetIdParameters 来获取由提供程序使用的 ID 格式架构。本示例中的实现返回从 IFileSyncProvider
对象检索的 ID 格式架构。有关检索和存储架构的代码,请参见本主题后面的“生成 RSS 馈送”一节。
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;
}
ConvertReplicaIdToString 方法
Sync Framework 调用 IFeedIdConverter::ConvertReplicaIdToString 来将副本 ID 从提供程序格式转换为字符串。此 ID 的字符串表示形式可以采用任何格式,并原样写入此馈送中。本示例中的实现使用 OLE32 函数 StringFromGUID2
将副本 ID 从 GUID 转换为 WCHAR 字符串。它通过使用 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;
}
ConvertItemIdToString 方法
Sync Framework 调用 IFeedIdConverter::ConvertItemIdToString 来将项 ID 从提供程序格式转换为字符串。此 ID 的字符串表示形式可以采用任何格式,并原样写入此馈送中。本示例中的实现将格式化为 SYNC_GID 结构的项 ID 转换为 WCHAR 字符串。它使用 CRT 函数 _ui64tow_s
将前缀部分从 ULONGLONG 转换为 WCHAR 字符串。它还使用 OLE32 函数 StringFromGUID2
将 ID 的 GUID 部分转换为 WCHAR 字符串。本示例使用 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;
}
ConvertStringToReplicaId 方法
Sync Framework 调用 IFeedIdConverter::ConvertStringToReplicaId 来将副本 ID 从字符串转换为提供程序格式。字符串表示形式与在 ConvertReplicaIdToString
方法中返回给 Sync Framework 的表示形式完全相同。本示例中的实现使用 OLE32 函数 CLSIDFromString
将副本 ID 从 WCHAR 字符串转换为 GUID。它通过使用 IFeedIdConverterCallback::ConvertStringToReplicaIdComplete 方法返回结果 ID。
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;
}
ConvertStringToItemId 方法
Sync Framework 调用 IFeedIdConverter::ConvertStringToItemId 来将项 ID 从字符串转换为提供程序格式。字符串表示形式与在 ConvertItemIdToString
方法中返回给 Sync Framework 的表示形式完全相同。本示例中的实现使用 CRT 函数 wcstoui64
将 ID 的前缀部分从 WCHAR 字符串转换为 ULONGLONG。它还使用 OLE32 函数 CLSIDFromString
将 ID 的 GUID 部分从 WCHAR 字符串转换为 GUID。本示例通过使用 IFeedIdConverterCallback::ConvertStringToItemIdComplete 方法返回格式化为 SYNC_GID
的结果 ID。
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;
}
未实现的方法
基本的馈送生成方方案不要求以下方法。此方法可以返回 E_NOTIMPL:
实现 IFeedItemConverter
提供程序中的项数据可以采用任何格式。因此,Sync Framework 要求应用程序实现 IFeedItemConverter 接口,以便在提供程序格式与 FeedSync 格式之间来回转换项数据。
声明 IFeedItemConverter
将 IFeedItemConverter
添加到类继承列表。
class CFileSyncProviderItemConverter : public IFeedItemConverter
将 IFeedItemConverter
方法添加到类声明。
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 调用 IFeedItemConverter::ConvertItemDataToXmlText 来将项数据从提供程序格式转换为 XML 文本。IFeedItemConverter::ConvertItemDataToXml 返回 E_NOTIMPL 时调用 ConvertItemDataToXmlText
。文件内容的示例如下所示。
<item>
<title>Sample</title>
<description>A sample item.</description>
</item>
IFileSyncProvider
提供文件的内容作为 IFileDataRetriever
对象。本示例中的实现从 IFileDataRetriever
对象获取 IStream
对象,并使用此对象将文件的内容读到 WCHAR 字符串中。它通过使用 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;
}
未实现的方法
基本的馈送生成方方案不要求以下方法。这些方法可能返回 E_NOTIMPL:
生成 RSS 馈送
Sync Framework 提供 IFeedProducer 接口 接口,以帮助提供程序将其关联副本中的项生成为 FeedSync 馈送。本示例中的实现将 IFileSyncProvider
用作提供程序,并将文件系统中的某个文件夹用作副本。本示例代码采用以下步骤以生成馈送:
创建
IFileSyncProvider
对象,并通过指定要同步的文件夹和一个只包含扩展名为 .txt 的文件的筛选器来配置此对象。创建
IStream
对象并使用空的 RSS 馈送初始化此对象。以下代码声明空的 RSS 馈送。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";
创建
IFeedProducer
对象并调用其 IFeedProducer::ProduceFeed 方法。将此馈送(已由 Sync Framework 写入
IStream
对象)写入到副本文件夹中的某个文件。
以下代码生成此馈送。
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;
}
后续步骤
既然您已创建了 FeedSync 生成方,您可能希望创建馈送使用方。馈送使用方是一个软件组件,它通过使用同步提供程序从 FeedSync 馈送提取项并将其应用于目标副本。有关更多信息,请参见使用 RSS 和 Atom 馈送。
请参阅
概念
同步 Web 馈送
生成 RSS 和 Atom 馈送
使用 RSS 和 Atom 馈送
为 RSS 和 Atom 馈送转换 ID 和项
Sync Framework Web 同步组件