Как создать неуправляемый производитель канала

В этом разделе показано, как написать на неуправляемом языке, например C++, приложение, которое будет использовать платформу Sync Framework для создания RSS-канала из списка файлов в папке. RSS-канал, создаваемый этим приложением, содержит один элемент для каждого файла в указанной папке. Каждый элемент в канале включает содержимое связанного файла, а также метаданные FeedSync об элементе.

В этом разделе предполагается, что читатель знаком с основными понятиями языка C++ и модели COM.

Примеры, приведенные в разделе, посвящены следующим компонентам веб-синхронизации платформы Sync Framework.

Основные сведения о производителях каналов

Производитель канала — это программный компонент, который создает канал FeedSync, содержащий элементы, переданные службой синхронизации. В приложении реализован интерфейс IFeedIdConverter для преобразования идентификаторов из формата службы в формат FeedSync, а также интерфейс IFeedItemConverter — для преобразования данных элементов из формата службы в формат FeedSync.

Дополнительные сведения о создании канала FeedSync см. в разделе Создание RSS- и Atom-каналов.

Дополнительные сведения о службах синхронизации см. в разделе Реализация стандартного пользовательского поставщика.

Требования построения

  • Synchronization.h, FeedSync.h, FileSyncProvider.h: объявления основных компонентов служб Sync Framework, компонентов веб-синхронизации и файла службы синхронизации.

    #include <Synchronization.h>
    #include <FeedSync.h>
    #include <FileSyncProvider.h>
    
  • Synchronization.lib, FeedSync.lib, FileSyncProvider.lib: библиотеки импорта.

Пример

В примере кода в этом разделе показано, как использовать объект IFeedProducer для создания RSS-канала, который содержит элементы, переданные объектом IFileSyncProvider. В примере также показано, как реализовать интерфейсы, которые преобразуют идентификаторы и данные элементов из формата службы синхронизации файлов в формат 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. Это строковое представление полностью совпадает с тем, которое было возвращено платформой Sync Framework в методе ConvertReplicaIdToString. Реализация в этом примере использует функцию 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. Это строковое представление полностью совпадает с тем, которое было возвращено платформой Sync Framework в методе ConvertItemIdToString. Реализация в этом примере использует функцию CRT wcstoui64, чтобы преобразовать часть идентификатора, занимаемую префиксом, из строки типа WCHAR в значение ULONGLONG. Также используется функция OLE32 CLSIDFromString, чтобы преобразовать часть идентификатора, занимаемую идентификатором GUID, из строки типа WCHAR в идентификатор GUID. В примере возвращается результирующий идентификатор, имеющий формат SYNC_GID, с помощью метода 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-текст. Метод ConvertItemDataToXmlText вызывается, когда метод IFeedItemConverter::ConvertItemDataToXml возвращает значение E_NOTIMPL. Реализация в этом примере предполагает, что передаваемые файлы содержат допустимый XML-код для элемента RSS-канала в формате Юникод. Далее представлен пример содержимого файла.

<item>
  <title>Sample</title>
  <description>A sample item.</description>
</item>

Объект IFileSyncProvider передает содержимое файла в виде объекта IFileDataRetriever. Реализация в этом примере получает объект IStream из объекта IFileDataRetriever и использует его для считывания содержимого файла в строку типа 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 в качестве службы, а папку в файловой системе — в качестве реплики. В примере кода для создания канала выполняются следующие действия.

  1. Создается объект IFileSyncProvider и настраивается путем указания папки для синхронизации и фильтра, который включает только TXT-файлы.

  2. Создается объект 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";
    
  3. Создается объект IFeedProducer и вызывается его метод IFeedProducer::ProduceFeed.

  4. Канал, который был записан в объект IStream платформами Sync Framework, записывается в файл в папке реплики.

В следующем коде создается канал.

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-каналов.

См. также

Другие ресурсы

Синхронизация веб-каналов

Создание RSS- и Atom-каналов

Использование данных из RSS- и Atom-каналов

Преобразование идентификаторов и элементов для RSS- и Atom-каналов

Компоненты веб-синхронизации Sync Framework