//------------------------------------------------------------------------------ // // File: xml_supp.cpp // Copyright (C) 1995-2000 Microsoft Corporation // All rights reserved. // // Purpose: // implements helper functions for parsing XML document // //------------------------------------------------------------------------------ #include "stdafx.h" #include "xml_supp.h" // include "strings.h" to get tag names and attribute names for XML elements in MSC file #define INIT_MMC_BASE_STRINGS #include "strings.h" // note if you want to untie the project from MMC, copy the definitions for // the following strings from strings.h here: /* XML_TAG_MMC_CONSOLE_FILE; XML_TAG_MMC_STRING_TABLE; XML_TAG_STRING_TABLE_MAP; XML_TAG_STRING_TABLE; XML_TAG_VALUE_GUID; XML_TAG_STRING_TABLE_STRING; XML_ATTR_STRING_TABLE_STR_ID; */ LPCSTR strXMLStringTablePath[] = { XML_TAG_MMC_CONSOLE_FILE, XML_TAG_MMC_STRING_TABLE, XML_TAG_STRING_TABLE_MAP }; /***************************************************************************\ * * METHOD: LocateNextElementNode * * PURPOSE: locates sibling node of type ELEMENT * * PARAMETERS: * IXMLDOMNode *pNode [in] - node which sibling to locate * IXMLDOMNode **ppNode [out] - sibling node * * RETURNS: * HRESULT - result code * \***************************************************************************/ static HRESULT LocateNextElementNode(IXMLDOMNode *pNode, IXMLDOMNode **ppNode) { // parameter check if (ppNode == NULL) return E_INVALIDARG; // init out parameter *ppNode = NULL; // check [in] parameter if (pNode == NULL) return E_INVALIDARG; // loop thru siblings CComPtr spCurrNode = pNode; CComPtr spResultNode; while (1) { // get sibling node HRESULT hr = spCurrNode->get_nextSibling(&spResultNode); if (FAILED(hr)) return hr; // check the pointer if (spResultNode == NULL) return E_FAIL; // not found // done if it's ELEMENT node DOMNodeType elType = NODE_INVALID; spResultNode->get_nodeType(&elType); if (elType == NODE_ELEMENT) { *ppNode = spResultNode.Detach(); return S_OK; } // get to the next one spCurrNode = spResultNode; } return E_UNEXPECTED; } /***************************************************************************\ * * METHOD: OpenXMLStringTable * * PURPOSE: Opens XML document and locates string table node in it * * PARAMETERS: * LPCWSTR lpstrFileName - [in] file to load document from * IXMLDOMNode **ppStringTableNode - [out] pointer to node containing string table * * RETURNS: * HRESULT - result code * \***************************************************************************/ HRESULT OpenXMLStringTable(LPCWSTR lpstrFileName, IXMLDOMNode **ppStringTableNode) { // do parameter check if (lpstrFileName == NULL || ppStringTableNode == NULL) return E_INVALIDARG; // init return value *ppStringTableNode = NULL; // cocreate xml document CComQIPtr spDocument; HRESULT hr = spDocument.CoCreateInstance(CLSID_DOMDocument); if (FAILED(hr)) return hr; // prevent re-formating spDocument->put_preserveWhiteSpace(VARIANT_TRUE); // load the file VARIANT_BOOL bOK = VARIANT_FALSE; hr = spDocument->load(CComVariant(lpstrFileName), &bOK); if (hr != S_OK || bOK != VARIANT_TRUE) return FAILED(hr) ? hr : E_FAIL; // the path represents element tags in similar to the file system manner // so 'c' from can be selected by "a/b/c" // construct the path std::string strPath; for (int i = 0; i< sizeof(strXMLStringTablePath)/sizeof(strXMLStringTablePath[0]); i++) strPath.append(i > 0 ? 1 : 0, '/' ).append(strXMLStringTablePath[i]); // locate required node hr = spDocument->selectSingleNode(CComBSTR(strPath.c_str()), ppStringTableNode); if (FAILED(hr)) return hr; return S_OK; } /***************************************************************************\ * * METHOD: SaveXMLContents * * PURPOSE: Saves XML document to file * * PARAMETERS: * LPCWSTR lpstrFileName [in] - file to save to * IXMLDOMNode *pStringTableNode [in] - pointer to document's element * * RETURNS: * HRESULT - result code * \***************************************************************************/ HRESULT SaveXMLContents(LPCWSTR lpstrFileName, IXMLDOMNode *pStringTableNode) { // do parameter check if (lpstrFileName == NULL || pStringTableNode == NULL) return E_INVALIDARG; // get the document CComQIPtr spDocument; HRESULT hr = pStringTableNode->get_ownerDocument(&spDocument); if (FAILED(hr)) return hr; // save the file hr = spDocument->save(CComVariant(lpstrFileName)); if (FAILED(hr)) return hr; return S_OK; } /***************************************************************************\ * * METHOD: GetXMLElementContents * * PURPOSE: retuns XML text elements' contents as BSTR * * PARAMETERS: * IXMLDOMNode *pNode [in] - node which contents is requested * CComBSTR& bstrResult [out] - resulting string * * RETURNS: * HRESULT - result code * \***************************************************************************/ HRESULT GetXMLElementContents(IXMLDOMNode *pNode, CComBSTR& bstrResult) { // init result bstrResult.Empty(); // parameter check if (pNode == NULL) return E_INVALIDARG; // locate required node CComQIPtr spTextNode; HRESULT hr = pNode->selectSingleNode(CComBSTR(L"text()"), &spTextNode); if (FAILED(hr)) return hr; // recheck the pointer if (spTextNode == NULL) return E_POINTER; // done return spTextNode->get_text(&bstrResult); } /***************************************************************************\ * * METHOD: ReadXMLStringTables * * PURPOSE: Reads string tables to std::map - based structure * * PARAMETERS: * IXMLDOMNode *pNode [in] - string table node * CStringTableMap& mapResult [out] - map containing string tables * * RETURNS: * HRESULT - result code * \***************************************************************************/ HRESULT ReadXMLStringTables(IXMLDOMNode *pNode, CStringTableMap& mapResult) { mapResult.clear(); // parameter check if (pNode == NULL) return E_INVALIDARG; // get the node list CComQIPtr spGUIDNodes; HRESULT hr = pNode->selectNodes(CComBSTR(XML_TAG_VALUE_GUID), &spGUIDNodes); if (FAILED(hr)) return hr; // recheck the pointer if (spGUIDNodes == NULL) return E_POINTER; // get the item count long length = 0; hr = spGUIDNodes->get_length(&length); if (FAILED(hr)) return hr; // read the items for (int n = 0; n < length; n++) { // get one node CComQIPtr spGUIDNode; hr = spGUIDNodes->get_item(n, &spGUIDNode); if (FAILED(hr)) return hr; // read the text CComBSTR bstrLastGUID; hr = GetXMLElementContents(spGUIDNode, bstrLastGUID); if (FAILED(hr)) return hr; // Add the entry to the map; CStringMap& rMapStrings = mapResult[static_cast(bstrLastGUID)]; //get the strings node following the guid CComPtr spStringsNode; hr = LocateNextElementNode(spGUIDNode, &spStringsNode); if (FAILED(hr)) return hr; // recheck if (spStringsNode == NULL) return E_POINTER; // select strings for this guid CComQIPtr spStringNodeList; HRESULT hr = spStringsNode->selectNodes(CComBSTR(XML_TAG_STRING_TABLE_STRING), &spStringNodeList); if (FAILED(hr)) return hr; // recheck the pointer if (spStringNodeList == NULL) return E_POINTER; // count the strings long nStrCount = 0; hr = spStringNodeList->get_length(&nStrCount); if (FAILED(hr)) return hr; // add all the strings to map CComQIPtr spStringNode; for(int iStr = 0; iStr < nStrCount; iStr++) { // get n-th string spStringNode.Release(); hr = spStringNodeList->get_item(iStr, &spStringNode); if (FAILED(hr)) return hr; CComQIPtr spElement = spStringNode; if (spElement == NULL) return E_UNEXPECTED; // get string id CComVariant val; hr = spElement->getAttribute(CComBSTR(XML_ATTR_STRING_TABLE_STR_ID), &val); if (FAILED(hr)) continue; DWORD dwID = val.bstrVal ? wcstoul(val.bstrVal, NULL, 10) : 0; // get string text CComBSTR bstrText; hr = GetXMLElementContents(spStringNode, bstrText); if (FAILED(hr)) return hr; // add to the map rMapStrings[dwID] = bstrText; } } return S_OK; } /***************************************************************************\ * * METHOD: UpdateXMLString * * PURPOSE: Updates string in string table * * PARAMETERS: * IXMLDOMNode *pNode [in] - string tables * const std::wstring& strGUID [in] - GUID of string table * DWORD ID [in] - id of string * const std::wstring& strNewVal [in] - new value for string * * RETURNS: * HRESULT - result code * \***************************************************************************/ HRESULT UpdateXMLString(IXMLDOMNode *pNode, const std::wstring& strGUID, DWORD ID, const std::wstring& strNewVal) { // parameter check if (pNode == NULL) return E_INVALIDARG; USES_CONVERSION; // locate the GUID node std::wstring strTagGUID(T2CW(XML_TAG_VALUE_GUID)); std::wstring strGUIDPattern( strTagGUID + L"[text() = \"" + strGUID + L"\"]" ); CComQIPtr spGUIDNode; HRESULT hr = pNode->selectSingleNode(CComBSTR(strGUIDPattern.c_str()), &spGUIDNode); if (FAILED(hr)) return hr; // recheck if (spGUIDNode == NULL) return E_POINTER; //get the strings node following the guid CComPtr spStringsNode; hr = LocateNextElementNode(spGUIDNode, &spStringsNode); if (FAILED(hr)) return hr; // recheck if (spStringsNode == NULL) return E_POINTER; // locate the string node by ID (actually its text node) CString strPattern; strPattern.Format("%s[@%s = %d]/text()", XML_TAG_STRING_TABLE_STRING, XML_ATTR_STRING_TABLE_STR_ID, ID); CComQIPtr spTextNode; hr = spStringsNode->selectSingleNode(CComBSTR(strPattern), &spTextNode); if (FAILED(hr)) return hr; // recheck if (spTextNode == NULL) return E_POINTER; // set the contents hr = spTextNode->put_text(CComBSTR(strNewVal.c_str())); if (FAILED(hr)) return hr; return S_OK; // done }