906 lines
24 KiB
C
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);
|
|
}
|
|
|