Freigeben über


Gewusst wie: Erstellen eines nicht verwalteten Synchronisierungsanbieters

In diesem Thema wird erläutert, wie Sie mit einer nicht verwalteten Sprache wie C++ einen Microsoft Sync Framework-Synchronisierungsanbieter erstellen, der Daten aus einem benutzerdefinierten Datenspeicher synchronisiert.

Dieses Thema setzt grundlegende Kenntnisse von C++ und COM-Konzepten voraus.

Die Beispiele in diesem Thema beziehen sich auf die folgenden Sync Framework-Schnittstellen:

Synchronisierungsanbieter

Ein Synchronisierungsanbieter ist eine Softwarekomponente, die bei der Synchronisierung ein Replikat darstellt. Dadurch können die Daten des Replikats mit anderen Replikaten synchronisiert werden. Um eine Synchronisierung durchzuführen, werden von einer Anwendung ein Synchronisierungssitzungsobjekt erstellt, Verbindungen zwischen diesem Objekt und zwei ISyncProvider-Objekten hergestellt und die Sitzung gestartet. Einer der Anbieter stellt das Quellreplikat dar. Das Quellreplikat stellt Metadaten für geänderte Elemente mit der IKnowledgeSyncProvider::GetChangeBatch-Methode und die Elementdaten mit einem ISynchronousDataRetriever-Objekt bereit. Der andere Anbieter stellt das Zielreplikat dar. Das Zielreplikat empfängt die Metadaten für geänderte Elemente mit der IKnowledgeSyncProvider::ProcessChangeBatch-Methode und übernimmt die Änderungen mithilfe eines von Sync Framework bereitgestellten ISynchronousChangeApplier-Objekts und dem eigenen ISynchronousChangeApplierTarget-Objekt in seinen Elementspeicher.

Weitere Informationen zur Rolle des Synchronisierungsanbieters finden Sie unter Synchronisierungsanbieter.

Erstellungsanforderungen

  • Synchronization.h: Deklarationen für Sync Framework-Komponenten

    #include <synchronization.h>
    
  • Synchronizationerrors.h: Benutzerdefinierte Fehlercodes

    #include <synchronizationerrors.h>
    
  • Synchronization.lib: Importbibliothek

Beispiel

Der Beispielcode in diesem Thema zeigt, wie Sie die grundlegenden Schnittstellenmethoden implementieren, die erforderlich sind, damit ein Replikat (sowohl als Quelle als auch als Ziel) an einer Sync Framework-Synchronisierungscommunity teilnehmen kann. In diesem Beispiel ist das Replikat eine XML-Datei und die zu synchronisierenden Elemente sind in der Datei enthaltene XML-Knoten. Im Code werden die XML-Knoten durch die IXMLDOMNode-Schnittstelle dargestellt. Außerdem wird in diesem Beispiel ein mithilfe der Metadatenspeicherdienst-API implementierter benutzerdefinierter Metadatenspeicher verwendet. Informationen zum Metadatenspeicherdienst und zu anderen Sync Framework-Komponenten finden Sie auf dieser Microsoft-Website.

Sowohl Metadatenspeicher als auch XML-Speicher werden als Elemente der Anbieterklasse deklariert.

CMetadataMgr* m_pMetadataMgr;
CItemStore* m_pItemStore;

Implementieren von 'ISyncProvider' und 'IKnowledgeSyncProvider'

Der Einstiegspunkt in den Anbieter ist die ISyncProvider-Schnittstelle. Diese Schnittstelle soll als Basisklasse für andere, leistungsstärkere Anbieterschnittstellen dienen. In diesem Beispiel wird die IKnowledgeSyncProvider-Schnittstelle verwendet.

Deklarieren von 'IKnowledgeSyncProvider'

Fügen Sie der Klassenvererbungsliste IKnowledgeSyncProvider hinzu.

class CXMLProvider : public IKnowledgeSyncProvider

Fügen Sie der Klassendeklaration die ISyncProvider-Methoden hinzu.

STDMETHOD(GetIdParameters)(
    ID_PARAMETERS * pIdParameters);

Fügen Sie der Klassendeklaration die IKnowledgeSyncProvider-Methoden hinzu.

STDMETHOD(BeginSession)(
    SYNC_PROVIDER_ROLE role,
    ISyncSessionState * pSessionState);

STDMETHOD(GetSyncBatchParameters)(
    ISyncKnowledge ** ppSyncKnowledge,
    DWORD * pdwRequestedBatchSize);

STDMETHOD(GetChangeBatch)(
    DWORD dwBatchSize,
    ISyncKnowledge * pSyncKnowledge,
    ISyncChangeBatch ** ppSyncChangeBatch,
    IUnknown ** ppUnkDataRetriever);
   
STDMETHOD(GetFullEnumerationChangeBatch)(
    DWORD dwBatchSize,
    const BYTE * pbLowerEnumerationBound,
    ISyncKnowledge * pSyncKnowledgeForDataRetrieval,
    ISyncFullEnumerationChangeBatch ** ppSyncChangeBatch,
    IUnknown ** ppUnkDataRetriever);

STDMETHOD(ProcessChangeBatch)(
    CONFLICT_RESOLUTION_POLICY resolutionPolicy,
    ISyncChangeBatch * pSourceChangeBatch,
    IUnknown * pUnkDataRetriever,
    ISyncCallback * pCallback,
    SYNC_SESSION_STATISTICS * pSyncSessionStatistics);

