/*++ Copyright (c) 1999-2001 Microsoft Corporation Module Name: merge.cpp Abstract: This module implements routines for service specific SSR Knowledge Base merging via KBreg.xml. Author: Vishnu Patankar (VishnuP) - Jun 2002 Environment: User mode only. Exported Functions: Revision History: Created - Jun 2002 --*/ #include "stdafx.h" #include "kbproc.h" #include "process.h" #include HRESULT process::SsrpProcessKBsMerge( IN PWSTR pszKBDir, IN PWSTR pszMachineName, OUT IXMLDOMElement **ppElementRoot, OUT IXMLDOMDocument **ppXMLDoc ) /*++ Routine Description: Routine called to merge KBs Arguments: pszKBDir - the root directory from which to get the KBs pszMachineName - name of the machine to preprocess ppElementRoot - the root element pointer to be filled in ppXMLDoc - document pointer to be filled in Return: HRESULT error code ++*/ { // // load the KB registration document // WCHAR szKBregs[MAX_PATH + 50]; WCHAR szWindir[MAX_PATH + 50]; WCHAR szMergedKB[MAX_PATH + 50]; DWORD rc = ERROR_SUCCESS; HRESULT hr = S_OK; CComPtr pXMLKBDoc; OSVERSIONINFOEX osVersionInfo; CComPtr pKBList; CComPtr pKB; CComPtr pXMLDocElemRoot; BOOL bOsKbMatch = FALSE; if ( !GetSystemWindowsDirectory(szWindir, MAX_PATH + 1) ) { SsrpLogError(L"Error GetSystemWindowsDirectory() \n"); SsrpLogWin32Error(GetLastError()); return E_INVALIDARG; } wcscpy(szMergedKB, szWindir); wcscat(szMergedKB, L"\\security\\ssr\\kbs\\MergedRawKB.xml"); CComVariant MergedKB(szMergedKB); wcscpy(szKBregs, pszKBDir); wcscat(szKBregs, L"KBreg.xml"); CComVariant KBregsFile(szKBregs); hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**)&pXMLKBDoc); if (FAILED(hr) || pXMLKBDoc == NULL ) { SsrpLogError(L"COM failed to create a DOM instance"); goto ExitHandler; } VARIANT_BOOL vtSuccess; hr = pXMLKBDoc->load(KBregsFile, &vtSuccess); if (FAILED(hr) || vtSuccess == VARIANT_FALSE ) { SsrpLogParseError(hr); goto ExitHandler; } // // get the root element // hr = pXMLKBDoc->get_documentElement(&pXMLDocElemRoot); if (FAILED(hr) || pXMLDocElemRoot == NULL ) { SsrpLogParseError(hr); goto ExitHandler; } if (NULL == pszMachineName) { // // local machine // osVersionInfo.dwOSVersionInfoSize = sizeof(osVersionInfo); if (!GetVersionEx((LPOSVERSIONINFOW)&osVersionInfo)){ SsrpLogError(L"Error GetVersionEx \n"); SsrpLogWin32Error(GetLastError()); goto ExitHandler; } } else { // // remote machine - use WMI // hr = SsrpGetRemoteOSVersionInfo(pszMachineName, &osVersionInfo); if (FAILED(hr)) { SsrpLogError(L"SsrpGetRemoteOSVersionInfo failed"); goto ExitHandler; } } hr = pXMLDocElemRoot->selectNodes(L"KBs", &pKBList); if (FAILED(hr) || pKBList == NULL ) { SsrpLogParseError(hr); goto ExitHandler; } hr = pKBList->nextNode(&pKB); if (FAILED(hr) || pKB == NULL ) { SsrpLogParseError(hr); goto ExitHandler; } while (pKB) { CComBSTR bstrText; CComPtr pName; CComPtr pXDNodeServiceStartup; CComPtr pXMLAttribNode; CComPtr pXMLMajorInfo; CComPtr pXMLMinorInfo; hr = pKB->get_attributes( &pXMLAttribNode ); if (FAILED(hr) || pXMLAttribNode == NULL){ SsrpLogParseError(hr); goto ExitHandler; } hr = pXMLAttribNode->getNamedItem(L"OSVersionMajorInfo", &pXMLMajorInfo ); if (FAILED(hr) || pXMLMajorInfo == NULL){ SsrpLogParseError(hr); goto ExitHandler; } hr = pXMLAttribNode->getNamedItem(L"OSVersionMinorInfo", &pXMLMinorInfo ); if (FAILED(hr) || pXMLMinorInfo == NULL){ SsrpLogParseError(hr); goto ExitHandler; } CComBSTR bstrValue; DWORD dwMajor; DWORD dwMinor; hr = pXMLMajorInfo->get_text(&bstrValue); if (FAILED(hr) || !bstrValue ) { SsrpLogParseError(hr); goto ExitHandler; } dwMajor = _wtoi(bstrValue); hr = pXMLMinorInfo->get_text(&bstrValue); if (FAILED(hr) || !bstrValue ) { SsrpLogParseError(hr); goto ExitHandler; } dwMinor = _wtoi(bstrValue); if (osVersionInfo.dwMajorVersion == dwMajor && osVersionInfo.dwMinorVersion == dwMinor) { // // got the required KB node // bOsKbMatch = TRUE; break; } hr = pKBList->nextNode(&pKB); if (FAILED(hr) || pKB == NULL ) { SsrpLogParseError(hr); goto ExitHandler; } } if (bOsKbMatch == FALSE) { SsrpLogError(L"Failed to map OSversion to KB information in registration"); hr = E_INVALIDARG; goto ExitHandler; } // // merge according to precedence // hr = SsrpMergeAccordingToPrecedence(L"Extensions", pszKBDir, ppElementRoot, ppXMLDoc, pKB); if (FAILED(hr)) { SsrpLogError(L"Failed to merge Extension KB"); goto ExitHandler; } hr = SsrpMergeAccordingToPrecedence(L"Root", pszKBDir, ppElementRoot, ppXMLDoc, pKB); if (FAILED(hr)) { SsrpLogError(L"Failed to merge Root KB"); goto ExitHandler; } hr = SsrpMergeAccordingToPrecedence(L"Custom", pszKBDir, ppElementRoot, ppXMLDoc, pKB); if (FAILED(hr)) { SsrpLogError(L"Failed to merge Custom KB"); goto ExitHandler; } hr = SsrpOverwriteServiceLocalizationFromSystem(*ppElementRoot, *ppXMLDoc); if (FAILED(hr)) { SsrpLogError(L"Failed to merge Custom KB"); goto ExitHandler; } hr = (*ppXMLDoc)->save(MergedKB); if (FAILED(hr)) { SsrpLogParseError(hr); } ExitHandler: return hr; } HRESULT process::SsrpGetRemoteOSVersionInfo( IN PWSTR pszMachineName, OUT OSVERSIONINFOEX *posVersionInfo ) /*++ Routine Description: Routine called to get version info from remote machine via WMI Arguments: pszMachineName - remote machine name posVersionInfo - os version info to fill via WMI queries Return: HRESULT error code ++*/ { HRESULT hr = S_OK; CComPtr pWbemLocator = NULL; CComPtr pWbemServices = NULL; CComPtr pWbemOsObjectInstance = NULL; CComPtr pWbemEnumObject = NULL; CComBSTR bstrMachineAndNamespace; ULONG nReturned = 0; bstrMachineAndNamespace = pszMachineName; bstrMachineAndNamespace += L"\\root\\cimv2"; hr = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pWbemLocator ); if (FAILED(hr) || pWbemLocator == NULL ) { SsrpLogError(L"Error getting instance of CLSID_WbemLocator \n"); SsrpLogParseError(hr); goto ExitHandler; } hr = pWbemLocator->ConnectServer( bstrMachineAndNamespace, NULL, NULL, NULL, 0L, NULL, NULL, &pWbemServices ); if (FAILED(hr) || pWbemServices == NULL ) { SsrpLogError(L"Error ConnectServer \n"); SsrpLogParseError(hr); goto ExitHandler; } hr = CoSetProxyBlanket( pWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE ); if (FAILED(hr)) { SsrpLogError(L"Error CoSetProxyBlanket \n"); SsrpLogParseError(hr); goto ExitHandler; } hr = pWbemServices->ExecQuery(CComBSTR(L"WQL"), CComBSTR(L"SELECT * FROM Win32_OperatingSystem"), WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &pWbemEnumObject); if (FAILED(hr) || pWbemEnumObject == NULL) { SsrpLogError(L"Error SELECT * FROM Win32_OperatingSystem\n"); SsrpLogParseError(hr); goto ExitHandler; } hr = pWbemEnumObject->Next(WBEM_INFINITE, 1, &pWbemOsObjectInstance, &nReturned); if (FAILED(hr) || pWbemOsObjectInstance == NULL) { SsrpLogError(L"Error enumerating\n"); SsrpLogParseError(hr); goto ExitHandler; } VARIANT vVersion; VariantInit(&vVersion); hr = pWbemOsObjectInstance->Get(CComBSTR(L"Version"), 0, &vVersion, NULL, NULL); if (FAILED(hr)) { SsrpLogError(L"Error getting Version property \n"); SsrpLogParseError(hr); goto ExitHandler; } if (V_VT(&vVersion) == VT_NULL) { SsrpLogError(L"Error Version property is null\n"); goto ExitHandler; } // // extract the version information into DWORDs since // the return type of this property is BSTR variant // of the form "5.1.2195" // BSTR bstrVersion = V_BSTR(&vVersion); WCHAR szVersion[5]; szVersion[0] = L'\0'; PWSTR pszDot = wcsstr(bstrVersion, L"."); if (NULL == pszDot) { SsrpLogError(L"Version property has no '.' \n"); hr = E_INVALIDARG; goto ExitHandler; } wcsncpy(szVersion, bstrVersion, 1); posVersionInfo->dwMajorVersion = (DWORD)_wtoi(szVersion); wcsncpy(szVersion, pszDot+1, 1); posVersionInfo->dwMinorVersion = (DWORD)_wtoi(szVersion); ExitHandler: if (V_VT(&vVersion) != VT_NULL) { VariantClear( &vVersion ); } return hr; } HRESULT process::SsrpMergeAccordingToPrecedence( IN PWSTR pszKBType, IN PWSTR pszKBDir, OUT IXMLDOMElement **ppElementRoot, OUT IXMLDOMDocument **ppXMLDoc, IN IXMLDOMNode *pKB ) /*++ Routine Description: Routine called to load and merge XML KBs Arguments: pszKBType - type of KB - i.e. Custom/Extension/Root pszKBDir - path to KB directory ppElementRoot - the root element pointer to be filled in ppXMLDoc - document pointer to be filled in pKB - pointer to KB registration node Return: HRESULT error code ++*/ { HRESULT hr = S_OK; WCHAR szKBandName[MAX_PATH]; CComPtr pKBList; WCHAR szKBFile[MAX_PATH + 20]; WCHAR szWindir[MAX_PATH + 20]; wcscpy(szKBandName, pszKBType); wcscat(szKBandName, L"/Name"); hr = pKB->selectNodes(szKBandName, &pKBList); if (FAILED(hr) || pKBList == NULL ) { SsrpLogError(L"No KBs in this category \n"); hr = S_OK; goto ExitHandler; } hr = pKBList->nextNode(&pKB); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } while (pKB) { CComBSTR bstrValue; hr = pKB->get_text(&bstrValue); if (FAILED(hr) || !bstrValue ) { SsrpLogParseError(hr); goto ExitHandler; } SsrpConvertBstrToPwstr(bstrValue); wcscpy(szKBFile, pszKBDir); wcscat(szKBFile, bstrValue); if ( 0xFFFFFFFF == GetFileAttributes(szKBFile) ) { SsrpLogError(L"KB File not found"); hr = E_INVALIDARG; goto ExitHandler; } hr = SsrpMergeDOMTrees(ppElementRoot, ppXMLDoc, szKBFile); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } hr = pKBList->nextNode(&pKB); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } } ExitHandler: return hr; } HRESULT process::SsrpMergeDOMTrees( OUT IXMLDOMElement **ppMergedKBElementRoot, OUT IXMLDOMDocument **ppMergedKBXMLDoc, IN WCHAR *szXMLFileName ) /*++ Routine Description: Routine called to load and merge XML KBs Arguments: *ppElementRoot - pointer to final merged KB root *ppXMLDoc - pointer to final merged KB doc to which merges are made Return: HRESULT error code ++*/ { CComPtr pXMLKBDoc; CComPtr pXMLKBElemRoot; CComVariant KBFile(szXMLFileName); CComPtr pNewNode; HRESULT hr = S_OK; VARIANT_BOOL vtSuccess = VARIANT_FALSE; // // instantiate DOM document object to read and store each KB // hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**)&pXMLKBDoc); if (FAILED(hr) || pXMLKBDoc == NULL ) { SsrpLogError(L"COM failed to create a DOM instance"); goto ExitHandler; } hr = pXMLKBDoc->put_preserveWhiteSpace(VARIANT_TRUE); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } // // load the KB XML into DOM // hr = pXMLKBDoc->load(KBFile, &vtSuccess); if (FAILED(hr) || vtSuccess == VARIANT_FALSE ) { SsrpLogParseError(hr); goto ExitHandler; } // // get the root element // hr = pXMLKBDoc->get_documentElement(&pXMLKBElemRoot); if (FAILED(hr) || pXMLKBElemRoot == NULL ) { SsrpLogParseError(hr); goto ExitHandler; } if (*ppMergedKBElementRoot == NULL) { // // special case: this is the first KB, so simply clone the empty merged KB tree with it // hr = pXMLKBElemRoot->cloneNode(VARIANT_TRUE, &pNewNode); if (FAILED(hr) || pNewNode == NULL ) { SsrpLogParseError(hr); goto ExitHandler; } hr = (*ppMergedKBXMLDoc)->appendChild(pNewNode, NULL); if (FAILED(hr) ) { SsrpLogParseError(hr); goto ExitHandler; } // // update the empty values so that next time around, we know that // the merged KB is initialized with the first KB // hr = (*ppMergedKBXMLDoc)->get_documentElement(ppMergedKBElementRoot); if (FAILED(hr) || *ppMergedKBElementRoot == NULL ) { SsrpLogParseError(hr); goto ExitHandler; } goto ExitHandler; } // // this is not the first KB - perform actual merges in the following way: // // number of mergeable entities in MergedKB = n // number of mergeable entities in CurrentKB = m // // O(m x n) algorithm for merging: // // foreach mergeable entity in CurrentKB // foreach mergeable entity in MergedKB // if no based collision // append entity from CurrentKB into MergedKB // else // replace existing entity in MergedKB by entity from CurrentKB // hr = SsrpAppendOrReplaceMergeableEntities(L"Description/Name", *ppMergedKBElementRoot, *ppMergedKBXMLDoc, pXMLKBDoc, pXMLKBElemRoot, szXMLFileName ); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } hr = SsrpAppendOrReplaceMergeableEntities(L"SecurityLevels/Level/Name", *ppMergedKBElementRoot, *ppMergedKBXMLDoc, pXMLKBDoc, pXMLKBElemRoot, szXMLFileName ); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } hr = SsrpAppendOrReplaceMergeableEntities(L"Roles/Role/Name", *ppMergedKBElementRoot, *ppMergedKBXMLDoc, pXMLKBDoc, pXMLKBElemRoot, szXMLFileName ); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } hr = SsrpAppendOrReplaceMergeableEntities(L"Tasks/Task/Name", *ppMergedKBElementRoot, *ppMergedKBXMLDoc, pXMLKBDoc, pXMLKBElemRoot, szXMLFileName ); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } hr = SsrpAppendOrReplaceMergeableEntities(L"Services/Service/Name", *ppMergedKBElementRoot, *ppMergedKBXMLDoc, pXMLKBDoc, pXMLKBElemRoot, szXMLFileName ); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } hr = SsrpAppendOrReplaceMergeableEntities(L"RoleLocalization/Role/Name", *ppMergedKBElementRoot, *ppMergedKBXMLDoc, pXMLKBDoc, pXMLKBElemRoot, szXMLFileName ); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } hr = SsrpAppendOrReplaceMergeableEntities(L"TaskLocalization/Task/Name", *ppMergedKBElementRoot, *ppMergedKBXMLDoc, pXMLKBDoc, pXMLKBElemRoot, szXMLFileName ); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } hr = SsrpAppendOrReplaceMergeableEntities(L"ServiceLocalization/Service/Name", *ppMergedKBElementRoot, *ppMergedKBXMLDoc, pXMLKBDoc, pXMLKBElemRoot, szXMLFileName ); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } ExitHandler: return hr; } HRESULT process::SsrpAppendOrReplaceMergeableEntities( IN PWSTR pszFullyQualifiedEntityName, IN IXMLDOMElement *pMergedKBElementRoot, IN IXMLDOMDocument *pMergedKBXMLDoc, IN IXMLDOMDocument *pCurrentKBDoc, IN IXMLDOMElement *pCurrentKBElemRoot, IN PWSTR pszKBName ) /*++ Routine Description: Routine called to load and merge XML KBs Arguments: pszFullyQualifiedEntityName - string containing the entity name representing the entity pMergedKBElementRoot - pointer to final merged KB root pMergedKBXMLDoc - pointer to final merged KB doc to which merges are made pElementRoot - root of current KB pXMLDoc - pointer to current KB doc from which merges are made pszKBName - name of the source KB Return: HRESULT error code ++*/ { HRESULT hr = S_OK; CComPtr pNameCurrent; CComPtr pNameListCurrent; CComPtr pAttribNodeMap; CComPtr pAttrib; CComBSTR bstrSourceKB(L"SourceKB"); CComBSTR bstrSourceKBName(wcsrchr(pszKBName, L'\\')+1); hr = pCurrentKBElemRoot->selectNodes(pszFullyQualifiedEntityName, &pNameListCurrent); if (FAILED(hr) || pNameListCurrent == NULL ) { hr = S_OK; goto ExitHandler; } hr = pNameListCurrent->nextNode(&pNameCurrent); if (FAILED(hr) || pNameCurrent == NULL) { #if 0 // // no need to error out if these nodes are not present in the source KB // #endif SsrpLogParseError(hr); goto ExitHandler; } while (pNameCurrent) { CComBSTR bstrCurrentText; CComPtr pNameMerged; CComPtr pNameListMerged; CComPtr pRootOfEntityName; LONG ulLength; hr = pNameCurrent->get_text(&bstrCurrentText); if (FAILED(hr) || !bstrCurrentText ) { SsrpLogParseError(hr); goto ExitHandler; } hr = pMergedKBElementRoot->selectNodes(pszFullyQualifiedEntityName, &pNameListMerged); if (FAILED(hr) || pNameListMerged == NULL ) { SsrpLogParseError(hr); goto ExitHandler; } hr = pNameListMerged->get_length(&ulLength); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } if (ulLength == 0) { PWSTR pszRootOfFullyQualifiedEntityName; WCHAR szRootOfEntityName[MAX_PATH]; memset(szRootOfEntityName, L'\0', MAX_PATH * sizeof(WCHAR)); // // no need to error out if these nodes are not present - but append is necessary // wcscpy(szRootOfEntityName, pszFullyQualifiedEntityName); pszRootOfFullyQualifiedEntityName = wcschr(szRootOfEntityName, L'/'); pszRootOfFullyQualifiedEntityName[0] = L'\0'; hr = pCurrentKBElemRoot->selectSingleNode(szRootOfEntityName, &pRootOfEntityName); if (FAILED(hr) || pRootOfEntityName == NULL) { SsrpLogParseError(hr); goto ExitHandler; } hr = pMergedKBElementRoot->appendChild(pRootOfEntityName, NULL); if (FAILED(hr)) { SsrpLogParseError(hr); } goto ExitHandler; } hr = pNameListMerged->nextNode(&pNameMerged); if (FAILED(hr) || pNameMerged == NULL) { SsrpLogParseError(hr); goto ExitHandler; } while (pNameMerged) { CComBSTR bstrMergedText; CComPtr pCurrentNameParent; CComPtr pMergedNameParent; CComPtr pMergedNameGrandParent; hr = pNameMerged->get_text(&bstrMergedText); if (FAILED(hr) || !bstrMergedText ) { SsrpLogParseError(hr); goto ExitHandler; } hr = pNameCurrent->get_parentNode(&pCurrentNameParent); if (FAILED(hr) || pCurrentNameParent == NULL ) { SsrpLogParseError(hr); goto ExitHandler; } hr = pNameMerged->get_parentNode(&pMergedNameParent); if (FAILED(hr) || pMergedNameParent == NULL ) { SsrpLogParseError(hr); goto ExitHandler; } hr = pMergedNameParent->get_parentNode(&pMergedNameGrandParent); if (FAILED(hr) || pMergedNameGrandParent == NULL ) { SsrpLogParseError(hr); goto ExitHandler; } hr = pCurrentNameParent->get_attributes(&pAttribNodeMap); if (FAILED(hr) || pAttribNodeMap == NULL ) { SsrpLogParseError(hr); goto ExitHandler; } hr = pCurrentKBDoc->createAttribute( bstrSourceKB, &pAttrib ); if (FAILED(hr) || pAttrib == NULL ) { SsrpLogParseError(hr); goto ExitHandler; } hr = pAttrib->put_text(bstrSourceKBName); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } hr = pAttribNodeMap->setNamedItem(pAttrib, NULL); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } if (0 == SsrpICompareBstrPwstr(bstrCurrentText, bstrMergedText)) { // // collision - need to delete pNameMerged's parent and // replace pMergedNameParent with pCurrentNameParent // hr = pMergedNameGrandParent->replaceChild(pCurrentNameParent, pMergedNameParent, NULL); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } } else { // // no collision - need to append pNameCurrent's parent to // pNameMerged's grandparent's section // hr = pMergedNameGrandParent->appendChild(pCurrentNameParent, NULL); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } } hr = pNameListMerged->nextNode(&pNameMerged); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } } hr = pNameListCurrent->nextNode(&pNameCurrent); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } } ExitHandler: return hr; } HRESULT process::SsrpOverwriteServiceLocalizationFromSystem( IN IXMLDOMElement *pMergedKBElementRoot, IN IXMLDOMDocument *pMergedKBXMLDoc ) /*++ Routine Description: Routine called to overwrite service info in localization section Arguments: pMergedKBElementRoot - pointer to root of merged DOM pMergedKBXMLDoc - pointer to merged Document Return: HRESULT error code ++*/ { CComPtr pServiceName; CComPtr pServiceNameList; HRESULT hr = S_OK; hr = pMergedKBElementRoot->selectNodes(L"ServiceLocalization/Service/Name", &pServiceNameList); if (FAILED(hr) || pServiceNameList == NULL ) { SsrpLogParseError(hr); goto ExitHandler; } hr = pServiceNameList->nextNode(&pServiceName); if (FAILED(hr) || pServiceName == NULL) { SsrpLogParseError(hr); goto ExitHandler; } while (pServiceName) { CComBSTR bstrServiceText; PWSTR pszDescription = NULL; PWSTR pszDisplay = NULL; LPSERVICE_DESCRIPTION pServiceDescription = NULL; hr = pServiceName->get_text(&bstrServiceText); if (FAILED(hr) || !bstrServiceText) { SsrpLogError(L"Failed to "); goto ExitHandler; } pszDisplay = SsrpQueryServiceDisplayName(bstrServiceText); SsrpConvertBstrToPwstr(bstrServiceText); if ( SsrpQueryServiceDescription(bstrServiceText, &pServiceDescription) && pServiceDescription != NULL){ pszDescription = pServiceDescription->lpDescription; } if ( pszDisplay != NULL && pszDescription != NULL) { CComPtr pServiceNameParent; CComPtr pDescription; CComPtr pDisplayName; pServiceName->get_parentNode(&pServiceNameParent); if (FAILED(hr) || pServiceNameParent == NULL) { SsrpLogParseError(hr); goto ExitHandler; } hr = pServiceNameParent->selectSingleNode(L"Description", &pDescription); if (FAILED(hr) || pDescription == NULL) { SsrpLogParseError(hr); goto ExitHandler; } hr = pDescription->put_text(pszDescription); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } hr = pServiceNameParent->selectSingleNode(L"DisplayName", &pDisplayName); if (FAILED(hr) || pDisplayName == NULL) { SsrpLogParseError(hr); goto ExitHandler; } hr = pDisplayName->put_text(pszDisplay); if (FAILED(hr)) { SsrpLogParseError(hr); goto ExitHandler; } } if (pServiceDescription) { LocalFree(pServiceDescription); pServiceDescription = NULL; } hr = pServiceNameList->nextNode(&pServiceName); if (FAILED(hr)) { SsrpLogError(L"Failed to "); goto ExitHandler; } } ExitHandler: return hr; }