Share via


Procédure : créer un fournisseur de synchronisation non managé

Cette rubrique explique comment utiliser un langage non managé, tel que C++, pour créer un fournisseur de synchronisation Microsoft Sync Framework qui synchronise les données à partir d'une banque de données personnalisée.

Cette rubrique suppose une connaissance de base de C++ et des concepts COM.

Les exemples de cette rubrique reposent sur les interfaces Sync Framework suivantes :

Présentation des fournisseurs de synchronisation

Un fournisseur de synchronisation est un composant logiciel qui représente un réplica lors de la synchronisation. Cela permet au réplica de synchroniser ses données avec d'autres réplicas. Afin que la synchronisation ait lieu, une application crée en premier un objet de session de synchronisation, le connecte à deux objets ISyncProvider et démarre la session. L'un des fournisseurs représente le réplica source. Le réplica source fournit les métadonnées pour les éléments modifiés via sa méthode IKnowledgeSyncProvider::GetChangeBatch et les données d'élément via un objet ISynchronousDataRetriever. L'autre fournisseur représente le réplica de destination. Le réplica de destination reçoit les métadonnées pour les éléments modifiés via sa méthode IKnowledgeSyncProvider::ProcessChangeBatch et applique les modifications à son magasin d'éléments en utilisant un objet ISynchronousChangeApplier fourni par Sync Framework avec son propre objet ISynchronousChangeApplierTarget.

Pour plus d'informations sur le rôle du fournisseur de synchronisation, consultez Fournisseurs de synchronisation.

Configuration de génération

  • Synchronization.h : déclarations pour les composants Sync Framework.

    #include <synchronization.h>
    
  • Synchronizationerrors.h : codes d'erreur personnalisés.

    #include <synchronizationerrors.h>
    
  • Synchronization.lib : bibliothèque d'importation.

Exemple

L'exemple de code de cette rubrique indique comment implémenter les méthodes d'interface de base requises afin qu'un réplica participe à une communauté de synchronisation Sync Framework, à la fois comme une source et comme une destination. Le réplica dans cet exemple est un fichier XML, et les éléments à synchroniser sont des nœuds XML contenus dans ce fichier. Dans le code, les nœuds XML sont représentés par l'interface IXMLDOMNode. Cet exemple utilise également une banque de métadonnées personnalisée implémentée en utilisant l'API Metadata Storage Service. Pour plus d'informations sur Metadata Storage Service et d'autres composants Sync Framework, consultez ce site Web de Microsoft.

La banque de métadonnées et le magasin XML sont déclarés comme membres de la classe de fournisseur.

CMetadataMgr* m_pMetadataMgr;
CItemStore* m_pItemStore;

Implémentation d'ISyncProvider et d'IKnowledgeSyncProvider

Le point d'entrée dans le fournisseur est l'interface ISyncProvider. Cette interface est conçue pour fonctionner en tant que classe de base pour d'autres interfaces de fournisseur plus puissantes. Cet exemple utilise l'interface IKnowledgeSyncProvider.

Déclaration de IKnowledgeSyncProvider

Ajoutez IKnowledgeSyncProvider à la liste d'héritage de classe.

class CXMLProvider : public IKnowledgeSyncProvider

Ajoutez les méthodes ISyncProvider à la déclaration de classe.

STDMETHOD(GetIdParameters)(
    ID_PARAMETERS * pIdParameters);

Ajoutez les méthodes IKnowledgeSyncProvider à la déclaration de classe.

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);

Méthode GetIdParameters

Sync Framework appelle ISyncProvider::GetIdParameters sur les fournisseurs de source et de destination lorsque l'objet ISyncSession est créé. Cette méthode retourne le schéma du format de l'ID utilisé par le fournisseur. Ce schéma doit être le même pour les deux fournisseurs. L'implémentation dans cet exemple utilise une constante globale car les formats d'ID sont constants pour le fournisseur.

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

L'utilisation d'une constante globale facilite l'implémentation de cette méthode.

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

Méthode BeginSession

Sync Framework appelle ensuite IKnowledgeSyncProvider::BeginSession sur les fournisseurs de source et de destination. Cette méthode informe un fournisseur qu'il rejoint une session de synchronisation et passe au fournisseur un objet qui contient des informations d'état de session. Cette implémentation stocke l'objet d'état de session.

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;
}

Méthode GetSyncBatchParameters