STDMETHOD(ProcessFullEnumerationChangeBatch)(
    CONFLICT_RESOLUTION_POLICY resolutionPolicy,
    ISyncFullEnumerationChangeBatch * pSourceChangeBatch,
    IUnknown * pUnkDataRetriever,
    ISyncCallback * pCallback,
    SYNC_SESSION_STATISTICS * pSyncSessionStatistics);

STDMETHOD(EndSession)(
    ISyncSessionState * pSessionState);

'GetIdParameters'-Methode

Sync Framework ruft beim Erstellen des ISyncSession-Objekts sowohl für den Quellen- als auch für den Zielanbieter ISyncProvider::GetIdParameters auf. Diese Methode gibt das vom Anbieter verwendete ID-Formatschema zurück. Beide Anbieter müssen dasselbe Schema verwenden. In der Implementierung dieses Beispiels wird eine globale Konstante verwendet, da sich die ID-Formate für den Anbieter nicht ändern.

const ID_PARAMETERS c_idParams = 
{
    sizeof(ID_PARAMETERS), // dwSize
    { FALSE, sizeof(GUID) }, // replicaId
    { FALSE, sizeof(SYNC_GID) }, // itemId
    { FALSE, 1 }, // changeUnitId
};

Mit einer globalen Konstante lässt sich diese Methode sehr einfach implementieren.

STDMETHODIMP CXMLProvider::GetIdParameters(
    ID_PARAMETERS * pIdParameters)
{
    if (NULL == pIdParameters)
    {
        return E_POINTER;
    }
    else
    {
        *pIdParameters = c_idParams;
        return S_OK;
    }
}

'BeginSession'-Methode

Anschließend ruft Sync Framework sowohl für den Quellen- als auch den Zielanbieter IKnowledgeSyncProvider::BeginSession auf. Mit dieser Methode wird ein Anbieter informiert, dass er einer Synchronisierungssitzung beitritt, und dem Anbieter wird ein Objekt mit Sitzungsstatusinformationen übergeben. In dieser Implementierung wird das Sitzungsstatusobjekt gespeichert.

STDMETHODIMP CXMLProvider::BeginSession(
    SYNC_PROVIDER_ROLE role,
    ISyncSessionState * pSessionState)
{
    HRESULT hr = E_UNEXPECTED;

    if (NULL == pSessionState)
    {
        hr = E_POINTER;
    }
    else
    {
        // This method should not be called twice.
        if (NULL != m_pSessionState || NULL == m_pMetadataMgr)
        {
            hr = SYNC_E_INVALID_OPERATION;
        }
        else
        {
            // Store the role and the session state object.
            m_role = role;

            pSessionState->AddRef();
            m_pSessionState = pSessionState;
            hr = S_OK;
        }
    }

    return hr;
}

'GetSyncBatchParameters'-Methode

Anschließend ruft Sync Framework für den Zielanbieter IKnowledgeSyncProvider::GetSyncBatchParameters auf. Mit dieser Methode wird die Anzahl an Änderungen, die der Quellenanbieter in einen Änderungsbatch einbeziehen sollte, und das aktuelle Wissen des Zielanbieters abgerufen. In der Implementierung wird das Wissen aus dem Metadatenspeicher extrahiert und die Batchgröße auf 10 festgelegt.

STDMETHODIMP CXMLProvider::GetSyncBatchParameters(
    ISyncKnowledge ** ppSyncKnowledge,
    DWORD * pdwRequestedBatchSize)
{
    HRESULT hr = E_UNEXPECTED;

    if (NULL == ppSyncKnowledge || NULL == pdwRequestedBatchSize)
    {
        hr = E_POINTER;
    }
    else
    {
        _ASSERT(NULL != m_pMetadataMgr);
    
        *pdwRequestedBatchSize = 10;

        hr = m_pMetadataMgr->GetKnowledge(ppSyncKnowledge);
    }

    return hr;
}

'GetChangeBatch'-Methode

Die eigentliche Synchronisierungssitzung startet, wenn Sync Framework für den Quellenanbieter IKnowledgeSyncProvider::GetChangeBatch aufruft. Mit dieser Methode wird ein Änderungsbatch abgerufen, der an den Zielanbieter gesendet wird. Außerdem wird die Datenabruferschnittstelle zurückgegeben. Mit dieser Schnittstelle ruft der Zielanbieter die Elementdaten der Änderungen ab, die für das Zielreplikat übernommen wurden. Sync Framework ruft wiederholt GetChangeBatch auf, bis der letzte Batch gesendet wurde. Der Quellenanbieter gibt durch Aufrufen der ISyncChangeBatchBase::SetLastBatch-Methode an, dass es sich bei einem Batch um den letzten Batch handelt. Das Auflisten der Änderungen wird in dieser Implementierung an die GetChangeBatch-Methode des Metadatenspeichers delegiert. Da das XML-Elementspeicherobjekt eine Datenabruferschnittstelle implementiert, wird die IUnknown-Schnittstelle zurückgegeben.

