Procedura: creare un provider di sincronizzazione non gestito
In questo argomento viene illustrato come utilizzare un linguaggio non gestito, ad esempio C++, per creare un provider di sincronizzazione Microsoft Sync Framework per la sincronizzazione dei dati da un archivio dati personalizzato.
Questo argomento presuppone una conoscenza di base dei concetti relativi a C++ e COM.
Gli esempi riportati in questo argomento riguardano le interfacce Sync Framework seguenti:
Informazioni sui provider di sincronizzazione
Un provider di sincronizzazione è un componente software che rappresenta una replica durante la sincronizzazione. Consente alla replica di sincronizzare i propri dati con altre repliche. Affinché sia possibile eseguire la sincronizzazione, un'applicazione crea innanzitutto un oggetto della sessione di sincronizzazione, lo connette a due oggetti ISyncProvider e avvia la sessione. Uno dei provider rappresenta la replica di origine. La replica di origine fornisce metadati per gli elementi modificati tramite il metodo IKnowledgeSyncProvider::GetChangeBatch e dati dell'elemento tramite un oggetto ISynchronousDataRetriever. L'altro provider rappresenta la replica di destinazione. La replica di destinazione riceve i metadati per gli elementi modificati tramite il metodo IKnowledgeSyncProvider::ProcessChangeBatch e applica le modifiche all'archivio di elementi tramite un oggetto ISynchronousChangeApplier fornito da Sync Framework insieme al proprio oggetto ISynchronousChangeApplierTarget.
Per ulteriori informazioni sul ruolo del provider di sincronizzazione, vedere Provider di sincronizzazione.
Requisiti di generazione
Dichiarazioni Synchronization.h: per i componenti Sync Framework.
#include <synchronization.h>
Synchronizationerrors.h: codici di errore personalizzati.
#include <synchronizationerrors.h>
Synchronization.lib: libreria di importazione.
Esempio
Nel codice di esempio riportato in questo argomento viene illustrato come implementare i metodi di interfaccia di base necessari affinché una replica partecipi in una community di sincronizzazione Sync Framework, sia come origine sia come destinazione. La replica in questo esempio è un file XML e gli elementi da sincronizzare sono nodi XML contenuti in questo file. Nel codice i nodi XML sono rappresentati dall'interfaccia IXMLDOMNode
. In questo esempio viene anche utilizzato un archivio di metadati personalizzato implementato tramite l'API del servizio di archiviazione dei metadati. Per informazioni sul servizio di archiviazione dei metadati e sugli altri componenti Sync Framework, visitare il sito Web Microsoft.
L'archivio di metadati e l'archivio XML sono entrambi dichiarati come membri della classe del provider.
CMetadataMgr* m_pMetadataMgr;
CItemStore* m_pItemStore;
Implementazione di ISyncProvider e IKnowledgeSyncProvider
Il punto di ingresso nel provider è l'interfaccia ISyncProvider. Questa interfaccia deve funzionare come una classe base per altre interfacce più potenti del provider. In questo esempio viene utilizzata l'interfaccia IKnowledgeSyncProvider.
Dichiarazione di IKnowledgeSyncProvider
Aggiungere IKnowledgeSyncProvider
all'elenco di ereditarietà della classe.
class CXMLProvider : public IKnowledgeSyncProvider
Aggiungere i metodi ISyncProvider
alla dichiarazione della classe.
STDMETHOD(GetIdParameters)(
ID_PARAMETERS * pIdParameters);
Aggiungere i metodi IKnowledgeSyncProvider alla dichiarazione della 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);
Metodo GetIdParameters
Sync Framework chiama ISyncProvider::GetIdParameters sui provider di origine e di destinazione al momento della creazione dell'oggetto ISyncSession. Questo metodo restituisce lo schema del formato dell'ID utilizzato dal provider. Questo schema deve essere uguale per entrambi i provider. Nell'implementazione illustrata in questo esempio viene utilizzata una costante globale poiché i formati dell'ID sono costanti per il provider.
const ID_PARAMETERS c_idParams =
{
sizeof(ID_PARAMETERS), // dwSize
{ FALSE, sizeof(GUID) }, // replicaId
{ FALSE, sizeof(SYNC_GID) }, // itemId
{ FALSE, 1 }, // changeUnitId
};
L'utilizzo di una costante globale rende molto semplice l'implementazione di questo metodo.
STDMETHODIMP CXMLProvider::GetIdParameters(
ID_PARAMETERS * pIdParameters)
{
if (NULL == pIdParameters)
{
return E_POINTER;
}
else
{
*pIdParameters = c_idParams;
return S_OK;
}
}
Metodo BeginSession
Sync Framework chiama quindi IKnowledgeSyncProvider::BeginSession sui provider di origine e di destinazione. Questo metodo consente di informare un provider che si sta unendo in join a una sessione di sincronizzazione e di passare al provider un oggetto che contiene informazioni sullo stato della sessione. In questa implementazione viene archiviato l'oggetto dello stato della sessione.
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;
}
Metodo GetSyncBatchParameters
Sync Framework chiama quindi IKnowledgeSyncProvider::GetSyncBatchParameters sul provider di destinazione. Questo metodo consente di recuperare il numero di modifiche che il provider di origine deve includere in un batch di modifiche e di ottenere la conoscenza corrente del provider di destinazione. L'implementazione estrae la conoscenza dall'archivio di metadati e imposta le dimensioni del batch su 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;
}
Metodo GetChangeBatch
La sessione di sincronizzazione si avvia concretamente quando Sync Framework chiama IKnowledgeSyncProvider::GetChangeBatch sul provider di origine. Questo metodo consente di recuperare un batch di modifiche da inviare al provider di destinazione e restituisce l'interfaccia della funzione di recupero dati. Il provider di destinazione utilizza questa interfaccia per recuperare i dati dell'elemento per le modifiche applicate alla replica di destinazione. Sync Framework chiama ripetutamente GetChangeBatch
finché non viene inviato l'ultimo batch. Il provider di origine indica che si tratta dell'ultimo batch chiamando il metodo ISyncChangeBatchBase::SetLastBatch. Questa implementazione delega l'attività di enumerazione delle modifiche al metodo GetChangeBatch
dell'archivio dei metadati. L'oggetto dell'archivio di elementi XML implementa un'interfaccia della funzione di recupero dati; di conseguenza viene restituita l'interfaccia IUnknown
.
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;
}
Il metodo GetChangeBatch
implementato dall'archivio di metadati enumera gli elementi nell'archivio di metadati e controlla la versione di ciascun elemento rispetto alla conoscenza della destinazione. Se la replica di destinazione non è in grado di rilevare la presenza di una modifica, quest'ultima viene aggiunta al batch di modifiche restituito.
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;
}
Metodo ProcessChangeBatch
Dopo che Sync Framework ha ottenuto un batch di modifiche dal provider di origine chiamando il metodo GetChangeBatch
, Sync Framework chiama IKnowledgeSyncProvider::ProcessChangeBatch sul provider di destinazione. Questo metodo applica le modifiche alla replica di destinazione. Questo metodo viene chiamato una volta per ogni batch recuperato tramite GetChangeBatch
dal provider di origine. Questa implementazione utilizza il metodo GetItemBatchVersions
dell'archivio di metadati per ottenere informazioni sulla versione locale degli elementi provenienti dal provider di origine. Crea quindi un oggetto ISynchronousNotifyingChangeApplier implementato da Sync Framework e chiama il metodo 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;
}
Il metodo GetItemBatchVersions
dell'archivio di metadati enumera le modifiche inviate nel batch di modifiche dal provider di origine. Se l'elemento è presente nei metadati di destinazione, le informazioni sulla versione vengono aggiunte a un nuovo batch creato espressamente per contenere le informazioni sulla versione. Se un elemento non è presente nei metadati di destinazione, viene contrassegnato come nuovo elemento nel batch della versione. Successivamente il metodo restituisce il batch della versione.
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;
}
Metodo EndSession
Dopo che il provider di origine ha inviato l'ultimo batch e dopo che il provider di destinazione ha applicato le modifiche al proprio archivio dati, Sync Framework chiama IKnowledgeSyncProvider::EndSession sui provider di origine e di destinazione. Questo metodo consente di informare un provider che sta abbandonando una sessione di sincronizzazione e che è necessario liberare qualsiasi risorsa associata alla sessione. Questa implementazione libera l'oggetto dello stato della sessione archiviato nella chiamata 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;
}
Metodi non implementati
I metodi seguenti non sono necessari perché in questo esempio non vengono mai rimossi gli elementi contrassegnati come eliminati nell'archivio di metadati. Questi metodi possono restituire E_NOTIMPL:
Implementazione di ISynchronousNotifyingChangeApplierTarget
Questa interfaccia viene fornita a Sync Framework quando il provider di destinazione chiama il metodo ISynchronousNotifyingChangeApplier::ApplyChanges, in genere nel metodo ProcessChangeBatch. ISynchronousNotifyingChangeApplierTarget contiene metodi chiamati durante l'applicazione delle modifiche. Questi metodi vengono chiamati esclusivamente nel provider di destinazione.
Dichiarazione di ISynchronousNotifyingChangeApplierTarget
Aggiungere ISynchronousNotifyingChangeApplierTarget
all'elenco di ereditarietà della classe.
class CXMLProvider : public IKnowledgeSyncProvider
, ISynchronousNotifyingChangeApplierTarget
Aggiungere i metodi ISynchronousNotifyingChangeApplierTarget
alla dichiarazione della 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);
Metodo GetIdParameters
Sync Framework chiama ISynchronousNotifyingChangeApplierTarget::GetIdParameters per recuperare lo schema del formato dell'ID del provider. In questo esempio viene utilizzata la stessa classe per implementare IKnowledgeSyncProvider
e ISynchronousNotifyingChangeApplierTarget
. Di conseguenza, questa implementazione è uguale a quella per ISyncProvider::GetIdParameters
.
STDMETHODIMP CXMLProvider::GetIdParameters(
ID_PARAMETERS * pIdParameters)
{
if (NULL == pIdParameters)
{
return E_POINTER;
}
else
{
*pIdParameters = c_idParams;
return S_OK;
}
}
GetCurrentTickCount
Sync Framework chiama ISynchronousNotifyingChangeApplierTarget::GetCurrentTickCount per incrementare e recuperare il conteggio per la replica. Questa implementazione chiama il metodo GetNextTickCount
dell'archivio di metadati.
STDMETHODIMP CXMLProvider::GetCurrentTickCount(
ULONGLONG * pTickCount)
{
_ASSERT(NULL != m_pMetadataMgr);
return m_pMetadataMgr->GetNextTickCount(pTickCount);
}
Il metodo GetNextTickCount
dell'archivio di metedati incrementa il conteggio della replica e lo restituisce.
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
Durante l'applicazione delle modifiche, Sync Framework chiama ISynchronousNotifyingChangeApplierTarget::SaveChange per ogni modifica che deve essere applicata alla replica di destinazione. Questa implementazione gestisce in modo appropriato i nuovi elementi, quelli modificati e quelli eliminati e aggiorna sia i dati dell'elemento nell'archivio di elementi sia i metadati dell'elemento nell'archivio di metadati.
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
Dopo avere elaborato ogni batch di modifiche, Sync Framework chiama ISynchronousNotifyingChangeApplierTarget::SaveKnowledge affinché il provider di destinazione possa salvare la conoscenza che contiene le nuove modifiche. Questa implementazione salva l'oggetto conoscenza nell'archivio di metadati e sovrascrive la conoscenza esistente.
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;
}
Metodi non implementati
I metodi seguenti non sono necessari per gli scenari di sincronizzazione di base e possono restituire solo E_NOTIMPL:
ISynchronousNotifyingChangeApplierTarget::GetDestinationVersion
ISynchronousNotifyingChangeApplierTarget::SaveChangeWithChangeUnits
Implementazione di ISynchronousDataRetriever
ISynchronousDataRetriever viene restituito a Sync Framework dal provider di origine in risposta alla chiamata a GetChangeBatch. ISynchronousDataRetriever viene inviato al provider di destinazione nella chiamata ProcessChangeBatch, dove viene in genere passato al metodo ApplyChanges di un oggetto di applicazione modifiche. L'oggetto di applicazione modifiche chiama quindi ISynchronousDataRetriever::LoadChangeData per ottenere un'interfaccia IUnknown che rappresenta i dati dell'elemento. L'oggetto di applicazione modifiche passa questa interfaccia al metodo SaveChange del provider di destinazione. Il provider di destinazione utilizza questa interfaccia IUnknown per recuperare dati per elementi nuovi o modificati e applica i dati dell'elemento alla replica di destinazione.
Dichiarazione di ISynchronousDataRetriever
Aggiungere ISynchronousDataRetriever
all'elenco di ereditarietà della classe.
class CItemStore : public ISynchronousDataRetriever
Aggiungere i metodi ISynchronousDataRetriever
alla dichiarazione della classe.
STDMETHOD(GetIdParameters)(
ID_PARAMETERS * pIdParameters);
STDMETHOD(LoadChangeData)(
ILoadChangeContext * pLoadChangeContext,
IUnknown ** ppUnkData);
Metodo GetIdParameters
Sync Framework chiama ISynchronousDataRetriever::GetIdParameters per recuperare lo schema del formato dell'ID del provider. Questa implementazione è sostanzialmente uguale a quella per ISyncProvider::GetIdParameters
.
STDMETHODIMP CItemStore::GetIdParameters(
ID_PARAMETERS * pIdParameters)
{
if (NULL == pIdParameters)
{
return E_POINTER;
}
else
{
*pIdParameters = c_idParams;
return S_OK;
}
}
Metodo LoadChangeData
Durante l'applicazione delle modifiche, Sync Framework chiama ISynchronousDataRetriever::LoadChangeData per ottenere un'interfaccia IUnknown
che può essere utilizzata dal provider di destinazione per recuperare i dati dell'elemento. Questa implementazione rileva l'elemento nell'archivio di elementi, lo duplica e restituisce l'interfaccia 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;
}
Passaggi successivi
Dopo avere creato un provider di sincronizzazione, è necessario creare un'applicazione che possa ospitare la sessione di sincronizzazione e connetterla al provider. Per informazioni su come eseguire questa operazione, vedere Procedura: creare un'applicazione di sincronizzazione non gestita.
Un altro passaggio successivo consiste nell'ottimizzare il provider per gestire le unità di modifica. Per ulteriori informazioni sulle unità di modifica, vedere Sincronizzazione delle unità di modifica.
È anche possibile creare un archivio di metadati personalizzato. Per ulteriori informazioni sulle modalità di gestione dei metadati di sincronizzazione, vedere Gestione dei metadati.
Vedere anche
Riferimento
Interfaccia ISyncProvider
Interfaccia IKnowledgeSyncProvider
Interfaccia ISynchronousNotifyingChangeApplierTarget
Interfaccia ISynchronousDataRetriever
Struttura ID_PARAMETERS
Interfaccia ISynchronousNotifyingChangeApplier
Concetti
Provider di sincronizzazione
Componenti principali di Sync Framework