다음을 통해 공유


방법: 관리되지 않는 피드 공급자 만들기

이 항목에서는 C++ 등의 비관리 언어로 Sync Framework를 사용하여 폴더의 파일 목록에서 RSS 피드를 생성하는 응용 프로그램을 만드는 방법을 보여 줍니다. 이 응용 프로그램에서 생성하는 RSS 피드에는 지정된 폴더의 각 파일에 대한 항목이 들어 있습니다. 피드의 각 항목에는 연결된 파일 내용 및 항목에 대한 FeedSync 메타데이터가 들어 있습니다.

이 항목에서는 기본적인 C++ 및 COM 개념에 익숙하다고 가정합니다.

이 항목의 예제에서는 다음과 같은 Sync Framework 웹 동기화 구성 요소를 중점적으로 설명합니다.

피드 공급자 이해

피드 공급자는 동기화 공급자에서 제공하는 항목이 들어 있는 FeedSync 피드를 생성하는 소프트웨어 구성 요소입니다. 응용 프로그램에서는 IFeedIdConverter 인터페이스를 구현하여 ID를 공급자의 형식에서 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 개체를 사용하여 IFileSyncProvider 개체에서 제공하는 항목이 들어 있는 RSS 피드를 생성하는 방법을 보여 줍니다. 또한 ID와 항목 데이터를 File Synchronization Provider의 형식에서 FeedSync 형식으로 변환하는 인터페이스를 구현하는 방법을 보여 줍니다.

IFeedIdConverter 구현

공급자에서는 모든 형식의 ID를 사용할 수 있습니다. 따라서 Sync Framework를 사용하려면 응용 프로그램에서 ID를 공급자 형식과 FeedSync 사이에서 상호 변환하는 IFeedIdConverter 인터페이스를 구현해야 합니다.

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로 변환합니다. 이 예제에서는 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를 사용하려면 응용 프로그램에서 항목 데이터를 공급자 형식과 FeedSync 사이에서 상호 변환하는 IFeedItemConverter 인터페이스를 구현해야 합니다.

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이 유니코드 형식으로 들어 있을 것으로 예상합니다. 파일 내용의 예는 다음과 같습니다.

<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 개체를 공급자로, 파일 시스템의 폴더를 복제본으로 사용합니다. 예제 코드에서는 다음과 같은 단계에 따라 피드를 생성합니다.

  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. 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 피드 소비를 참조하십시오.

참고 항목

개념

웹 피드 동기화
RSS 및 Atom 피드 생성
RSS 및 Atom 피드 소비
RSS 및 Atom 피드의 ID와 항목 변환
Sync Framework 웹 동기화 구성 요소