STDMETHODIMP CXMLProvider::GetChangeBatch(
    DWORD dwBatchSize,
    ISyncKnowledge * pSyncKnowledge,
    ISyncChangeBatch ** ppSyncChangeBatch,
    IUnknown ** ppUnkDataRetriever)
{
    HRESULT hr = E_UNEXPECTED;

    if (NULL == pSyncKnowledge || NULL == ppSyncChangeBatch || NULL == ppUnkDataRetriever)
    {
        hr = E_POINTER;
    }
    else
    {
        _ASSERT(NULL != m_pMetadataMgr);
        hr = m_pMetadataMgr->GetChangeBatch(dwBatchSize, pSyncKnowledge, ppSyncChangeBatch);
        if (SUCCEEDED(hr))
        {
            hr = m_pItemStore->QueryInterface(IID_IUnknown, (void**)ppUnkDataRetriever);
        }
    }

    return hr;
}

Die vom Metadatenspeicher implementierte GetChangeBatch-Methode listet die Elemente im Metadatenspeicher auf und überprüft die Version der Elemente anhand des Wissens des Ziels. Wenn dem Zielreplikat eine Änderung nicht bekannt ist, wird sie dem zurückzugebenden Änderungsbatch hinzugefügt.

STDMETHODIMP CMetadataMgr::GetChangeBatch(
    DWORD dwBatchSize,
    ISyncKnowledge *pSyncKnowledge,
    ISyncChangeBatch ** ppSyncChangeBatch)
{
    HRESULT hr = E_UNEXPECTED;

    ISyncChangeBatch* pChangeBatch = NULL;
    ISyncKnowledge* pMappedDestKnowledge = NULL;
    ISyncKnowledge* pSourceKnowledge = NULL;

    if (NULL == pSyncKnowledge || NULL == ppSyncChangeBatch)
    {
        hr = E_POINTER;
    }
    else
    {
        // Get our (source) knowledge object, map the remote (destination) knowledge for local use, 
        // and get our replica ID.
        GUID guidReplicaID;
        hr = GetKnowledge(&pSourceKnowledge);
        if (SUCCEEDED(hr))
        {
            hr = pSourceKnowledge->MapRemoteToLocal(pSyncKnowledge, &pMappedDestKnowledge);
            if (SUCCEEDED(hr))
            {
                ULONG cbID = sizeof(guidReplicaID);
                hr = GetReplicaId((BYTE*)&guidReplicaID, &cbID);
            }
        }

        if (SUCCEEDED(hr))
        {
            // Create a new change batch object.  We'll fill this object with changes to send.
            IProviderSyncServices* pProvSvc = NULL;
            // This helper function creates and initializes the IProviderSyncServices interface.
            hr = GetProviderSyncServices(&c_idParams, &pProvSvc);
            if (SUCCEEDED(hr))
            {
                hr = pProvSvc->CreateChangeBatch(pSyncKnowledge, NULL, &pChangeBatch);            

                pProvSvc->Release();
                pProvSvc = NULL;
            }
        }

        // Enumerate the items in our store and add new changes to the change batch.
        if (SUCCEEDED(hr))
        {
            // Begin an unordered group in our change batch. All change items will be added to this group.
            hr = pChangeBatch->BeginUnorderedGroup();
            if (SUCCEEDED(hr))
            {
                ULONG cFetched = 1;
                IItemMetadata* pItemMeta = NULL;
                SYNC_GID gidItem;
                ULONG cbgid = sizeof(gidItem);
                SYNC_VERSION verCur;
                SYNC_VERSION verCreate;
                hr = Reset();
                while (S_OK == hr)
                {
                    hr = Next(1, &pItemMeta, &cFetched);
                    if (S_OK == hr)
                    {
                        hr = pItemMeta->GetGlobalId((BYTE*)&gidItem, &cbgid);
                        if (SUCCEEDED(hr))
                        {
                            hr = pItemMeta->GetChangeVersion(&verCur);
                            if (SUCCEEDED(hr))
                            {
                                // Find out whether the destination already knows about this change.
                                hr = pMappedDestKnowledge->ContainsChange((BYTE*)&guidReplicaID,
                                    (BYTE*)&gidItem, &verCur);
                                if (S_FALSE == hr)
                                {
                                    // S_FALSE means the destination does not know about the 
                                    // change, so add it to the change batch.
                                    DWORD dwFlags = 0;
                                    BOOL fTomb = 0;
                                    hr = pItemMeta->GetIsDeleted(&fTomb);
                                    if (fTomb)
                                    {
                                        dwFlags = SYNC_CHANGE_FLAG_DELETED;                            
                                    }

                                    hr = pItemMeta->GetCreationVersion(&verCreate);
                                    if (SUCCEEDED(hr))
                                    {
                                        hr = pChangeBatch->AddItemMetadataToGroup((BYTE*)&guidReplicaID, 
                                            (BYTE*)&gidItem, &verCur, &verCreate, dwFlags, 0, NULL);
                                    }
                                }
                            }
                        }

                        pItemMeta->Release();
                    }
                }
            }

            if (SUCCEEDED(hr))
            {
                // We always send the entire set of changes, so every batch is the last batch. 
                // If this flag is not set Sync Framework will call GetChangeBatch again.
                hr = pChangeBatch->SetLastBatch();
            }

            if (SUCCEEDED(hr))
            {
                // Close the change batch group that contains our changes.
                hr = pChangeBatch->EndUnorderedGroup(pSourceKnowledge, TRUE);
            }
        }

        if (NULL != pChangeBatch)
        {
            if (SUCCEEDED(hr))
            {
                // Return the change batch we've constructed.  This will be sent to the 
                // destination provider.
                *ppSyncChangeBatch = pChangeBatch;
            }
            else
            {
                pChangeBatch->Release();            
            }
        }

        if (NULL != pMappedDestKnowledge)
        {
            pMappedDestKnowledge->Release();
        }
        if (NULL != pSourceKnowledge)
        {
            pSourceKnowledge->Release();
        }
    }

    return hr;
}

