extern "C" HRESULT DependencyAddIgnoreDependencies( __in STRINGDICT_HANDLE sdIgnoreDependencies, __in_z LPCWSTR wzAddIgnoreDependencies ) { HRESULT hr = S_OK; LPWSTR wzContext = NULL; // Parse through the semicolon-delimited tokens and add to the array. for (LPCWSTR wzToken = ::wcstok_s(const_cast<LPWSTR>(wzAddIgnoreDependencies), vcszIgnoreDependenciesDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, vcszIgnoreDependenciesDelim, &wzContext)) { hr = DictKeyExists(sdIgnoreDependencies, wzToken); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to check the dictionary of unique dependencies."); } else { hr = DictAddKey(sdIgnoreDependencies, wzToken); ExitOnFailure1(hr, "Failed to add \"%ls\" to the string dictionary.", wzToken); } } LExit: return hr; }
HRESULT ProductSyncValues( __in CFGDB_STRUCT *pcdb1, __in CFGDB_STRUCT *pcdb2, __in BOOL fAllowLocalToReceiveData, __in STRINGDICT_HANDLE shDictValuesSeen, __out CONFLICT_PRODUCT **ppcpProduct ) { HRESULT hr = S_OK; LPWSTR sczName = NULL; DWORD dwInserting = 0; SCE_QUERY_HANDLE sqhHandle = NULL; SCE_QUERY_RESULTS_HANDLE sqrhResults = NULL; SCE_ROW_HANDLE sceRow = NULL; DWORD dwFoundIndex = 0; DWORD dwSubsumeIndex = 0; BOOL fSame = FALSE; BOOL fFirstIsLocal = (NULL == pcdb1->pcdbLocal); CFG_ENUMERATION * valueHistory1 = NULL; DWORD dwCfgCount1 = 0; CFG_ENUMERATION * valueHistory2 = NULL; DWORD dwCfgCount2 = 0; hr = SceBeginQuery(pcdb1->psceDb, VALUE_INDEX_TABLE, 0, &sqhHandle); ExitOnFailure(hr, "Failed to begin query into value table"); hr = SceSetQueryColumnDword(sqhHandle, pcdb1->dwAppID); ExitOnFailure(hr, "Failed to set query column dword to: %u", pcdb1->dwAppID); hr = SceRunQueryRange(&sqhHandle, &sqrhResults); if (E_NOTFOUND == hr) { ExitFunction1(hr = S_OK); } ExitOnFailure(hr, "Failed to enumerate values for product %u", pcdb1->dwAppID); hr = SceGetNextResultRow(sqrhResults, &sceRow); while (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get next row from query into value table"); CfgReleaseEnumeration(valueHistory1); valueHistory1 = NULL; CfgReleaseEnumeration(valueHistory2); valueHistory2 = NULL; hr = SceGetColumnString(sceRow, VALUE_COMMON_NAME, &sczName); ExitOnFailure(hr, "Failed to get value name"); if (NULL != shDictValuesSeen) { hr = DictKeyExists(shDictValuesSeen, sczName); if (E_NOTFOUND == hr) { hr = DictAddKey(shDictValuesSeen, sczName); ExitOnFailure(hr, "Failed to add to dictionary value: %ls", sczName); } else { ExitOnFailure(hr, "Failed to check if key exists: %ls", sczName); // This value was already synced; skip it! goto Skip; } } // Exclude legacy detect cache values, they should never be synced off the machine // TODO: when we support per-machine settings, migrate this to use that feature if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczName, lstrlenW(wzLegacyDetectCacheValuePrefix), wzLegacyDetectCacheValuePrefix, lstrlenW(wzLegacyDetectCacheValuePrefix))) { goto Skip; } // First check if the values are identical. Even if they were set by different folks at different times, // same value means nothing to sync. hr = ValueMatch(sczName, pcdb1, pcdb2, sceRow, &fSame); ExitOnFailure(hr, "Failed to check if values are identical"); if (fSame) { goto Skip; } // Get history of the value in db2 hr = EnumPastValues(pcdb2, sczName, &valueHistory2, &dwCfgCount2); if (E_NOTFOUND == hr) { hr = S_OK; } ExitOnFailure(hr, "Failed to enumerate previous values in db2"); // Get history of the value in db1 hr = EnumPastValues(pcdb1, sczName, &valueHistory1, &dwCfgCount1); ExitOnFailure(hr, "Found value in db1, but failed to enumerate previous values in db1 while searching for conflicts"); if (0 == dwCfgCount2) { if (fFirstIsLocal || fAllowLocalToReceiveData) { hr = ValueTransferFromHistory(pcdb2, valueHistory1, 0, pcdb1); ExitOnFailure(hr, "Failed to transfer history (due to value not present) from db 2 to db 1 for value %ls", sczName); } goto Skip; } // Don't write anything to db2 if it's local and we're told not to if (fFirstIsLocal || fAllowLocalToReceiveData) { // We first check the latest value. However, if the previous value is identical (same type & value, just different source), check for subsumation of that too. // This reduces unnecessary conflicts in rare corner case scenarios. dwSubsumeIndex = dwCfgCount2; do { --dwSubsumeIndex; // Check if the last history entry for database 2 exists in the database 1 - if it does, database 2's changes are subsumed hr = EnumFindValueInHistory(valueHistory1, dwCfgCount1, valueHistory2->valueHistory.rgcValues + dwSubsumeIndex, &dwFoundIndex); if (S_OK == hr) { // Database 2 is subsumed - pipe over all the newest history entries hr = ValueTransferFromHistory(pcdb2, valueHistory1, dwFoundIndex + 1, pcdb1); ExitOnFailure(hr, "Failed to transfer history (due to history subsumed) from db 1 to db 2 for value %ls", sczName); goto Skip; } else if (E_NOTFOUND == hr) { hr = S_OK; } else { ExitOnFailure(hr, "Failed to check if db2's value history is subsumed by db1's value history"); } if (0 < dwSubsumeIndex) { hr = ValueCompare(valueHistory2->valueHistory.rgcValues + dwSubsumeIndex, valueHistory2->valueHistory.rgcValues + dwSubsumeIndex - 1, FALSE, &fSame); ExitOnFailure(hr, "Failed to check if value and previous value in database 2 are equivalent"); } } while (0 < dwSubsumeIndex && fSame); } // Don't write anything to db1 if it's local and we're told not to if (!fFirstIsLocal || fAllowLocalToReceiveData) { // We first check the latest value. However, if the previous value is identical (same type & value, just different source), check for subsumation of that too. // This reduces unnecessary conflicts in rare corner case scenarios. dwSubsumeIndex = dwCfgCount1; do { --dwSubsumeIndex; hr = EnumFindValueInHistory(valueHistory2, dwCfgCount2, valueHistory1->valueHistory.rgcValues + dwSubsumeIndex, &dwFoundIndex); if (S_OK == hr) { // Database 1 is subsumed - pipe over all the newest history entries hr = ValueTransferFromHistory(pcdb1, valueHistory2, dwFoundIndex + 1, pcdb2); ExitOnFailure(hr, "Failed to transfer history (due to history subsumed) from db 2 to db 1 for value %ls", sczName); goto Skip; } else if (E_NOTFOUND == hr) { hr = S_OK; } else { ExitOnFailure(hr, "Failed to check if db1's value history is subsumed by db2's value history"); } if (0 < dwSubsumeIndex) { hr = ValueCompare(valueHistory1->valueHistory.rgcValues + dwSubsumeIndex, valueHistory1->valueHistory.rgcValues + dwSubsumeIndex - 1, FALSE, &fSame); ExitOnFailure(hr, "Failed to check if value and previous value in database 1 are equivalent"); } } while (0 < dwSubsumeIndex && fSame); } // OK, we have a conflict. Report it. if (NULL == *ppcpProduct) { *ppcpProduct = static_cast<CONFLICT_PRODUCT *>(MemAlloc(sizeof(CONFLICT_PRODUCT), TRUE)); ExitOnNull(*ppcpProduct, hr, E_OUTOFMEMORY, "Failed to allocate product conflict struct"); (*ppcpProduct)->cValues = 1; } else { ++(*ppcpProduct)->cValues; } hr = MemEnsureArraySize(reinterpret_cast<void **>(&(*ppcpProduct)->rgcesValueEnumLocal), (*ppcpProduct)->cValues, sizeof(CFG_ENUMERATION *), 10); ExitOnFailure(hr, "Failed to ensure product local value conflict array size"); hr = MemEnsureArraySize(reinterpret_cast<void **>(&(*ppcpProduct)->rgdwValueCountLocal), (*ppcpProduct)->cValues, sizeof(DWORD), 10); ExitOnFailure(hr, "Failed to ensure product local value conflict count array size"); hr = MemEnsureArraySize(reinterpret_cast<void **>(&(*ppcpProduct)->rgcesValueEnumRemote), (*ppcpProduct)->cValues, sizeof(CFG_ENUMERATION *), 10); ExitOnFailure(hr, "Failed to ensure product remote value conflict array size"); hr = MemEnsureArraySize(reinterpret_cast<void **>(&(*ppcpProduct)->rgdwValueCountRemote), (*ppcpProduct)->cValues, sizeof(DWORD), 10); ExitOnFailure(hr, "Failed to ensure product remote value conflict count array size"); hr = MemEnsureArraySize(reinterpret_cast<void **>(&(*ppcpProduct)->rgrcValueChoices), (*ppcpProduct)->cValues, sizeof(RESOLUTION_CHOICE), 10); ExitOnFailure(hr, "Failed to ensure product value resolution choice array size"); dwInserting = (*ppcpProduct)->cValues - 1; // Neither is subsumed by the other, so we have conflicts - report them if (fFirstIsLocal) { hr = ConflictGetList(reinterpret_cast<const CFG_ENUMERATION *>(valueHistory1), dwCfgCount1, reinterpret_cast<const CFG_ENUMERATION *>(valueHistory2), dwCfgCount2, &((*ppcpProduct)->rgcesValueEnumLocal[dwInserting]), &(*ppcpProduct)->rgdwValueCountLocal[dwInserting], &((*ppcpProduct)->rgcesValueEnumRemote[dwInserting]), &(*ppcpProduct)->rgdwValueCountRemote[dwInserting]); } else { hr = ConflictGetList(reinterpret_cast<const CFG_ENUMERATION *>(valueHistory2), dwCfgCount2, reinterpret_cast<const CFG_ENUMERATION *>(valueHistory1), dwCfgCount1, &((*ppcpProduct)->rgcesValueEnumLocal[dwInserting]), &(*ppcpProduct)->rgdwValueCountLocal[dwInserting], &((*ppcpProduct)->rgcesValueEnumRemote[dwInserting]), &(*ppcpProduct)->rgdwValueCountRemote[dwInserting]); } ExitOnFailure(hr, "Failed to get conflict list"); Skip: ReleaseNullSceRow(sceRow); hr = SceGetNextResultRow(sqrhResults, &sceRow); } hr = S_OK; LExit: ReleaseSceQuery(sqhHandle); ReleaseSceQueryResults(sqrhResults); ReleaseSceRow(sceRow); CfgReleaseEnumeration(valueHistory1); CfgReleaseEnumeration(valueHistory2); ReleaseStr(sczName); return hr; }
extern "C" HRESULT RegDefaultReadValue( __in CFGDB_STRUCT *pcdb, __in LEGACY_SYNC_PRODUCT_SESSION *pSyncProductSession, __in_z LPCWSTR wzNamespace, __in HKEY hkKey, __in_z LPCWSTR wzRegKey, __in_z LPCWSTR wzValueName, __in DWORD dwRegType ) { HRESULT hr = S_OK; BOOL fIgnore = FALSE; LPWSTR sczCfgValueName = NULL; hr = MapRegValueToCfgName(wzNamespace, wzRegKey, wzValueName, &sczCfgValueName); ExitOnFailure(hr, "Failed to format default legacy value name from namespace: %ls, key: %ls, valuename: %ls", wzNamespace, wzRegKey, wzValueName); hr = FilterCheckValue(&pSyncProductSession->product, sczCfgValueName, &fIgnore, NULL); ExitOnFailure(hr, "Failed to check if cfg value should be ignored: %ls", sczCfgValueName); if (fIgnore) { ExitFunction1(hr = S_OK); } hr = DictAddKey(pSyncProductSession->shDictValuesSeen, sczCfgValueName); ExitOnFailure(hr, "Failed to add to dictionary value: %ls", sczCfgValueName); switch (dwRegType) { case REG_BINARY: hr = RegDefaultReadValueBinary(pcdb, hkKey, wzValueName, sczCfgValueName); ExitOnFailure(hr, "Failed to handle binary value by default handler while reading from registry: %ls", wzValueName); break; case REG_SZ: hr = RegDefaultReadValueString(pcdb, hkKey, wzValueName, sczCfgValueName); ExitOnFailure(hr, "Failed to handle string value by default handler while reading from registry: %ls", wzValueName); break; case REG_DWORD: hr = RegDefaultReadValueDword(pcdb, hkKey, wzValueName, sczCfgValueName); ExitOnFailure(hr, "Failed to handle dword value by default handler while reading from registry: %ls", wzValueName); break; case REG_QWORD: hr = RegDefaultReadValueQword(pcdb, hkKey, wzValueName, sczCfgValueName); ExitOnFailure(hr, "Failed to handle qword value by default handler while reading from registry: %ls", wzValueName); break; default: // Ignore this value, it's unsupported ExitFunction1(hr = S_OK); break; } LExit: ReleaseStr(sczCfgValueName); return hr; }