Sync Framework appelle ensuite IKnowledgeSyncProvider::GetSyncBatchParameters sur le fournisseur de destination. Cette méthode récupère le nombre de modifications que le fournisseur de source doit inclure dans un lot de modifications et obtient la connaissance actuelle du fournisseur de destination. L'implémentation récupère la connaissance de la banque de métadonnées et définit la taille de lot à 10.

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;
}

Méthode GetChangeBatch

La session de synchronisation démarre vraiment lorsque Sync Framework appelle IKnowledgeSyncProvider::GetChangeBatch sur le fournisseur de source. Cette méthode récupère un lot de modifications à envoyer au fournisseur de destination et retourne également l'interface d'extracteur de données. Le fournisseur de destination utilise cette interface pour récupérer des données d'élément pour les modifications appliquées au réplica de destination. Sync Framework appelle GetChangeBatch à plusieurs reprises jusqu'à ce que le dernier lot soit envoyé. Le fournisseur de source indique qu'un lot est le dernier en appelant la méthode ISyncChangeBatchBase::SetLastBatch. Cette implémentation délègue la tâche d'énumération de la modification à la méthode GetChangeBatch de la banque de métadonnées. L'objet du magasin de l'élément XML implémente une interface d'extracteur de données ; par conséquent, son interface IUnknown est retournée.

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;
}

La méthode GetChangeBatch implémentée par la banque de métadonnées énumère les éléments dans la banque de métadonnées et vérifie la version de chacun d'eux par rapport à la connaissance de la destination. Si le réplica de destination n'est pas au courant d'une modification, la modification est ajoutée au lot de modifications retourné.

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;
}

Méthode ProcessChangeBatch

Après Sync Framework avoir obtenu un lot de modifications du fournisseur de source en appelant sa méthode GetChangeBatch, Sync Framework appelle IKnowledgeSyncProvider::ProcessChangeBatch sur le fournisseur de destination. Cette méthode applique les modifications au réplica de destination. Cette méthode est appelée une fois pour chaque lot récupéré en utilisant GetChangeBatch du fournisseur de source. Cette implémentation utilise la méthode GetItemBatchVersions de la banque de métadonnées pour obtenir des informations de version locales pour les éléments du fournisseur de source. Il crée ensuite un objet ISynchronousNotifyingChangeApplier implémenté par Sync Framework et appelle sa méthode ISynchronousNotifyingChangeApplier::ApplyChanges.

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;
}

La méthode GetItemBatchVersions de la banque de métadonnées énumère les modifications envoyées dans le lot de modifications du fournisseur de source. Si un élément se trouve dans les métadonnées de destination, ses informations de version sont ajoutées à un nouveau lot créé spécialement pour contenir les informations de version. Si un élément n'existe pas dans les métadonnées de destination, il est signalé comme un nouvel élément dans le lot de version. La méthode retourne alors le lot de version.

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;
}

Méthode EndSession

Une fois que le fournisseur de source a envoyé son dernier lot et que le fournisseur de destination a appliqué les modifications à sa banque de données, Sync Framework appelle IKnowledgeSyncProvider::EndSession sur les fournisseurs de source et de destination. Cette méthode informe un fournisseur qu'il quitte une session de synchronisation et qu'il doit libérer toutes les ressources associées à la session. Cette implémentation libère l'objet d'état de session qu'il a stocké dans l'appel BeginSession.

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;
}

Méthodes qui ne sont pas implémentées

Les méthodes suivantes ne sont pas requises car cet exemple ne supprime jamais des éléments marqués comme supprimés dans la banque de métadonnées. Ces méthodes peuvent retourner E_NOTIMPL :

Implémentation d'ISynchronousNotifyingChangeApplierTarget

Cette interface est fournie à Sync Framework lorsque le fournisseur de destination appelle la méthode ISynchronousNotifyingChangeApplier::ApplyChanges, en général dans la méthode ProcessChangeBatch. ISynchronousNotifyingChangeApplierTarget contient des méthodes appelées lors de l'application des modifications. Ces méthodes sont appelées uniquement sur le fournisseur de destination.

Déclaration d'ISynchronousNotifyingChangeApplierTarget

Ajoutez ISynchronousNotifyingChangeApplierTarget à votre liste d'héritage de classe.

class CXMLProvider : public IKnowledgeSyncProvider
    , ISynchronousNotifyingChangeApplierTarget

Ajoutez les méthodes ISynchronousNotifyingChangeApplierTarget à votre déclaration de classe.

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);

Méthode GetIdParameters