'ProcessChangeBatch'-Methode

Nachdem Sync Framework mit der GetChangeBatch-Methode einen Änderungsbatch vom Quellenanbieter abgerufen hat, ruft Sync Framework für den Zielanbieter IKnowledgeSyncProvider::ProcessChangeBatch auf. Diese Methode übernimmt die Änderungen in das Zielreplikat. Diese Methode wird für jeden Batch, der mit GetChangeBatch vom Quellenanbieter abgerufen wird, einmal aufgerufen. In dieser Implementierung werden mit der GetItemBatchVersions-Methode des Metadatenspeichers lokale Versionsinformationen für Elemente vom Quellenanbieter abgerufen. Anschließend wird ein von Sync Framework implementiertes ISynchronousNotifyingChangeApplier-Objekt erstellt und dessen ISynchronousNotifyingChangeApplier::ApplyChanges-Methode aufgerufen.

STDMETHODIMP CXMLProvider::ProcessChangeBatch(
    CONFLICT_RESOLUTION_POLICY resolutionPolicy,
    ISyncChangeBatch * pSourceChangeBatch,
    IUnknown * pUnkDataRetriever,
    ISyncCallback * pCallback,
    SYNC_SESSION_STATISTICS * pSyncSessionStatistics)
{
    HRESULT hr = E_UNEXPECTED;

    if (NULL == pSourceChangeBatch || NULL == pUnkDataRetriever || NULL == pSyncSessionStatistics)
    {
        hr = E_POINTER;
    }
    else
    {
        IEnumSyncChanges* pDestinationChangeEnum = NULL;

        _ASSERT(NULL != m_pMetadataMgr);

        // Obtain the local (destination) versions for the items in the source change batch.
        hr = m_pMetadataMgr->GetItemBatchVersions(pSourceChangeBatch, &pDestinationChangeEnum);
        if (SUCCEEDED(hr))
        {
            IProviderSyncServices* pProviderSvc = NULL;
            hr = GetProviderSyncServices(&c_idParams, &pProviderSvc);
            if (SUCCEEDED(hr))
            {
                // Create a standard change applier from Sync Framework.
                ISynchronousNotifyingChangeApplier* pChangeApplier = NULL;
                hr = pProviderSvc->CreateChangeApplier(IID_ISynchronousNotifyingChangeApplier,
                    (void**)&pChangeApplier);
                if (SUCCEEDED(hr))
                {
                    ISyncKnowledge* pDestinationKnowledge = NULL;
                    hr = m_pMetadataMgr->GetKnowledge(&pDestinationKnowledge);
                    if (SUCCEEDED(hr))
                    {
                        // Have the change applier process the change batch and apply changes.
                        // This method will call the change applier target methods to save
                        // changes and conflicts.  It will also pass the data retriever
                        // interface to the change applier target so it can retrieve item data.
                        hr = pChangeApplier->ApplyChanges(resolutionPolicy, pSourceChangeBatch, 
                            pUnkDataRetriever, pDestinationChangeEnum, pDestinationKnowledge, 
                            NULL, this, m_pSessionState, pCallback);
                        
                        pDestinationKnowledge->Release();
                    }

                    pChangeApplier->Release();
                }

                pProviderSvc->Release();
            }

            pDestinationChangeEnum->Release();
        }
    }

    return hr;
}

Die vom Quellenanbieter im Änderungsbatch gesendeten Änderungen werden mit der GetItemBatchVersions-Methode des Metadatenspeichers aufgelistet. Wenn ein Element in den Zielmetadaten vorhanden ist, werden dessen Versionsinformationen einem neuen Batch hinzugefügt, der ausdrücklich zum Speichern der Versionsinformationen erstellt wird. Ein in den Zielmetadaten nicht vorhandenes Element wird im Versionsbatch als neues Element gekennzeichnet. Anschließend wird der Versionsbatch von der Methode zurückgegeben.

