HOW TO:建立 Unmanaged 摘要生產者
本主題示範如何使用 Unmanaged 語言 (如 C++) 建立應用程式,該應用程式會使用 Sync Framework,從資料夾中的檔案清單產生 RSS 摘要。由此應用程式產生的 RSS 摘要會針對指定之資料夾中的每個檔案各包含一個項目。摘要中每個項目都包含其相關聯檔案的內容,以及有關項目的 FeedSync 中繼資料。
本主題假設您對 C++ 和 COM 已有基本的了解。
本主題中的範例著重於下列 Sync Framework Web 同步處理元件:
了解摘要生產者
摘要生產者是產生 FeedSync 摘要的軟體元件,摘要中包含由同步處理提供者所提供的項目。應用程式會實作 IFeedIdConverter 介面,將識別碼從提供者的格式轉換成 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 摘要。此範例也會示範如何實作介面,將識別碼和項目資料從檔案同步處理提供者的格式轉換成 FeedSync 格式。
實作 IFeedIdConverter
提供者使用的識別碼可以是任何格式。因此,Sync Framework 需要應用程式實作 IFeedIdConverter 介面,將識別碼從提供者格式轉換成 FeedSync 格式,也進行逆向操作。
宣告 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,以取得提供者所使用的識別碼格式結構描述。本範例中的實作會傳回從 IFileSyncProvider
物件擷取的識別碼格式結構描述。如需擷取及存放結構描述的程式碼,請參閱本主題稍後的<產生 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,將複寫識別碼從提供者格式轉換成字串。識別碼的字串表示可以是任何形式,並逐字寫入摘要中。此範例中的實作使用 OLE32 函式 StringFromGUID2
,將複寫識別碼從 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,將項目識別碼從提供者格式轉換成字串。識別碼的字串表示可以是任何形式,並逐字寫入摘要中。此範例中的實作會將格式化為 SYNC_GID 結構的項目識別碼轉換成 WCHAR 字串。它使用 CRT 函式 _ui64tow_s
,將前置字元部分從 ULONGLONG 轉換成 WCHAR 字串,也使用 OLE32 函式 StringFromGUID2
,將識別碼的 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,將複寫識別碼從字串轉換成提供者格式。此字串表示與在 ConvertReplicaIdToString
方法中傳回給 Sync Framework 的字串表示完全相符。此範例中的實作使用 OLE32 函式 CLSIDFromString
,將複寫識別碼從 WCHAR 字串轉換成 GUID。它會使用 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;
}
ConvertStringToItemId 方法
Sync Framework 會呼叫 IFeedIdConverter::ConvertStringToItemId,將項目識別碼從字串轉換成提供者格式。此字串表示與在 ConvertItemIdToString
方法中傳回給 Sync Framework 的字串表示完全相符。此範例中的實作使用 CRT 函式 wcstoui64
,將識別碼的前置字元部分從 WCHAR 字串轉換成 ULONGLONG,也使用 OLE32 函式 CLSIDFromString
,將識別碼的 GUID 部分從 WCHAR 字串轉換成 GUID。此範例使用 IFeedIdConverterCallback::ConvertStringToItemIdComplete 方法,傳回格式化為 SYNC_GID
的結果識別碼。
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
。此範例中的實作預期正在生產的檔案會以 Unicode 格式包含 RSS 項目的有效 XML。以下是檔案內容的範例。
<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 摘要的識別碼和項目
Sync Framework Web 同步處理元件