Sync Framework appelle ISynchronousNotifyingChangeApplierTarget::GetIdParameters pour récupérer le schéma du format de l'ID du fournisseur. Cet exemple utilise la même classe pour implémenter IKnowledgeSyncProvider et ISynchronousNotifyingChangeApplierTarget. Par conséquent, cette implémentation est la même pour ISyncProvider::GetIdParameters.

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

Méthode GetCurrentTickCount

Sync Framework appelle ISynchronousNotifyingChangeApplierTarget::GetCurrentTickCount pour incrémenter et récupérer le nombre de cycles pour le réplica. Cette implémentation appelle la méthode GetNextTickCount de la banque de métadonnées.

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

La méthode GetNextTickCount de la banque de métadonnées incrémente le nombre de cycles du réplica et le retourne.

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

Pendant l'application des modifications, Sync Framework appelle ISynchronousNotifyingChangeApplierTarget::SaveChange pour chaque modification qui sera appliquée au réplica de destination. Cette implémentation gère correctement les nouveaux éléments, les éléments modifiés et les éléments supprimés et met à jour à la fois les données d'élément dans le magasin d'éléments et les métadonnées d'élément dans la banque de métadonnées.

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

Après avoir traité chaque lot de modifications, Sync Framework appelle ISynchronousNotifyingChangeApplierTarget::SaveKnowledge afin que le fournisseur de destination puisse enregistrer la connaissance qui contient les nouvelles modifications. Cette implémentation enregistre l'objet de connaissance dans la banque de métadonnées et remplace la connaissance existante précédente.

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;
}

Méthodes non implémentées

Les méthodes suivantes ne sont pas requises pour les scénarios de synchronisation de base et ne peuvent retourner que E_NOTIMPL :

Implémentation d'ISynchronousDataRetriever

ISynchronousDataRetriever est retourné à Sync Framework par le fournisseur de source en réponse à l'appel GetChangeBatch. ISynchronousDataRetriever est envoyé au fournisseur de destination dans l'appel ProcessChangeBatch, où il est passé en général à la méthode ApplyChanges d'un applicateur de modifications. L'applicateur de modifications appelle ensuite ISynchronousDataRetriever::LoadChangeData pour obtenir une interface IUnknown qui représente les données d'élément. L'applicateur de modifications passe cette interface à la méthode SaveChange du fournisseur de destination. Le fournisseur de destination utilise cette interface IUnknown pour récupérer les données d'élément pour les éléments nouveaux ou modifiés et applique les données d'élément au réplica de destination.

Déclaration d'ISynchronousDataRetriever

Ajoutez ISynchronousDataRetriever à la liste d'héritage de classe.

class CItemStore : public ISynchronousDataRetriever

Ajoutez les méthodes ISynchronousDataRetriever à la déclaration de classe.

STDMETHOD(GetIdParameters)(
    ID_PARAMETERS * pIdParameters);

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

Méthode GetIdParameters

Sync Framework appelle ISynchronousDataRetriever::GetIdParameters pour récupérer le schéma du format de l'ID du fournisseur. Cette implémentation est quasiment la même que pour ISyncProvider::GetIdParameters.

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

Méthode LoadChangeData

Pendant l'application des modifications, Sync Framework appelle ISynchronousDataRetriever::LoadChangeData pour obtenir une interface IUnknown que le fournisseur de destination peut utiliser pour récupérer les données d'élément. Cette implémentation recherche l'élément dans le magasin d'éléments, le clone et retourne son interface IUnknown.

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;
}

Étapes suivantes

Une fois créé un fournisseur de synchronisation, vous pouvez souhaiter créer une application que peut héberger la session de synchronisation et la connecter au fournisseur. Pour plus d'informations sur la procédure à suivre, consultez Procédure : créer une application de synchronisation non managée.

Une autre étape que vous pourriez effectuer consiste à améliorer le fournisseur pour gérer les unités de modification. Pour plus d'informations sur les unités de modification, consultez Synchronisation des unités de modification.

Vous pouvez également créer une banque de métadonnées personnalisée. Pour plus d'informations sur la gestion des métadonnées de synchronisation, consultez Gestion des métadonnées.

Voir aussi

Référence

Interface ISyncProvider
Interface IKnowledgeSyncProvider
Interface ISynchronousNotifyingChangeApplierTarget
Interface ISynchronousDataRetriever
Structure ID_PARAMETERS
Interface ISynchronousNotifyingChangeApplier

Concepts

Fournisseurs de synchronisation
Composants principaux de Sync Framework