STDMETHODIMP CMetadataMgr::GetItemBatchVersions(
    ISyncChangeBatch * pRemoteSyncChangeBatch,
    IEnumSyncChanges ** ppLocalVersionsEnum)
{
    HRESULT hr = E_UNEXPECTED;

    if (NULL == pRemoteSyncChangeBatch || NULL == ppLocalVersionsEnum)
    {
        hr = E_POINTER;
    }
    else
    {
        IProviderSyncServices* pProvSvc;
        hr = GetProviderSyncServices(&c_idParams, &pProvSvc);
        if (SUCCEEDED(hr))
        {
            IDestinationChangeVersionsBuilder* pDestChangeBuilder = NULL;
            hr = pProvSvc->CreateDestinationChangeVersionsBuilder(&pDestChangeBuilder);
            if (SUCCEEDED(hr))
            {
                IEnumSyncChanges* pRemoteEnum = NULL;
                hr = pRemoteSyncChangeBatch->GetChangeEnumerator(&pRemoteEnum);
                if (SUCCEEDED(hr))
                {
                    ULONG cFetched;

                    ISyncChange* pChange;
                    SYNC_GID gidItem;
                    DWORD cbID = sizeof(gidItem);
                    DWORD dwFlags;
                    SYNC_VERSION verCurrent;
                    SYNC_VERSION verCreation;
                    HRESULT hrEnum = S_OK;
                    while (S_OK == hrEnum && SUCCEEDED(hr))
                    {
                        pChange = NULL;
                        hrEnum = pRemoteEnum->Next(1, &pChange, &cFetched);
                        if (S_OK == hrEnum)
                        {
                            hr = pChange->GetRootItemId((BYTE*)&gidItem, &cbID);
                            if (SUCCEEDED(hr))
                            {
                                // Try to find the item in the local (destination) metadata.
                                IItemMetadata* pItem = NULL;
                                hr = FindItemMetadataByGlobalId((BYTE*)&gidItem, &pItem);
                                if (S_OK == hr)
                                {
                                    // S_OK means the item exists in our local store.
                                    // Extract its version and tombstone information.
                                    dwFlags = 0;

                                    BOOL fTombstone = FALSE;
                                    hr = pItem->GetIsDeleted(&fTombstone);
                                    if (SUCCEEDED(hr))
                                    {
                                        if (fTombstone)
                                        {
                                            dwFlags = SYNC_CHANGE_FLAG_DELETED;
                                        }
                                    }

                                    if (SUCCEEDED(hr))
                                    {
                                        hr = pItem->GetChangeVersion(&verCurrent);
                                        if (SUCCEEDED(hr))
                                        {
                                            hr = pItem->GetCreationVersion(&verCreation);                                            
                                        }
                                    }

                                    pItem->Release();
                                }
                                else if (S_FALSE == hr)
                                {
                                    // S_FALSE means this item does not exist in our local store.
                                    // Set versions to 0 and flag it as a new item.
                                    verCurrent.dwLastUpdatingReplicaKey = 0;
                                    verCurrent.ullTickCount = 0;
                                    verCreation.dwLastUpdatingReplicaKey = 0;
                                    verCreation.ullTickCount = 0;
                                    dwFlags = SYNC_CHANGE_FLAG_DOES_NOT_EXIST;
                                }

                                if (SUCCEEDED(hr))
                                {
                                    // Add the item to the batch of destination versions.
                                    GUID guidReplicaID = GUID_NULL;
                                    ULONG cbID = sizeof(guidReplicaID);
                                    hr = GetReplicaId((BYTE*)&guidReplicaID, &cbID);
                                    if (SUCCEEDED(hr))
                                    {
                                        hr = pDestChangeBuilder->AddItemMetadata((BYTE*)&guidReplicaID,
                                            (BYTE*)&gidItem, &verCurrent, &verCreation, dwFlags, NULL);
                                    }
                                }
                            }

                            pChange->Release();
                        }
                    }

                    if (FAILED(hrEnum))
                    {
                        hr = hrEnum;                    
                    }

                    pRemoteEnum->Release();                
                }

                if (SUCCEEDED(hr))
                {
                    hr = pDestChangeBuilder->GetChangeEnumerator(ppLocalVersionsEnum);               
                }

                pDestChangeBuilder->Release();
            }

            pProvSvc->Release();        
        }
    }

    return hr;
}

'EndSession'-Methode

Nachdem der Quellenanbieter den letzten Batch gesendet und der Zielanbieter die Änderungen in den Datenspeicher übernommen hat, ruft Sync Framework sowohl für den Quellen- als auch für den Zielanbieter IKnowledgeSyncProvider::EndSession auf. Mit dieser Methode wird ein Anbieter informiert, dass er eine Synchronisierungssitzung verlässt und alle zur Sitzung gehörenden Ressourcen freigeben sollte. In dieser Implementierung wird das im BeginSession-Aufruf gespeicherte Sitzungsstatusobjekt freigegeben.

STDMETHODIMP CXMLProvider::EndSession(
    ISyncSessionState * pSessionState)
{
    HRESULT hr = E_UNEXPECTED;

    if (NULL == m_pSessionState)
    {
        hr = SYNC_E_INVALID_OPERATION;
    }
    else
    {
        m_pSessionState->Release();
        m_pSessionState = NULL;
        hr = S_OK;
    }

    return hr;
}

Nicht implementierte Methoden

Die folgenden Methoden sind nicht erforderlich, da in diesem Beispiel keine im Metadatenspeicher als gelöscht markierten Elemente entfernt werden. Diese Methoden können E_NOTIMPL zurückgeben:

Implementieren von 'ISynchronousNotifyingChangeApplierTarget'

Diese Schnittstelle wird für Sync Framework bereitgestellt, wenn der Zielanbieter die ISynchronousNotifyingChangeApplier::ApplyChanges-Methode aufruft. Dies geschieht in der Regel in der ProcessChangeBatch-Methode. ISynchronousNotifyingChangeApplierTarget enthält Methoden, die bei der Übernahme von Änderungen aufgerufen werden. Diese Methoden werden nur für den Zielanbieter aufgerufen.

