NT4/private/windows/rover/filesync/core/merge.c
2020-09-30 17:12:29 +02:00

906 lines
24 KiB
C

/*
* merge.c - File merge handler module.
*/
/* Headers
**********/
#include "project.h"
#pragma hdrstop
#include "stub.h"
#include "oleutil.h"
#include "irecinit.h"
/* Module Variables
*******************/
#pragma data_seg(DATA_SEG_PER_INSTANCE)
/* lock count for reconciliation handler cache */
PRIVATE_DATA ULONG MulcRecHandlerCacheLock = 0;
/* handle to reconciliation handler cache */
PRIVATE_DATA HCLSIFACECACHE MhcicRecHandlerCache = NULL;
#pragma data_seg()
/***************************** 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);
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);
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);
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);
}