|
|
/*
* merge.c - File merge handler module. */
/* Headers
**********/
#include "project.h"
#pragma hdrstop
#include "stub.h"
#include "oleutil.h"
#include "irecinit.h"
/* Module Variables
*******************/
/* lock count for reconciliation handler cache */
PRIVATE_DATA ULONG MulcRecHandlerCacheLock = 0;
/* handle to reconciliation handler cache */
PRIVATE_DATA HCLSIFACECACHE MhcicRecHandlerCache = NULL;
/***************************** Private Functions *****************************/
/* Module Prototypes
********************/
PRIVATE_CODE HRESULT CreateRecHandlerCache(void); PRIVATE_CODE void DestroyRecHandlerCache(void); PRIVATE_CODE HRESULT OLEMerge(PRECNODE, RECSTATUSPROC, LPARAM, DWORD, HWND, HWND, PRECNODE *); PRIVATE_CODE BOOL GetRecNodeByIndex(PCRECITEM, LONG, PRECNODE *); PRIVATE_CODE HRESULT CreateMergeSourceMonikers(PRECNODE, PULONG, PIMoniker **); PRIVATE_CODE HRESULT CreateCopyDestinationMonikers(PCRECITEM, PULONG, PIMoniker **);
#ifdef DEBUG
PRIVATE_CODE BOOL RecHandlerCacheIsOk(void); PRIVATE_CODE BOOL VerifyRECITEMAndDestRECNODE(PCRECNODE);
#endif
/*
** CreateRecHandlerCache() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE HRESULT CreateRecHandlerCache(void) { HRESULT hr;
ASSERT(RecHandlerCacheIsOk());
/* Has the merge handler cache already been created? */
if (MhcicRecHandlerCache) /* Yes. */ hr = S_OK; else { /* No. Create it. */
if (CreateClassInterfaceCache(&MhcicRecHandlerCache)) { hr = S_OK;
TRACE_OUT((TEXT("CreateRecHandlerCache(): Merge handler cache created."))); } else hr = E_OUTOFMEMORY; }
ASSERT(RecHandlerCacheIsOk());
return(hr); }
/*
** DestroyRecHandlerCache() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE void DestroyRecHandlerCache(void) { ASSERT(RecHandlerCacheIsOk());
/* Has the merge handler cache already been created? */
if (MhcicRecHandlerCache) { /* Yes. Destroy it. */
DestroyClassInterfaceCache(MhcicRecHandlerCache); MhcicRecHandlerCache = NULL;
TRACE_OUT((TEXT("DestroyRecHandlerCache(): Merge handler cache destroyed."))); }
ASSERT(RecHandlerCacheIsOk());
return; }
/*
** OLEMerge() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE HRESULT OLEMerge(PRECNODE prnDest, RECSTATUSPROC rsp, LPARAM lpCallbackData, DWORD dwInFlags, HWND hwndOwner, HWND hwndProgressFeedback, PRECNODE *pprnMergedResult) { HRESULT hr; TCHAR rgchMergeDestPath[MAX_PATH_LEN]; CLSID clsidReconcilableObject;
/* lpCallbackData may be any value. */
ASSERT(IS_VALID_STRUCT_PTR(prnDest, CRECNODE)); ASSERT(! rsp || IS_VALID_CODE_PTR(rsp, RECSTATUSPROC)); ASSERT(FLAGS_ARE_VALID(dwInFlags, ALL_RI_FLAGS)); ASSERT(IS_FLAG_CLEAR(dwInFlags, RI_FL_ALLOW_UI) || IS_VALID_HANDLE(hwndOwner, WND)); ASSERT(IS_FLAG_CLEAR(dwInFlags, RI_FL_FEEDBACK_WINDOW_VALID) || IS_VALID_HANDLE(hwndProgressFeedback, WND)); ASSERT(IS_VALID_WRITE_PTR(pprnMergedResult, PRECNODE));
ComposePath(rgchMergeDestPath, prnDest->pcszFolder, prnDest->priParent->pcszName, ARRAYSIZE(rgchMergeDestPath)); ASSERT(lstrlen(rgchMergeDestPath) < ARRAYSIZE(rgchMergeDestPath));
hr = GetReconcilerClassID(rgchMergeDestPath, &clsidReconcilableObject);
if (SUCCEEDED(hr)) { PIReconcilableObject piro;
hr = GetClassInterface(MhcicRecHandlerCache, &clsidReconcilableObject, &IID_IReconcilableObject, &piro);
if (SUCCEEDED(hr)) { HSTGIFACE hstgi;
hr = GetStorageInterface((PIUnknown)piro, &hstgi);
if (SUCCEEDED(hr)) { hr = LoadFromStorage(hstgi, rgchMergeDestPath);
if (SUCCEEDED(hr)) { PIReconcileInitiator pirecinit;
hr = IReconcileInitiator_Constructor( GetTwinBriefcase((HTWIN)(prnDest->hObjectTwin)), rsp, lpCallbackData, &pirecinit);
if (SUCCEEDED(hr)) { ULONG ulcMergeSources; PIMoniker *ppimkMergeSources;
hr = CreateMergeSourceMonikers(prnDest, &ulcMergeSources, &ppimkMergeSources);
if (SUCCEEDED(hr)) { DWORD dwOLEFlags; LONG liMergedResult;
dwOLEFlags = (RECONCILEF_NORESIDUESOK | RECONCILEF_OMITSELFRESIDUE | RECONCILEF_YOUMAYDOTHEUPDATES);
if (IS_FLAG_SET(dwInFlags, RI_FL_ALLOW_UI)) SET_FLAG(dwOLEFlags, RECONCILEF_MAYBOTHERUSER);
if (IS_FLAG_SET(dwInFlags, RI_FL_FEEDBACK_WINDOW_VALID)) SET_FLAG(dwOLEFlags, RECONCILEF_FEEDBACKWINDOWVALID);
hr = piro->lpVtbl->Reconcile(piro, pirecinit, dwOLEFlags, hwndOwner, hwndProgressFeedback, ulcMergeSources, ppimkMergeSources, &liMergedResult, NULL, NULL);
if (SUCCEEDED(hr)) { if (hr == REC_S_IDIDTHEUPDATES) { /* Return original merge destination RECNODE. */
*pprnMergedResult = prnDest;
TRACE_OUT((TEXT("OLEMerge(): IReconcilableObject::Reconcile() returned %s. Not saving merged result to %s\\%s."), GetHRESULTString(hr), prnDest->pcszFolder, prnDest->priParent->pcszName)); } else { /*
* Only save the merged result if it's different * than all of the replicas. */
if (liMergedResult < 0) { ASSERT(liMergedResult == -1);
hr = SaveToStorage(hstgi);
if (SUCCEEDED(hr)) { *pprnMergedResult = prnDest;
TRACE_OUT((TEXT("OLEMerge(): Merge into %s completed successfully."), rgchMergeDestPath)); } else WARNING_OUT((TEXT("OLEMerge(): Failed to save merged result to %s."), rgchMergeDestPath)); } else if (! liMergedResult) { *pprnMergedResult = prnDest;
TRACE_OUT((TEXT("OLEMerge(): Merged result identical to %s."), rgchMergeDestPath)); } else { if (GetRecNodeByIndex(prnDest->priParent, liMergedResult, pprnMergedResult)) TRACE_OUT((TEXT("OLEMerge(): Merged result identical to %s\\%s."), (*pprnMergedResult)->pcszFolder, (*pprnMergedResult)->priParent->pcszName)); else { hr = E_UNEXPECTED;
WARNING_OUT((TEXT("OLEMerge(): Merge handler returned bad merge result index %ld. No such RECNODE for %s."), liMergedResult, prnDest->priParent->pcszName)); } } } } else WARNING_OUT((TEXT("OLEMerge(): Merge to %s failed."), rgchMergeDestPath));
ReleaseIUnknowns(ulcMergeSources, (PIUnknown *)ppimkMergeSources); } else WARNING_OUT((TEXT("OLEMerge(): Failed to create merge source monikers for merge destination %s."), rgchMergeDestPath));
EVAL(! pirecinit->lpVtbl->Release(pirecinit)); } else WARNING_OUT((TEXT("OLEMerge(): Failed to create ReconcileInitiator for merge destination %s."), rgchMergeDestPath)); } else WARNING_OUT((TEXT("OLEMerge(): Failed to load replica %s from storage."), rgchMergeDestPath));
ReleaseStorageInterface(hstgi); } else WARNING_OUT((TEXT("OLEMerge(): Failed to get storage interface for replica %s."), rgchMergeDestPath)); } else TRACE_OUT((TEXT("OLEMerge(): Failed to get IReconcilableObject for replica %s."), rgchMergeDestPath)); } else TRACE_OUT((TEXT("OLEMerge(): Failed to get reconciliation handler class ID for replica %s."), rgchMergeDestPath));
ASSERT(FAILED(hr) || (IS_VALID_STRUCT_PTR(*pprnMergedResult, CRECNODE) && (*pprnMergedResult)->priParent == prnDest->priParent));
return(hr); }
/*
** GetRecNodeByIndex() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none ** ** The first RECNODE in the RECITEM's list of RECNODEs is index 1, the second ** RECNODE is index 2, etc. */ PRIVATE_CODE BOOL GetRecNodeByIndex(PCRECITEM pcri, LONG li, PRECNODE *pprn) { BOOL bFound;
ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM)); ASSERT(IS_VALID_WRITE_PTR(pprn, PRECNODE));
if (EVAL(li > 0)) { PRECNODE prn;
for (prn = pcri->prnFirst; prn && --li > 0; prn = prn->prnNext) ;
bFound = EVAL(prn && ! li);
if (bFound) *pprn = prn; } else bFound = FALSE;
ASSERT(! bFound || (IS_VALID_STRUCT_PTR(*pprn, CRECNODE) && (*pprn)->priParent == pcri));
return(bFound); }
/*
** CreateMergeSourceMonikers() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE HRESULT CreateMergeSourceMonikers(PRECNODE prnDest, PULONG pulcMergeSources, PIMoniker **pppimk) { HRESULT hr; ULONG ulcMergeSources; PCRECNODE pcrn;
ASSERT(IS_VALID_STRUCT_PTR(prnDest, CRECNODE)); ASSERT(IS_VALID_WRITE_PTR(pulcMergeSources, ULONG)); ASSERT(IS_VALID_WRITE_PTR(pppimk, PIMoniker *));
ulcMergeSources = 0;
for (pcrn = prnDest->priParent->prnFirst; pcrn; pcrn = pcrn->prnNext) { if (pcrn->rnaction == RNA_MERGE_ME && pcrn != prnDest) ulcMergeSources++; }
if (AllocateMemory(ulcMergeSources * sizeof(**pppimk), (PVOID *)pppimk)) { hr = S_OK; *pulcMergeSources = 0;
for (pcrn = prnDest->priParent->prnFirst; pcrn; pcrn = pcrn->prnNext) { if (pcrn->rnaction == RNA_MERGE_ME && pcrn != prnDest) { hr = MyCreateFileMoniker(pcrn->pcszFolder, pcrn->priParent->pcszName, &((*pppimk)[*pulcMergeSources]));
if (SUCCEEDED(hr)) { ASSERT(*pulcMergeSources < ulcMergeSources); (*pulcMergeSources)++; } else break; } }
if (FAILED(hr)) ReleaseIUnknowns(*pulcMergeSources, *(PIUnknown **)pppimk); } else hr = E_OUTOFMEMORY;
return(hr); }
/*
** CreateCopyDestinationMonikers() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE HRESULT CreateCopyDestinationMonikers(PCRECITEM pcri, PULONG pulcCopyDestinations, PIMoniker **pppimk) { HRESULT hr; ULONG ulcCopyDestinations; PCRECNODE pcrn;
ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM)); ASSERT(IS_VALID_WRITE_PTR(pulcCopyDestinations, ULONG)); ASSERT(IS_VALID_WRITE_PTR(pppimk, PIMoniker *));
ulcCopyDestinations = 0;
for (pcrn = pcri->prnFirst; pcrn; pcrn = pcrn->prnNext) { if (pcrn->rnaction == RNA_COPY_TO_ME) ulcCopyDestinations++; }
if (AllocateMemory(ulcCopyDestinations * sizeof(**pppimk), (PVOID *)pppimk)) { hr = S_OK; *pulcCopyDestinations = 0;
for (pcrn = pcri->prnFirst; pcrn; pcrn = pcrn->prnNext) { if (pcrn->rnaction == RNA_COPY_TO_ME) { ASSERT(pcrn->priParent == pcri);
hr = MyCreateFileMoniker(pcrn->pcszFolder, pcrn->priParent->pcszName, &((*pppimk)[*pulcCopyDestinations]));
if (SUCCEEDED(hr)) { ASSERT(*pulcCopyDestinations < ulcCopyDestinations); (*pulcCopyDestinations)++; } else break; } }
if (FAILED(hr)) ReleaseIUnknowns(*pulcCopyDestinations, *(PIUnknown **)pppimk); } else hr = E_OUTOFMEMORY;
return(hr); }
#ifdef DEBUG
/*
** RecHandlerCacheIsOk() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL RecHandlerCacheIsOk(void) { /* Are the module merge handler cache variables in a correct state? */
return(! MhcicRecHandlerCache || IS_VALID_HANDLE(MhcicRecHandlerCache, CLSIFACECACHE)); }
/*
** VerifyRECITEMAndDestRECNODE() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL VerifyRECITEMAndDestRECNODE(PCRECNODE pcrnSrc) { /* Do the RECITEM and source RECNODE actions match? */
return(pcrnSrc->priParent->riaction == RIA_MERGE && pcrnSrc->rnaction == RNA_MERGE_ME); }
#endif
/****************************** Public Functions *****************************/
/*
** BeginMerge() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE void BeginMerge(void) { ASSERT(RecHandlerCacheIsOk());
ASSERT(MulcRecHandlerCacheLock < ULONG_MAX); MulcRecHandlerCacheLock++;
ASSERT(RecHandlerCacheIsOk());
return; }
/*
** EndMerge() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE void EndMerge(void) { ASSERT(RecHandlerCacheIsOk());
/* Is the merge handler cache still locked? */
if (! --MulcRecHandlerCacheLock) DestroyRecHandlerCache();
ASSERT(RecHandlerCacheIsOk());
return; }
/*
** MergeHandler() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE HRESULT MergeHandler(PRECNODE prnDest, RECSTATUSPROC rsp, LPARAM lpCallbackData, DWORD dwInFlags, HWND hwndOwner, HWND hwndProgressFeedback, PRECNODE *pprnMergedResult) { HRESULT hr;
/* lpCallbackData may be any value. */
ASSERT(IS_VALID_STRUCT_PTR(prnDest, CRECNODE)); ASSERT(! rsp || IS_VALID_CODE_PTR(rsp, RECSTATUSPROC)); ASSERT(FLAGS_ARE_VALID(dwInFlags, ALL_RI_FLAGS)); ASSERT(IS_FLAG_CLEAR(dwInFlags, RI_FL_ALLOW_UI) || IS_VALID_HANDLE(hwndOwner, WND)); ASSERT(IS_FLAG_CLEAR(dwInFlags, RI_FL_FEEDBACK_WINDOW_VALID) || IS_VALID_HANDLE(hwndProgressFeedback, WND)); ASSERT(IS_VALID_WRITE_PTR(pprnMergedResult, PRECNODE));
ASSERT(VerifyRECITEMAndDestRECNODE(prnDest));
BeginMerge();
/* Make sure the merge handler cache has been created. */
hr = CreateRecHandlerCache();
if (SUCCEEDED(hr)) { RECSTATUSUPDATE rsu;
/* 0% complete. */
rsu.ulScale = 1; rsu.ulProgress = 0;
if (NotifyReconciliationStatus(rsp, RS_BEGIN_MERGE, (LPARAM)&rsu, lpCallbackData)) { hr = OLEMerge(prnDest, rsp, lpCallbackData, dwInFlags, hwndOwner, hwndProgressFeedback, pprnMergedResult);
if (SUCCEEDED(hr)) { /* 100% complete. */
rsu.ulScale = 1; rsu.ulProgress = 1;
/* Don't allow abort. */
NotifyReconciliationStatus(rsp, RS_END_MERGE, (LPARAM)&rsu, lpCallbackData); } } else hr = E_ABORT; }
EndMerge();
ASSERT(FAILED(hr) || (IS_VALID_STRUCT_PTR(*pprnMergedResult, CRECNODE) && (*pprnMergedResult)->priParent == prnDest->priParent));
return(hr); }
/*
** MyCreateFileMoniker() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE HRESULT MyCreateFileMoniker(LPCTSTR pcszPath, LPCTSTR pcszSubPath, PIMoniker *ppimk) { HRESULT hr; TCHAR rgchPath[MAX_PATH_LEN]; WCHAR rgwchUnicodePath[MAX_PATH_LEN];
ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR)); ASSERT(IS_VALID_WRITE_PTR(ppimk, PIMoniker));
ComposePath(rgchPath, pcszPath, pcszSubPath, ARRAYSIZE(rgchPath)); ASSERT(lstrlen(rgchPath) < ARRAYSIZE(rgchPath));
#ifdef UNICODE
hr = CreateFileMoniker(rgchPath, ppimk);
#else
/* Translate ANSI string into Unicode for OLE. */
if (MultiByteToWideChar(CP_ACP, 0, rgchPath, -1, rgwchUnicodePath, ARRAY_ELEMENTS(rgwchUnicodePath))) { hr = CreateFileMoniker(rgwchUnicodePath, ppimk); } else { hr = MAKE_SCODE(SEVERITY_ERROR, FACILITY_WIN32, GetLastError()); }
#endif
if (FAILED(hr)) WARNING_OUT((TEXT("MyCreateFileMoniker(): CreateFileMoniker() on %s failed, returning %s."), pcszPath, GetHRESULTString(hr)));
return(hr); }
/*
** ReleaseIUnknowns() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE void ReleaseIUnknowns(ULONG ulcIUnknowns, PIUnknown *ppiunk) { ULONG uli;
/* ulcIUnknowns may be any value. */
ASSERT(IS_VALID_READ_BUFFER_PTR(ppiunk, PIUnknown, ulcIUnknowns * sizeof(*ppiunk)));
for (uli = 0; uli < ulcIUnknowns; uli++) { ASSERT(IS_VALID_STRUCT_PTR(ppiunk[uli], CIUnknown));
ppiunk[uli]->lpVtbl->Release(ppiunk[uli]); }
FreeMemory(ppiunk);
return; }
/*
** OLECopy() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE HRESULT OLECopy(PRECNODE prnSrc, PCCLSID pcclsidReconcilableObject, RECSTATUSPROC rsp, LPARAM lpCallbackData, DWORD dwFlags, HWND hwndOwner, HWND hwndProgressFeedback) { HRESULT hr;
/* lpCallbackData may be any value. */
ASSERT(IS_VALID_STRUCT_PTR(prnSrc, CRECNODE)); ASSERT(IS_VALID_STRUCT_PTR(pcclsidReconcilableObject, CCLSID)); ASSERT(! rsp || IS_VALID_CODE_PTR(rsp, RECSTATUSPROC)); ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_RI_FLAGS)); ASSERT(IS_FLAG_CLEAR(dwFlags, RI_FL_ALLOW_UI) || IS_VALID_HANDLE(hwndOwner, WND)); ASSERT(IS_FLAG_CLEAR(dwFlags, RI_FL_FEEDBACK_WINDOW_VALID) || IS_VALID_HANDLE(hwndProgressFeedback, WND));
BeginMerge();
/* Make sure the merge handler cache has been created. */
hr = CreateRecHandlerCache();
if (SUCCEEDED(hr)) { TCHAR rgchCopySrcPath[MAX_PATH_LEN]; PIReconcilableObject piro;
ComposePath(rgchCopySrcPath, prnSrc->pcszFolder, prnSrc->priParent->pcszName, ARRAYSIZE(rgchCopySrcPath)); ASSERT(lstrlen(rgchCopySrcPath) < ARRAYSIZE(rgchCopySrcPath));
hr = GetClassInterface(MhcicRecHandlerCache, pcclsidReconcilableObject, &IID_IReconcilableObject, &piro);
if (SUCCEEDED(hr)) { HSTGIFACE hstgi;
hr = GetStorageInterface((PIUnknown)piro, &hstgi);
if (SUCCEEDED(hr)) { hr = LoadFromStorage(hstgi, rgchCopySrcPath);
if (SUCCEEDED(hr)) { PIReconcileInitiator pirecinit;
hr = IReconcileInitiator_Constructor( GetTwinBriefcase((HTWIN)(prnSrc->hObjectTwin)), rsp, lpCallbackData, &pirecinit);
if (SUCCEEDED(hr)) { ULONG ulcCopyDestinations; PIMoniker *ppimkCopyDestinations;
hr = CreateCopyDestinationMonikers(prnSrc->priParent, &ulcCopyDestinations, &ppimkCopyDestinations);
if (SUCCEEDED(hr)) { DWORD dwOLEFlags; LONG liMergedResult;
dwOLEFlags = (RECONCILEF_YOUMAYDOTHEUPDATES | RECONCILEF_ONLYYOUWERECHANGED);
if (IS_FLAG_SET(dwFlags, RI_FL_ALLOW_UI)) SET_FLAG(dwOLEFlags, RECONCILEF_MAYBOTHERUSER);
if (IS_FLAG_SET(dwFlags, RI_FL_FEEDBACK_WINDOW_VALID)) SET_FLAG(dwOLEFlags, RECONCILEF_FEEDBACKWINDOWVALID);
hr = piro->lpVtbl->Reconcile(piro, pirecinit, dwOLEFlags, hwndOwner, hwndProgressFeedback, ulcCopyDestinations, ppimkCopyDestinations, &liMergedResult, NULL, NULL);
if (SUCCEEDED(hr)) { ASSERT(liMergedResult == -1);
if (hr == S_FALSE) /* Release storage for internal copy routine. */ HandsOffStorage(hstgi); else ASSERT(hr == REC_S_IDIDTHEUPDATES); } else WARNING_OUT((TEXT("OLECopy(): Copy from %s failed."), rgchCopySrcPath));
ReleaseIUnknowns(ulcCopyDestinations, (PIUnknown *)ppimkCopyDestinations); } else WARNING_OUT((TEXT("OLECopy(): Failed to create copy destination monikers for copy source %s."), rgchCopySrcPath));
EVAL(! pirecinit->lpVtbl->Release(pirecinit)); } else WARNING_OUT((TEXT("OLECopy(): Failed to create ReconcileInitiator for copy source %s."), rgchCopySrcPath)); } else WARNING_OUT((TEXT("OLECopy(): Failed to load copy source %s from storage."), rgchCopySrcPath));
ReleaseStorageInterface(hstgi); } else WARNING_OUT((TEXT("OLECopy(): Failed to get storage interface for copy source %s."), rgchCopySrcPath)); } else TRACE_OUT((TEXT("OLECopy(): Failed to get reconciliation handler class ID for replica %s."), rgchCopySrcPath)); }
EndMerge();
return(hr); }
|