Deklarieren von 'ISynchronousNotifyingChangeApplierTarget'

Fügen Sie der Klassenvererbungsliste ISynchronousNotifyingChangeApplierTarget hinzu.

class CXMLProvider : public IKnowledgeSyncProvider
    , ISynchronousNotifyingChangeApplierTarget

Fügen Sie der Klassendeklaration die ISynchronousNotifyingChangeApplierTarget-Methoden hinzu.

STDMETHOD(GetDataRetriever)(
    IUnknown ** ppDataRetriever);

STDMETHOD(GetCurrentTickCount)(
    ULONGLONG * pTickCount);

STDMETHOD(GetDestinationVersion)(
    ISyncChange * pSourceChange,
    ISyncChange ** ppDestinationVersion);

STDMETHOD(SaveChange)(
    SYNC_SAVE_ACTION  ssa,
    ISyncChange * pChange,
    ISaveChangeContext * pSaveContext);

STDMETHOD(SaveChangeWithChangeUnits)(
    ISyncChange * pChange,
    ISaveChangeWithChangeUnitsContext * pSaveContext);

STDMETHOD(SaveConflict)(
    ISyncChange * pChange,
    IUnknown * pUnkData,
    ISyncKnowledge * pConflictKnowledge);

STDMETHOD(SaveKnowledge)(
    ISyncKnowledge * pSyncKnowledge,
    IForgottenKnowledge * pForgottenKnowledge);

'GetIdParameters'-Methode

Sync Framework ruft ISynchronousNotifyingChangeApplierTarget::GetIdParameters auf, um das ID-Formatschema des Anbieters abzurufen. In diesem Beispiel werden IKnowledgeSyncProvider und ISynchronousNotifyingChangeApplierTarget in derselben Klasse implementiert. Daher stimmt diese Implementierung mit derjenigen für ISyncProvider::GetIdParameters überein.

STDMETHODIMP CXMLProvider::GetIdParameters(
    ID_PARAMETERS * pIdParameters)
{
    if (NULL == pIdParameters)
    {
        return E_POINTER;
    }
    else
    {
        *pIdParameters = c_idParams;
        return S_OK;
    }
}

GetCurrentTickCount

Sync Framework ruft ISynchronousNotifyingChangeApplierTarget::GetCurrentTickCount auf, um die Taktanzahl für das Replikat zu inkrementieren und abzurufen. In dieser Implementierung wird die GetNextTickCount-Methode des Metadatenspeichers aufgerufen.

STDMETHODIMP CXMLProvider::GetCurrentTickCount(
    ULONGLONG * pTickCount)
{
    _ASSERT(NULL != m_pMetadataMgr);
    return m_pMetadataMgr->GetNextTickCount(pTickCount);
}

Die GetNextTickCount-Methode des Metadatenspeichers inkrementiert die Taktanzahl des Replikats und gibt diese zurück.

STDMETHODIMP CMetadataMgr::GetNextTickCount(
     ULONGLONG * pNextTickCount)
{
    HRESULT hr = E_UNEXPECTED;

    if (NULL == pNextTickCount)
    {
        hr = E_POINTER;
    }
    else
    {
        // Get the local tick count, increment it, store it, and return it.
        ULONGLONG ullTickCount = -1;
        hr = GetTickCount(&ullTickCount);
        if (SUCCEEDED(hr))
        {
            ++ullTickCount;
            hr = SetTickCount(ullTickCount);
            if (SUCCEEDED(hr))
            {
                *pNextTickCount = ullTickCount;            
            }
        }
    }

    return hr;
}

SaveChange

Bei der Übernahme von Änderungen ruft Sync Framework für jede in das Zielreplikat zu übernehmende Änderung ISynchronousNotifyingChangeApplierTarget::SaveChange auf. In dieser Implementierung werden neue Elemente, geänderte Elemente und gelöschte Elemente ordnungsgemäß verarbeitet. Außerdem werden sowohl die Elementdaten im Elementspeicher als auch die Elementmetadaten im Metadatenspeicher aktualisiert.

