アンマネージ フィード プロデューサーを作成する方法
このトピックでは、Sync Framework を使用して、フォルダー内のファイルの一覧から RSS フィードを作成するアプリケーションを、C++ などのアンマネージ言語で作成する方法について説明します。このアプリケーションによって作成される RSS フィードには、指定されたフォルダー内の各ファイルに対応する項目が含まれます。フィード内の各項目には、関連するファイルの内容のほか、項目に関する FeedSync のメタデータが含まれます。
このトピックは、C++ および COM の概念について基本的な知識がある方を対象としています。
このトピックの例では、次に示す Sync Framework Web 同期コンポーネントを中心に説明します。
フィード プロデューサーについて
フィード プロデューサーは、同期プロバイダーから提供された項目を含んだ FeedSync フィードを作成するソフトウェア コンポーネントです。プロバイダーと FeedSync では、ID の形式および項目データの形式が異なるため、両者間での変換処理が必要となります。アプリケーションでは、この処理を IFeedIdConverter インターフェイスと IFeedItemConverter を実装することにより実現します。
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: インポート ライブラリ。
例
このトピックのコード例では、IFileSyncProvider オブジェクトから提供された項目を含んだ RSS フィードを、IFeedProducer オブジェクトを使用して作成します。ファイル同期プロバイダーと FeedSync 間で、ID および項目データの形式を変換するインターフェイスの実装方法も紹介しています。
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 文字列に変換します。プレフィックス部分を ULONGLONG から WCHAR 文字列に変換するために、CRT 関数 _ui64tow_s
が使用されます。また、ID の GUID 部分を WCHAR 文字列に変換するために、OLE32 関数 StringFromGUID2
が使用されています。この例では、この 2 つの文字列を 1 つに連結し、その結果の文字列を 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 に変換しています。その結果の ID は、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 を呼び出して、項目 ID を文字列からプロバイダー形式に変換します。この文字列表現は、ConvertItemIdToString
メソッドで Sync Framework に返されたものです。この例の実装では、CRT 関数 wcstoui64
を使用して、ID のプレフィックス部分を WCHAR 文字列から ULONGLONG に変換しています。また、ID の GUID 部分を WCHAR 文字列から GUID に変換するために、OLE32 関数 CLSIDFromString
が使用されています。最終的に、SYNC_GID
形式の ID が、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;
}
実装されないメソッド
基本的なフィード プロデューサーのシナリオでは、次のメソッドは不要です。このメソッドからは、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
が呼び出されます。この例の実装では、作成中のファイルに、RSS 項目の有効な XML が、Unicode 形式で格納されます。たとえば、このファイルには、次のような内容が格納されます。
<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 同期コンポーネント