405 lines
12 KiB
C++
405 lines
12 KiB
C++
//------------------------------------------------------------------------------
|
|
//
|
|
// 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<IXMLDOMNode> spCurrNode = pNode;
|
|
CComPtr<IXMLDOMNode> 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<IXMLDOMDocument> 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 <a><b><c/></b></a> 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 <any> 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<IXMLDOMDocument> 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<IXMLDOMNode> 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<IXMLDOMNodeList> 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<IXMLDOMNode> 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<LPOLESTR>(bstrLastGUID)];
|
|
|
|
//get the strings node following the guid
|
|
CComPtr<IXMLDOMNode> spStringsNode;
|
|
hr = LocateNextElementNode(spGUIDNode, &spStringsNode);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// recheck
|
|
if (spStringsNode == NULL)
|
|
return E_POINTER;
|
|
|
|
// select strings for this guid
|
|
CComQIPtr<IXMLDOMNodeList> 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<IXMLDOMNode> 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<IXMLDOMElement> 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<IXMLDOMNode> 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<IXMLDOMNode> 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<IXMLDOMNode> 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
|
|
}
|
|
|
|
|