STDMETHODIMP CXMLProvider::SaveChange(
    SYNC_SAVE_ACTION ssa,
    ISyncChange * pChange,
    ISaveChangeContext * pSaveContext)
{
    HRESULT hr = E_UNEXPECTED;

    _ASSERT(NULL != m_pItemStore);

    if (NULL == pChange || NULL == pSaveContext)
    {
        hr = E_POINTER;
    }
    else
    {
        // First save or delete the item data itself.
        switch (ssa)
        {
        case SSA_DELETE_AND_REMOVE_TOMBSTONE:
        {
            // This sample does not track forgotten knowledge and so cannot properly
            // handle this action.
            hr = E_UNEXPECTED;
            break;
        }

        case SSA_CREATE:
        case SSA_UPDATE_VERSION_AND_DATA:
        case SSA_UPDATE_VERSION_AND_MERGE_DATA:
        {
            // Save the item in the data store.

            // This IUnknown interface is the interface returned by the data retriever's
            // LoadChangeData method.
            IUnknown* pUnk = NULL;
            hr = pSaveContext->GetChangeData(&pUnk);
            if (S_OK == hr)
            {
                // The item is an XML node.
                IXMLDOMNode* pNode = NULL;
                hr = pUnk->QueryInterface(__uuidof(pNode), (void**)&pNode);
                if (SUCCEEDED(hr))
                {
                    // Have the data store save the item.
                    hr = m_pItemStore->SaveItem(pChange, pNode);

                    pNode->Release();
                }

                pUnk->Release();
            }

            break;
        }

        case SSA_DELETE_AND_STORE_TOMBSTONE:
        {
            // Delete the item from the data store.
            hr = m_pItemStore->DeleteItem(pChange);
        }
            break;

        case SSA_UPDATE_VERSION_ONLY:
        {
            // Update the version only, so nothing to do in the data store.
            hr = S_OK;
        }
            break;

        default:
            hr = E_INVALIDARG;
        }

        // Now update the metadata for the item in the metadata store.
        if (SUCCEEDED(hr))
        {
            SYNC_GID gidItem;
            DWORD cbItemID = sizeof(gidItem);
            hr = pChange->GetRootItemId((BYTE*)&gidItem, &cbItemID);
            if (SUCCEEDED(hr))
            {
                // Save the item metadata to the metadata store.
                // First extract the information from the change.
                GUID guidReplicaID;
                ULONG cbReplicaID = sizeof(guidReplicaID);
                hr = m_pMetadataMgr->GetReplicaId((BYTE*)&guidReplicaID, &cbReplicaID);
                if (SUCCEEDED(hr))
                {
                    SYNC_VERSION verCurrent;
                    hr = pChange->GetChangeVersion((BYTE*)&guidReplicaID, &verCurrent);
                    if (SUCCEEDED(hr))
                    {
                        SYNC_VERSION verCreation;
                        hr = pChange->GetCreationVersion((BYTE*)&guidReplicaID, &verCreation);
                        if (SUCCEEDED(hr))
                        {
                            DWORD dwFlags;
                            hr = pChange->GetFlags(&dwFlags);
                            if (SUCCEEDED(hr))
                            {
                                // Try to find the item in the metadata store.
                                IItemMetadata* pItem = NULL;
                                hr = m_pMetadataMgr->FindItemMetadataByGlobalId((BYTE*)&gidItem, 
                                    &pItem);
                                if (S_FALSE == hr)
                                {
                                    // S_FALSE means the item does not exist in the metadata store.
                                    // Therefore it must be a new item.  Create it and set its
                                    // creation version.
                                    hr = m_pMetadataMgr->CreateNewItemMetadata(&pItem);
                                    if (SUCCEEDED(hr))
                                    {
                                        hr = pItem->SetGlobalId((BYTE*)&gidItem);
                                        if (SUCCEEDED(hr))
                                        {
                                            hr = pItem->SetCreationVersion(&verCreation);
                                        }
                                    }
                                }

                                // Set the item's change version and tombstone status.
                                if (SUCCEEDED(hr))
                                {
                                    if (dwFlags & SYNC_CHANGE_FLAG_DELETED)
                                    {
                                        hr = pItem->MarkAsDeleted(&verCurrent);
                                    }
                                    else
                                    {
                                        hr = pItem->SetChangeVersion(&verCurrent);
                                    }
                                }

                                // Commit the item change and update the knowledge.
                                if (SUCCEEDED(hr))
                                {
                                    hr = m_pMetadataMgr->SaveItemMetadata(pItem);
                                    if (SUCCEEDED(hr))
                                    {
                                        ISyncKnowledge* pUpdatedKnowledge = NULL;
                                        IForgottenKnowledge* pUpdatedForgottenKnowledge = NULL;
                                        hr = pSaveContext->GetKnowledgeForScope(&pUpdatedKnowledge, &pUpdatedForgottenKnowledge);
                                        if (SUCCEEDED(hr))
                                        {
                                            hr = m_pMetadataMgr->SetKnowledge(pUpdatedKnowledge);

                                            pUpdatedKnowledge->Release();

                                            if (NULL != pUpdatedForgottenKnowledge)
                                            {
                                                // This sample does not use forgotten knowledge, so it is an error to receive
                                                // forgotten knowledge from the save context.
                                                hr = E_UNEXPECTED;

                                                pUpdatedForgottenKnowledge->Release();
                                            }
                                        }
                                    }
                                }

                                if (NULL != pItem)
                                {
                                    pItem->Release();                                    
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    return hr;
}

SaveKnowledge

Nach dem Verarbeiten der einzelnen Änderungsbatches ruft Sync Framework ISynchronousNotifyingChangeApplierTarget::SaveKnowledge auf, damit der Zielanbieter das Wissen mit den neuen Änderungen speichern kann. In dieser Implementierung wird das Wissensobjekt im Metadatenspeicher gespeichert und zuvor vorhandenes Wissen überschrieben.

STDMETHODIMP CXMLProvider::SaveKnowledge(
    ISyncKnowledge * pSyncKnowledge,
    IForgottenKnowledge * pForgottenKnowledge)
{
    HRESULT hr = E_UNEXPECTED;

    _ASSERT(NULL != m_pMetadataMgr);

    if (NULL == pSyncKnowledge)
    {
        hr = E_POINTER;    
    }
    else if (NULL != pForgottenKnowledge)
    {
        // This sample does not support forgotten knowledge, so it is an error to receive it in this method.bb
        hr = E_INVALIDARG;
    }
    else
    {
        hr = m_pMetadataMgr->SetKnowledge(pSyncKnowledge);
    }
    
    return hr;
}

Nicht implementierte Methoden

Die folgenden Methoden sind für grundlegende Synchronisierungsszenarios nicht erforderlich und können nur E_NOTIMPL zurückgeben:

Implementieren von 'ISynchronousDataRetriever'

Als Antwort auf den GetChangeBatch-Aufruf wird vom Quellenanbieter ISynchronousDataRetriever an Sync Framework zurückgegeben. Im ProcessChangeBatch-Aufruf wird ISynchronousDataRetriever an den Zielanbieter gesendet. Dort wird sie in der Regel an die ApplyChanges-Methode eines Änderungsanwenders weitergegeben. Der Änderungsanwender ruft dann ISynchronousDataRetriever::LoadChangeData auf, um eine IUnknown-Schnittstelle zu erhalten, die die Elementdaten darstellt. Der Änderungsanwender übergibt diese Schnittstelle an die SaveChange-Methode des Zielanbieters. Der Zielanbieter ruft mit dieser IUnknown-Schnittstelle Elementdaten für neue oder geänderte Elemente ab und übernimmt die Elementdaten in das Zielreplikat.

Deklarieren von 'ISynchronousDataRetriever'

Fügen Sie der Klassenvererbungsliste ISynchronousDataRetriever hinzu.

class CItemStore : public ISynchronousDataRetriever

Fügen Sie der Klassendeklaration die ISynchronousDataRetriever-Methoden hinzu.

STDMETHOD(GetIdParameters)(
    ID_PARAMETERS * pIdParameters);

STDMETHOD(LoadChangeData)(
    ILoadChangeContext * pLoadChangeContext,
    IUnknown ** ppUnkData);

'GetIdParameters'-Methode

Sync Framework ruft ISynchronousDataRetriever::GetIdParameters auf, um das ID-Formatschema des Anbieters abzurufen. Diese Implementierung entspricht im Grunde derjenigen für ISyncProvider::GetIdParameters.

STDMETHODIMP CItemStore::GetIdParameters(
    ID_PARAMETERS * pIdParameters)
{
    if (NULL == pIdParameters)
    {
        return E_POINTER;
    }
    else
    {
        *pIdParameters = c_idParams;
        return S_OK;
    }
}

'LoadChangeData'-Methode

Bei der Übernahme von Änderungen ruft Sync Framework ISynchronousDataRetriever::LoadChangeData auf, um eine IUnknown-Schnittstelle zu erhalten, mit der der Zielanbieter Elementdaten abrufen kann. In dieser Implementierung wird das Element im Elementspeicher gesucht, geklont und seine IUnknown-Schnittstelle zurückgegeben.

STDMETHODIMP CItemStore::LoadChangeData(
    ILoadChangeContext * pLoadChangeContext,
    IUnknown ** ppUnkData)
{
    HRESULT hr = E_UNEXPECTED;

    if (NULL == pLoadChangeContext || NULL == ppUnkData)
    {
        hr = E_POINTER;    
    }
    else
    {
        // Find the item in the data store, clone it, and return its IUnknown interface.
        ISyncChange* pChange = NULL;
        hr = pLoadChangeContext->GetSyncChange(&pChange);
        if (SUCCEEDED(hr))
        {
            SYNC_GID gidItem;
            DWORD cbID = sizeof(gidItem);
            hr = pChange->GetRootItemId((BYTE*)&gidItem, &cbID);
            if (SUCCEEDED(hr))
            {
                IXMLDOMNode* pNodeItem = NULL;
                hr = FindItem(&gidItem, &pNodeItem);
                if (SUCCEEDED(hr))
                {
                    IXMLDOMNode* pNodeClone = NULL;
                    hr = pNodeItem->cloneNode(TRUE, &pNodeClone);
                    if (SUCCEEDED(hr))
                    {
                        hr = pNodeClone->QueryInterface(IID_IUnknown, (void**)ppUnkData);

                        pNodeClone->Release();
                    }

                    pNodeItem->Release();                
                }
            }

            pChange->Release();
        }
    }

    return hr;
}

Nächste Schritte

Nachdem Sie nun einen Synchronisierungsanbieter erstellt haben, können Sie eine Anwendung zum Hosten der Synchronisierungssitzung und zum Herstellen einer Verbindung mit dem Anbieter erstellen. Weitere Informationen hierzu finden Sie unter Gewusst wie: Erstellen einer nicht verwalteten Synchronisierungsanwendung.

Weiterhin könnten Sie den Anbieter so erweitern, dass dieser Änderungseinheiten verarbeiten kann. Weitere Informationen zu Änderungseinheiten finden Sie unter Synchronisieren von Änderungseinheiten.

Sie können auch einen benutzerdefinierten Metadatenspeicher erstellen. Weitere Informationen zum Verarbeiten von Synchronisierungsmetadaten finden Sie unter Metadatenverwaltung.

Siehe auch

Verweis

ISyncProvider-Schnittstelle
IKnowledgeSyncProvider-Schnittstelle
ISynchronousNotifyingChangeApplierTarget-Schnittstelle
ISynchronousDataRetriever-Schnittstelle
ID_PARAMETERS-Struktur
ISynchronousNotifyingChangeApplier-Schnittstelle

Konzepte

Synchronisierungsanbieter
Kernkomponenten von Synchronization Framework