windows-nt/Source/XPSP1/NT/net/upnp/common/upbase/ncxml.cpp

2151 lines
57 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1999.
//
// File: ncxml.cpp
//
// Contents: helper functions for doing remarkably simple things
// with the XML DOM.
//
//----------------------------------------------------------------------------
#include "pch.h"
#pragma hdrstop
#include <oleauto.h> // for SysFreeString()
#include "ncbase.h"
#include "ncdebug.h"
#include "ncxml.h"
#include "ncinet2.h" // HrCombineUrl, HrCopyAndValidateUrl
struct DTNAME
{
LPCWSTR m_pszType;
SST_DATA_TYPE m_sdt;
};
// Lookup table, sorted by string
// Note: if you add strings here, you also have to add a
// corresponding entry to SST_DATA_TYPE
//
static CONST DTNAME g_rgdtTypeStrings[] =
{
{L"bin.base64", SDT_BIN_BASE64}, // base64 as defined in the MIME IETF spec
{L"bin.hex", SDT_BIN_HEX}, // Hexadecimal digits representing octets VT_ARRAY safearray or stream
{L"boolean", SDT_BOOLEAN}, // boolean, "1" or "0" VT_BOOL int
{L"char", SDT_CHAR}, // char, string VT_UI2 wchar
{L"date", SDT_DATE_ISO8601}, // date.iso8601, A date in ISO 8601 format. (no time) VT_DATE long
{L"dateTime", SDT_DATETIME_ISO8601}, // dateTime.iso8601, A date in ISO 8601 format, with optional time and no optional zone. Fractional seconds may be as precise as nanoseconds. VT_DATE long
{L"dateTime.tz", SDT_DATETIME_ISO8601TZ},// dateTime.iso8601tz, A date in ISO 8601 format, with optional time and optional zone. Fractional seconds may be as precise as nanoseconds. VT_DATE long
{L"fixed.14.4", SDT_FIXED_14_4}, // fixed.14.4, Same as "number" but no more than 14 digits to the left of the decimal point, and no more than 4 to the right. VT_CY large_integer
{L"float", SDT_FLOAT}, // float, Same as for "number." VT_R8 double
{L"i1", SDT_I1}, // i1, A number, with optional sign, no fractions, no exponent. VT_I1 char
{L"i2", SDT_I2}, // i2, " VT_I2 short
{L"i4", SDT_I4}, // i4, " VT_I4 long
{L"int", SDT_INT}, // int, A number, with optional sign, no fractions, no exponent. VT_I4 long
{L"number", SDT_NUMBER}, // number, A number, with no limit on digits, may potentially have a leading sign, fractional digits, and optionally an exponent. Punctuation as in US English. VT_R8 double
{L"r4", SDT_R4}, // r4, Same as "number." VT_FLOAT float
{L"r8", SDT_R8}, // r8, " VT_DOUBLE double
{L"string", SDT_STRING}, // string, pcdata VT_BSTR BSTR
{L"time", SDT_TIME_ISO8601}, // time.iso8601, A time in ISO 8601 format, with no date and no time zone. VT_DATE long
{L"time.tz", SDT_TIME_ISO8601TZ}, // time.iso8601.tz, A time in ISO 8601 format, with no date but optional time zone. VT_DATE. long
{L"ui1", SDT_UI1}, // ui1, A number, unsigned, no fractions, no exponent. VT_UI1 unsigned char
{L"ui2", SDT_UI2}, // ui2, " VT_UI2 unsigned short
{L"ui4", SDT_UI4}, // ui4, " VT_UI4 unsigned long
{L"uri", SDT_URI}, // uri, Universal Resource Identifier VT_BSTR BSTR
{L"uuid", SDT_UUID}, // uuid, Hexadecimal digits representing octets, optional embedded hyphens which should be ignored. VT_BSTR GUID
//
// note(cmr): ADD NEW VALUES IN ALPHABETICAL ORDER
//
};
// NOTE: the order of elements in this array must correspond to the order of
// elements in the SST_DATA_TYPE enum.
static CONST VARTYPE g_rgvtTypes[] =
{
VT_BSTR,
VT_BSTR,
VT_I4,
VT_CY,
VT_BOOL,
VT_DATE,
VT_DATE,
VT_DATE,
VT_DATE,
VT_DATE,
VT_I1,
VT_I2,
VT_I4,
VT_UI1,
VT_UI2,
VT_UI4,
VT_R4,
VT_R8,
VT_R8,
VT_BSTR,
VT_ARRAY,
VT_ARRAY,
VT_UI2,
VT_BSTR,
//
// note(cmr): ADD NEW VALUES IMMEDIATELY BEFORE THIS COMMENT.
// If adding new values, see comment above.
//
VT_EMPTY
};
/*
* Function: GetStringFromType()
*
* Purpose: Returns an xml-data type string for the specified
* SDT_DATA_TYPE value.
*
* Arguments:
* sdt [in] The data type whose descriptive string is desired.
* This must be a valid SST_DATA_TYPE value (less
* than SDT_INVALID)
*
* Returns:
* A pointer to a constant string id representing the type. This string
* should not be modified or freed.
* This method will never return NULL.
*
*/
LPCWSTR
GetStringFromType(CONST SST_DATA_TYPE sdt)
{
// there must be an entry in g_rgdtTypeStrings for every real
// value of SST_DATA_TYPE
//
Assert(SDT_INVALID == celems(g_rgdtTypeStrings));
Assert(sdt < SDT_INVALID);
// since comparing SDT_s is somewhat cheaper than comparing whole strings,
// we just walk through the whole list sequentually
//
CONST INT nSize = celems(g_rgdtTypeStrings);
LPCWSTR pszResult;
INT i;
pszResult = NULL;
i = 0;
for ( ; i < nSize; ++i)
{
SST_DATA_TYPE sdtCurrent;
sdtCurrent = g_rgdtTypeStrings[i].m_sdt;
if (sdt == sdtCurrent)
{
// we have a match
pszResult = g_rgdtTypeStrings[i].m_pszType;
break;
}
}
AssertSz(pszResult, "GetStringFromType: "
"sdt not found in g_rgdtTypeStrings!");
return pszResult;
}
/*
* Function: GetTypeFromString()
*
* Purpose: Returns the appropriate SDT_DATA_TYPE value for the given
* xml-data type string.
*
* Arguments:
* pszTypeString [in] The data type identifier whose SDT_DATA_TYPE value
* is desired.
*
* Returns:
* If the string is found, it returns the appropriate value of the
* SST_DATA_TYPE enumeration.
* If the string is not found, it returns SDT_INVALID.
*
* Notes:
* The source string is compared to known strings in a case-insensitive
* comparison.
*
*/
SST_DATA_TYPE
GetTypeFromString(LPCWSTR pszTypeString)
{
// there must be an entry in g_rgdtTypeStrings for every real
// value of SST_DATA_TYPE
//
Assert(SDT_INVALID == celems(g_rgdtTypeStrings));
SST_DATA_TYPE sdtResult;
sdtResult = SDT_INVALID;
{
// now search for the string in the list, using a
// standard binary search
//
INT nLow;
INT nMid;
INT nHigh;
nLow = 0;
nHigh = celems(g_rgdtTypeStrings) - 1;
while (TRUE)
{
if (nLow > nHigh)
{
// not found
//
break;
}
nMid = (nLow + nHigh) / 2;
{
LPCWSTR pszCurrent;
int result;
pszCurrent = g_rgdtTypeStrings[nMid].m_pszType;
result = _wcsicmp(pszTypeString, pszCurrent);
if (result < 0)
{
nHigh = nMid - 1;
}
else if (result > 0)
{
nLow = nMid + 1;
}
else
{
// found
//
sdtResult = g_rgdtTypeStrings[nMid].m_sdt;
break;
}
}
}
}
return sdtResult;
}
VARTYPE
GetVarTypeFromString(LPCWSTR pszTypeString)
{
SST_DATA_TYPE sdt = SDT_INVALID;
sdt = GetTypeFromString(pszTypeString);
return g_rgvtTypes[sdt];
}
// not an exported function, but one that we use internally...
HRESULT
HrGetChildElement(IXMLDOMNode * pxdn,
LPCWSTR pszNodeName,
IXMLDOMNode ** ppxdn);
CONST WCHAR pszTextType [] = L"string";
//+---------------------------------------------------------------------------
//
// Function: HrGetTypedValueFromElement
//
// Purpose: Given an IXMLDOMNode that should be of type
// dt:string, returns a new BSTR containing that
// string.
//
// Arguments:
// pxdn IXMLDOMNode to extract the string from.
// It is intended that this node be of type
// NODE_ELEMENT.
//
// pszDataType The type of the data encoded in the element.
//
// pvarOut Address of a VARIANT that will obtain the
// data value. This must be freed when
// no longer needed.
//
// Returns:
// S_OK if *pvarOut contains the data of the desired type
//
// Notes:
//
HRESULT
HrGetTypedValueFromElement(IXMLDOMNode * pxdn,
CONST LPCWSTR pszDataType,
VARIANT * pvarOut)
{
Assert(pxdn);
Assert(pszDataType);
Assert(pvarOut);
HRESULT hr;
BSTR bstrDataType;
bstrDataType = ::SysAllocString(pszDataType);
if (bstrDataType)
{
hr = pxdn->put_dataType(bstrDataType);
if (SUCCEEDED(hr))
{
hr = pxdn->get_nodeTypedValue(pvarOut);
if (FAILED(hr))
{
TraceError("HrGetTypedValueFromElement: "
"get_nodeTypedValue()", hr);
// clear pvarOut
::VariantInit(pvarOut);
}
}
else
{
TraceError("HrGetTypedValueFromElement: "
"put_dataType()", hr);
}
::SysFreeString(bstrDataType);
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrGetTypedValueFromElement: "
"SysAllocString()", hr);
}
TraceError("HrGetTypedValueFromElement", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrGetTypedValueFromChildElement
//
// Purpose: Given an IXMLDOMElement, finds its child element
// of the given name, and extracts the data contained
// in that child element, decoding it from the specified
// format.
//
// Arguments:
// pxdn The element which should contain the specified
// children
//
// arypszTokens A serial list of child element names that uniquely
// describe the desired element.
//
// cTokens The number of element names contained in
// arypszTokens.
//
// pszDataType The type of the data encoded in the element.
//
// pvarOut Address of a VARIANT that will obtain the
// data value. This must be freed when
// no longer needed.
//
// Returns:
// S_OK if *pbstrOut has been set to a new BSTR
// S_FALSE if the specified element did not exist.
// in this case, *pbstrOut is set to NULL
//
// Notes:
// for example, if the document looked like this:
// <foo><bar>text</bar></foo>
// and pxdn referred to <foo>, arypszTokens = [ "bar" ]
// cTokens = 1, and sdtType = "string", this would
// return "text".
// See the definition of HrGetNestedChildElement() for
// further explination.
//
HRESULT
HrGetTypedValueFromChildElement(IXMLDOMNode * pxdn,
CONST LPCWSTR * arypszTokens,
CONST ULONG cTokens,
CONST LPCWSTR pszDataType,
VARIANT * pvarOut)
{
Assert(pxdn);
Assert(arypszTokens);
Assert(cTokens);
Assert(pszDataType);
Assert(pvarOut);
HRESULT hr;
IXMLDOMNode * pxdnTemp;
pxdnTemp = NULL;
hr = HrGetNestedChildElement(pxdn, arypszTokens, cTokens, &pxdnTemp);
if (FAILED(hr))
{
pxdnTemp = NULL;
goto Cleanup;
}
if (S_OK == hr)
{
Assert(pxdn);
hr = HrGetTypedValueFromElement(pxdnTemp, pszDataType, pvarOut);
if (FAILED(hr))
{
goto Cleanup;
}
}
// hr is S_FALSE if bstrResult is NULL, or S_OK if
// pvarOut has been retrieved
Cleanup:
SAFE_RELEASE(pxdnTemp);
TraceErrorOptional("HrGetTypedValueFromChildElement", hr, (S_FALSE == hr));
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrGetTextValueFromElement
//
// Purpose: Given an IXMLDOMNode that should be of type
// dt:string, returns a new BSTR containing that
// string.
//
// Arguments:
// pxdn IXMLDOMNode to extract the string from.
// It is intended that this node be of type
// NODE_ELEMENT.
//
// pbstrOut Address of a BSTR that will obtain the
// text value. This must be freed when
// no longer needed.
//
// Returns:
// S_OK if *pbstrOut has been set to a new BSTR
//
// Notes:
// This function should return an error if the element
// contains things other than NODE_TEXT children.
//
HRESULT
HrGetTextValueFromElement(IXMLDOMNode * pxdn,
BSTR * pbstrOut)
{
Assert(pxdn);
Assert(pbstrOut);
HRESULT hr;
VARIANT varOut;
*pbstrOut = NULL;
hr = HrGetTypedValueFromElement(pxdn,
pszTextType,
&varOut);
if (S_OK == hr)
{
Assert(VT_BSTR == V_VT(&varOut));
*pbstrOut = V_BSTR(&varOut);
// note: Don't clear varOut, since it's the returned BSTR
}
TraceErrorOptional("HrGetTextValueFromElement", hr, (S_FALSE == hr));
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrGetTextValueFromChildElement
//
// Purpose: Given an IXMLDOMElement, finds its child element
// of the given name, and extracts the dt:type="string"
// data contained in that child element.
//
// Arguments:
// pxdn The element which should contain the specified
// children
//
// arypszTokens A serial list of child element names that uniquely
// describe the desired element.
//
// cTokens The number of element names contained in
// arypszTokens.
//
// pbstrOut Address of a BSTR that will obtain the
// text value. This must be freed when
// no longer needed.
//
// Returns:
// S_OK if *pbstrOut has been set to a new BSTR
// S_FALSE if the specified element did not exist.
// in this case, *pbstrOut is set to NULL
//
// Notes:
// for example, if the document looked like this:
// <foo><bar>text</bar></foo>
// and pxdn referred to <foo>, arypszTokens = [ "bar" ]
// and cTokens = 1, this would return "text".
// See the definition of HrGetNestedChildElement() for
// further explination.
//
HRESULT
HrGetTextValueFromChildElement(IXMLDOMNode * pxdn,
const LPCWSTR * arypszTokens,
const ULONG cTokens,
BSTR * pbstrOut)
{
HRESULT hr;
VARIANT varOut;
*pbstrOut = NULL;
hr = HrGetTypedValueFromChildElement(pxdn,
arypszTokens,
cTokens,
pszTextType,
&varOut);
if (S_OK == hr)
{
Assert(VT_BSTR == V_VT(&varOut));
*pbstrOut = V_BSTR(&varOut);
// note: Don't clear varOut, since it's the returned BSTR
}
TraceErrorOptional("HrGetTextValueFromChildElement", hr, (S_FALSE == hr));
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: FIsThisTheNodeName
//
// Purpose: Returns TRUE if the tag name of the specified XML DOM
// element is the specified string.
//
// Arguments:
// pxdn Node whose name is being tested
//
// pszNodeName Proposed name of the node
//
// Returns:
// TRUE if the node name matches, FALSE if not
//
// Notes:
// for the xml node declared as
// <ab:foo xmlns:ab="urn:...">...</ab:foo>
// IsThisTheNodeName() will return TRUE only when
// pszNodeName == T"foo";
BOOL
FIsThisTheNodeName(IXMLDOMNode * pxdn,
LPCWSTR pszNodeName)
{
Assert(pxdn);
Assert(pszNodeName);
HRESULT hr;
BSTR bstrNodeName;
BOOL fResult;
int result;
bstrNodeName = NULL;
hr = pxdn->get_baseName(&bstrNodeName);
if (SUCCEEDED(hr))
{
Assert(bstrNodeName);
result = wcscmp(bstrNodeName, pszNodeName);
::SysFreeString(bstrNodeName);
return (result == 0) ? TRUE : FALSE;
}
TraceError("OBJ: FIsThisTheNodeName - get_baseName", hr);
Assert(!bstrNodeName);
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Function: FIsThisTheNodeNameWithNamespace
//
// Purpose: Returns TRUE if the element name of the specified XML DOM
// element is the specified string and is in a given namespace.
//
// Arguments:
// pxdn Node whose name is being tested
// pszNodeName Proposed base name of the node
// pszNamespaceURI Proposed namespace URI of the node
//
// Returns:
// TRUE if the node name matches, FALSE if not
//
// Notes:
// for the xml node declared as
// <ab:foo xmlns:ab="urn:...">...</ab:foo>
// FIsThisTheNodeNameWithNamespace() will return TRUE only when
// pszNodeName == L"foo" and pszNamespaceURI == L"urn:..."
BOOL
FIsThisTheNodeNameWithNamespace(IXMLDOMNode * pxdn,
LPCWSTR pszNodeName,
LPCWSTR pszNamespaceURI)
{
Assert(pxdn);
Assert(pszNodeName);
Assert(pszNamespaceURI);
HRESULT hr = S_OK;;
BSTR bstrNodeName = NULL;
BSTR bstrNamespaceURI = NULL;
BOOL fResult = FALSE;
hr = pxdn->get_baseName(&bstrNodeName);
if (SUCCEEDED(hr))
{
if (bstrNodeName)
{
hr = pxdn->get_namespaceURI(&bstrNamespaceURI);
if (SUCCEEDED(hr))
{
if (bstrNamespaceURI)
{
if ((lstrcmpW(bstrNodeName, pszNodeName) == 0) &&
(lstrcmpW(bstrNamespaceURI, pszNamespaceURI) == 0))
{
fResult = TRUE;
}
::SysFreeString(bstrNamespaceURI);
}
}
::SysFreeString(bstrNodeName);
}
}
TraceError("FIsThisTheNodeNameWithNamespace(): "
"Exiting", hr);
return fResult;
}
//+---------------------------------------------------------------------------
//
// Function: FIsThisTheNodeTextValue
//
// Purpose: Determines whether the text value of a node matches a specified
// value
//
// Arguments:
// pxdn [in] The node to check
// cszTextValue [in] The value to compare against
//
// Returns:
// TRUE if the node's text value matches the value specified in cszTextValue
// FALSE otherwise
//
// Author: spather 2000/11/2
//
// Notes:
//
BOOL
FIsThisTheNodeTextValue(
IN IXMLDOMNode * pxdn,
IN LPCWSTR cszTextValue)
{
HRESULT hr = S_OK;
BOOL fResult = FALSE;
BSTR bstrNodeText = NULL;
Assert(pxdn);
Assert(cszTextValue);
hr = pxdn->get_text(&bstrNodeText);
if (SUCCEEDED(hr))
{
Assert(bstrNodeText);
if (0 == lstrcmpW(bstrNodeText, cszTextValue))
{
fResult = TRUE;
}
SysFreeString(bstrNodeText);
}
else
{
TraceError("FIsThisTheNodeTextValue(): "
"Failed to get node text",
hr);
}
TraceError("FIsThisTheNodeTextValue(): "
"Exiting",
hr);
return fResult;
}
//+---------------------------------------------------------------------------
//
// Function: FAreNodeValuesEqual
//
// Purpose: Compares the text values of two DOM nodes and, if they are
// equal, returns TRUE.
//
// Arguments:
// pxdn1 [in] The first node
// pxdn2 [in] The second node
//
// Returns:
// TRUE if the nodes' text values are equal, false otherwise.
//
// Author: spather 2000/11/2
//
// Notes:
//
BOOL
FAreNodeValuesEqual(
IN IXMLDOMNode * pxdn1,
IN IXMLDOMNode * pxdn2)
{
HRESULT hr = S_OK;
BOOL fResult = FALSE;
BSTR bstrNode1Text = NULL;
BSTR bstrNode2Text = NULL;
hr = pxdn1->get_text(&bstrNode1Text);
if (SUCCEEDED(hr))
{
Assert(bstrNode1Text);
hr = pxdn2->get_text(&bstrNode2Text);
if (SUCCEEDED(hr))
{
Assert(bstrNode2Text);
}
else
{
TraceError("FAreNodeValuesEqual(): "
"Failed to get node 2 text",
hr);
}
}
else
{
TraceError("FAreNodeValuesEqual(): "
"Failed to get node 1 text",
hr);
}
if (SUCCEEDED(hr))
{
if (0 == lstrcmpW(bstrNode1Text, bstrNode2Text))
{
fResult = TRUE;
}
}
if (bstrNode1Text)
{
SysFreeString(bstrNode1Text);
}
if (bstrNode2Text)
{
SysFreeString(bstrNode2Text);
}
TraceError("FAreNodeValuesEqual(): "
"Exiting",
hr);
return fResult;
}
//+---------------------------------------------------------------------------
//
// Function: HrAreThereDuplicatesInChildNodeTextValues
//
// Purpose: Starting from a specified parent node, this function
// examines all child nodes that have a given name, and
// checks their text values to see whether duplicates
// exist (i.e. whether two or more of these child nodes
// have the same text value).
//
// Arguments:
// pxdn [in] The parent node
// cszXSLPattern [in] XSL Pattern identifying the name of the child
// nodes to examine
// fCaseSensitive [in] Indicates whether the text values should be
// examined with a case-sensitive or case-
// insensitive comparison (if TRUE, compare will
// be case-sensitive)
// pfDuplicatesExist [out] Receives a boolean value indicating whether or
// not there were duplicates in the child node
// values. (TRUE if there were duplicates
//
// Returns:
// If the function succeeds, the return value is S_OK. Otherwise, the
// function returns one of the COM error codes defined in WinError.h.
//
// Author: spather 2000/11/5
//
// Notes:
// This function uses a very simple O(n^2) algorithm. Do not use it to
// check nodes that have very many (> 100) children.
HRESULT
HrAreThereDuplicatesInChildNodeTextValues(
IN IXMLDOMNode * pxdn,
IN LPCWSTR cszXSLPattern,
IN BOOL fCaseSensitive,
OUT BOOL * pfDuplicatesExist)
{
HRESULT hr = S_OK;
BOOL fDuplicatesExist = FALSE;
LONG lNumNodes = 0;
BSTR * rgbstrValues = NULL;
BSTR bstrXSLPattern;
IXMLDOMNodeList * pxdnlNodes = NULL;
Assert(pxdn);
Assert(cszXSLPattern);
Assert(pfDuplicatesExist);
// Get the child nodes that match the XSL pattern.
bstrXSLPattern = SysAllocString(cszXSLPattern);
if (bstrXSLPattern)
{
hr = pxdn->selectNodes(bstrXSLPattern, &pxdnlNodes);
SysFreeString(bstrXSLPattern);
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
"Failed to allocate BSTR for XSL pattern",
hr);
}
// Construct an array of the child nodes' text values.
if (SUCCEEDED(hr))
{
Assert(pxdnlNodes);
hr = pxdnlNodes->get_length(&lNumNodes);
if (SUCCEEDED(hr))
{
if (lNumNodes > 0)
{
rgbstrValues = new BSTR[lNumNodes];
if (rgbstrValues)
{
ZeroMemory(rgbstrValues, lNumNodes * sizeof(BSTR));
for (LONG i = 0; SUCCEEDED(hr) && (i < lNumNodes); i++)
{
IXMLDOMNode * pxdnChild = NULL;
hr = pxdnlNodes->get_item(i, &pxdnChild);
if (SUCCEEDED(hr))
{
Assert(pxdnChild);
hr = pxdnChild->get_text(&rgbstrValues[i]);
if (FAILED(hr))
{
TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
"Failed to get node text value",
hr);
}
pxdnChild->Release();
}
else
{
TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
"Failed to get list item",
hr);
}
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
"Failed to allocate text value array",
hr);
}
}
}
pxdnlNodes->Release();
}
else
{
TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
"Failed to get nodes matching XSL pattern",
hr);
}
// Check the array for duplicates.
if (SUCCEEDED(hr) && (lNumNodes > 0))
{
Assert(rgbstrValues);
for (LONG i = 0; i < lNumNodes; i++)
{
for (LONG j = i+1; j < lNumNodes; j++)
{
if (fCaseSensitive)
{
if (0 == lstrcmpW(rgbstrValues[i], rgbstrValues[j]))
{
fDuplicatesExist = TRUE;
break;
}
}
else
{
if (0 == lstrcmpiW(rgbstrValues[i], rgbstrValues[j]))
{
fDuplicatesExist = TRUE;
break;
}
}
}
}
}
// Clean up the array.
if (rgbstrValues)
{
for (LONG i = 0; i < lNumNodes; i++)
{
if (rgbstrValues[i])
{
SysFreeString(rgbstrValues[i]);
rgbstrValues[i] = NULL;
}
}
delete [] rgbstrValues;
rgbstrValues = NULL;
}
if (SUCCEEDED(hr))
{
*pfDuplicatesExist = fDuplicatesExist;
}
TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
"Exiting",
hr);
return hr;
}
HRESULT
HrGetFirstChildElement(IXMLDOMNode * pxdn,
LPCWSTR pszNodeName,
IXMLDOMNode ** ppxdn)
{
Assert(pxdn);
Assert(pszNodeName);
Assert(ppxdn);
HRESULT hr;
IXMLDOMNode * pxdnChild;
pxdnChild = NULL;
hr = pxdn->get_firstChild(&pxdnChild);
if (FAILED(hr))
{
TraceError("HrGetFirstChildElement - get_firstChild", hr);
pxdnChild = NULL;
goto Cleanup;
}
// maybe there is a child, maybe not.
hr = HrGetChildElement(pxdnChild, pszNodeName, ppxdn);
Cleanup:
SAFE_RELEASE(pxdnChild);
TraceErrorOptional("HrGetFirstChildElement", hr, (S_FALSE == hr));
return hr;
}
HRESULT
HrGetNextChildElement(IXMLDOMNode * pxdnLastChild,
LPCWSTR pszNodeName,
IXMLDOMNode ** ppxdn)
{
Assert(pxdnLastChild);
Assert(pszNodeName);
Assert(ppxdn);
HRESULT hr;
IXMLDOMNode * pxdnChild;
IXMLDOMNode * pxdnResult;
pxdnChild = NULL;
pxdnResult = NULL;
// we're searching for another child.
hr = pxdnLastChild->get_nextSibling(&pxdnChild);
if (FAILED(hr))
{
TraceError("HrGetNextChildElement - get_nextSibling", hr);
pxdnChild = NULL;
// we couldn't get the next child, and we haven't found a result,
// so we need to barf.
goto Cleanup;
}
// maybe there was a next node, maybe there wasn't
hr = HrGetChildElement(pxdnChild, pszNodeName, &pxdnResult);
if (FAILED(hr))
{
goto Cleanup;
}
Cleanup:
Assert(FImplies(S_OK == hr, pxdnResult));
Assert(FImplies(S_OK != hr, !pxdnResult));
SAFE_RELEASE(pxdnChild);
*ppxdn = pxdnResult;
TraceErrorOptional("HrGetNextChildElement", hr, (S_FALSE == hr));
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrGetChildElement
//
// Purpose: Returns the first child element of a given name.
//
// Arguments:
// pxdn Node to start searching at. This node and its
// subsequent siblings are searched.
// Note that this MAY be null, in which case this
// function doesn't do much...
//
// pszNodeName Name of the desired child node.
//
// ppxdn On return, contiains an AddRef()'d IXMLDOMNode
// pointer to the child element, if one of the
// given name exists.
//
// Returns:
// S_OK if a matching child node has been found. *ppxdn contains
// a reference to this found node
// S_FALSE if no matching child was found. *ppxdn is set to NULL.
//
// Notes:
// To retrieve a child element declared as <xxxx:yyyy>, pszNodeName
// must be "yyyy".
// TODO: make sure the elements considered match a specified
// namespace uri.
HRESULT
HrGetChildElement(IXMLDOMNode * pxdn,
LPCWSTR pszNodeName,
IXMLDOMNode ** ppxdn)
{
Assert(ppxdn);
Assert(pszNodeName);
HRESULT hr;
IXMLDOMNode * pxdnChild;
if (pxdn)
{
pxdn->AddRef();
}
// in case pxdnChild is NULL
hr = S_FALSE;
pxdnChild = pxdn;
while (pxdnChild)
{
// invaiant: pxdnChild is an addref()'d pointer to the current
// child of interest
IXMLDOMNode * pxdnTemp;
DOMNodeType dnt;
pxdnTemp = NULL;
// get the DOMNodeType
hr = pxdnChild->get_nodeType(&dnt);
if (FAILED(hr))
{
TraceError("HrGetChildElement - get_nodeType", hr);
goto Error;
}
if (NODE_ELEMENT == dnt)
{
if (FIsThisTheNodeName(pxdnChild, pszNodeName))
{
break;
}
}
hr = pxdnChild->get_nextSibling(&pxdnTemp);
if (FAILED(hr))
{
TraceError("HrGetChildElement - get_nextSibling", hr);
// we couldn't get the next child, and we haven't found a result,
// so we need to barf.
goto Error;
}
Assert((S_OK == hr) || (S_FALSE == hr));
Assert(FImplies(S_OK == hr, pxdnTemp));
Assert(FImplies(S_FALSE == hr, !pxdnTemp));
// note: if hr is S_FALSE, we're done
pxdnChild->Release();
pxdnChild = pxdnTemp;
}
Assert((S_OK == hr) || (S_FALSE == hr));
Assert(FImplies(S_OK == hr, pxdnChild));
Assert(FImplies(S_FALSE == hr, !pxdnChild));
// post-loop-condition: pxdnChild is an addref'd pointer to the
// matching child, of type NODE_ELEMENT.
// if there is no match, pxdnChild is NULL.
Cleanup:
*ppxdn = pxdnChild;
TraceErrorOptional("HrGetChildElement", hr, (S_FALSE == hr));
return hr;
Error:
hr = E_FAIL;
SAFE_RELEASE(pxdnChild);
goto Cleanup;
}
// Used for Validation duplicate tags in Description Document - Guru
HRESULT HrIsElementPresentOnce(
IXMLDOMNode * pxdnRoot,
LPCWSTR pszNodeName ) {
HRESULT hr = S_OK ;
LONG nNumNodes = 0;
IXMLDOMNodeList * pxdnlNodes = NULL;
BSTR bstrXSLPattern = NULL;
bstrXSLPattern = SysAllocString(pszNodeName);
if (bstrXSLPattern)
{
hr = pxdnRoot->selectNodes(bstrXSLPattern, &pxdnlNodes);
if(hr == S_OK)
{
hr = pxdnlNodes->get_length(&nNumNodes);
if(SUCCEEDED(hr))
{
if ( nNumNodes == 1 )
hr = S_OK;
else {
hr = E_FAIL ;
//hr = S_FALSE;
}
}
}
else
hr = E_FAIL;
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrIsElementPresentOnce(): "
"Failed to allocate BSTR for XSL pattern",
hr);
}
SAFE_RELEASE(pxdnlNodes);
SysFreeString(bstrXSLPattern);
TraceErrorOptional("HrIsElementPresentOnce", hr, (S_FALSE == hr));
return hr;
}
// Used for Validation duplicate tags in Description Document - Guru
HRESULT HrCheckForDuplicateElement(IXMLDOMNode * pxdnRoot,
LPCWSTR pszNodeName ) {
HRESULT hr = S_OK ;
LONG nNumNodes = 0;
IXMLDOMNodeList * pxdnlNodes = NULL;
BSTR bstrXSLPattern = NULL;
bstrXSLPattern = SysAllocString(pszNodeName);
if (bstrXSLPattern)
{
hr = pxdnRoot->selectNodes(bstrXSLPattern, &pxdnlNodes);
if(hr == S_OK)
{
hr = pxdnlNodes->get_length(&nNumNodes);
if(SUCCEEDED(hr))
{
if ( nNumNodes > 1 )
{
hr = E_FAIL;
TraceError("HrCheckForDuplicateElement(): "
"Duplicate Tag Present",
hr);
}
else {
hr = S_OK;
}
}
}
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrCheckForDuplicateElement(): "
"Failed to allocate BSTR for XSL pattern",
hr);
}
SAFE_RELEASE(pxdnlNodes);
SysFreeString(bstrXSLPattern);
TraceErrorOptional("HrCheckForDuplicateElement", hr, (S_FALSE == hr));
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrGetNestedChildElement
//
// Purpose: Returns the nth-level child of a particular element,
// given the name of the element and the name of each
// intermediate element.
//
// Arguments:
// pxdnRoot The element which should contain the specified
// children
//
// arypszTokens A serial list of child element names that uniquely
// describe the desired element. See notes below.
//
// cTokens The number of element names contained in
// arypszTokens.
//
// ppxdn On return, contiains an AddRef()'d IXMLDOMNode
// pointer to the child element, if one of the
// given name exists.
//
// Returns:
// S_OK if a matching child node has been found. *ppxdn contains
// a reference to this found node
// S_FALSE if no matching child was found. *ppxdn is set to NULL.
//
// Notes:
// To retrieve a child element declared as <xxxx:yyyy>, pszNodeName
// must be "yyyy".
// TODO: make it ensure that there is only ONE matching node!
// also...
// TODO: make sure the elements considered match a specified
// namespace uri.
//
// overview of function:
// given a list of element names and a root element, it returns the
// "n"th child element of the given element.
// e.g., if the "root" element passed in was <foo> and the tokens
// passed in were ["bar","car","dar" and "ear"], and the document
// looked like the following,
// <foo> <--- pxdnRoot
// <bar>
// <car>
// <dar>
// <ear/>
// </dar>
// </car>
// </bar>
// </foo>
// this returns the IXMLDOMNode reference to "<ear/>"
//
// if more than one of the desired name exists at a given level,
// this will chose the first element.
// <foo>
// <bar>...</bar> <!-- this one is returned -->
// <bar>...</bar>
// </foo>
//
// TODO: only return an element if its namespace matches the desired
// namespace uri
//
// pszNameSpaceURI = URI of the namespace that each element in the
// token list must be defined in
HRESULT
HrGetNestedChildElement(IXMLDOMNode * pxdnRoot,
const LPCWSTR * arypszTokens,
const ULONG cTokens,
IXMLDOMNode ** ppxdn)
{
HRESULT hr;
IXMLDOMNode * pxdnCurrent;
ULONG i;
Assert(pxdnRoot);
Assert(arypszTokens);
Assert(cTokens);
Assert(ppxdn);
pxdnCurrent = pxdnRoot;
pxdnCurrent->AddRef();
i = 0;
for ( ; i < cTokens; ++i)
{
Assert(arypszTokens[i]);
IXMLDOMNode * pxdnChild;
pxdnChild = NULL;
hr = HrGetFirstChildElement(pxdnCurrent, arypszTokens[i], &pxdnChild);
if (FAILED(hr))
{
goto Error;
}
else if (S_FALSE == hr)
{
// the child of the specified name doesn't exist. well, then...
// we return S_FALSE and NULL in this case, which is what
// we happen to have in hr and will put in pxdnCurrent
goto Error;
}
Assert(pxdnChild);
pxdnCurrent->Release();
pxdnCurrent = pxdnChild;
}
Assert(pxdnCurrent);
// pxdnCurrent is the desired child
Cleanup:
Assert(FImplies((S_OK == hr), pxdnCurrent));
Assert(FImplies(S_OK != hr, !pxdnCurrent));
*ppxdn = pxdnCurrent;
TraceErrorOptional("HrGetNestedChildElement", hr, (S_FALSE == hr));
return hr;
Error:
// we didn't find a match
SAFE_RELEASE(pxdnCurrent);
goto Cleanup;
}
// return values:
// S_OK all values have been read
// error some element couldn't be read.
// the returned array is undefined.
HRESULT
HrReadElementWithParseData (IXMLDOMNode * pxdn,
const ULONG cElems,
const DevicePropertiesParsingStruct dppsInfo [],
LPCWSTR pszBaseUrl,
LPWSTR arypszReadValues [])
{
Assert(pxdn);
Assert(cElems);
Assert(dppsInfo);
Assert(arypszReadValues);
HRESULT hr = S_OK;
ULONG i;
::ZeroMemory(arypszReadValues, sizeof(LPWSTR *) * cElems);
i = 0;
for ( ; i < cElems; ++i )
{
BSTR bstrTemp;
bstrTemp = NULL;
hr = HrGetTextValueFromChildElement(pxdn,
&(dppsInfo[i].m_pszTagName),
1,
&bstrTemp);
Assert(FImplies((S_FALSE == hr), !bstrTemp));
if (S_OK == hr)
{
Assert(bstrTemp);
if (dppsInfo[i].m_fIsUrl)
{
LPWSTR pszTemp;
pszTemp = NULL;
if (pszBaseUrl)
{
// we have to combine base and relative urls
hr = HrCombineUrl(pszBaseUrl, bstrTemp, &pszTemp);
}
else
{
// just copy and validate the url
hr = HrCopyAndValidateUrl(bstrTemp, &pszTemp);
if (S_FALSE == hr)
{
// invalid urls aren't acceptable
hr = E_FAIL;
}
}
if (SUCCEEDED(hr))
{
arypszReadValues[i] = pszTemp;
}
}
else
{
// just copy the value
arypszReadValues[i] = WszAllocateAndCopyWsz(bstrTemp);
if (!(arypszReadValues[i]))
{
hr = E_OUTOFMEMORY;
}
}
::SysFreeString(bstrTemp);
}
// note: hr may have been changed by the above code
if (FAILED(hr))
{
Assert(!(arypszReadValues[i]));
goto Error;
}
TraceTag(ttidUPnPDescriptionDoc,"XML Tag - %S - %S",dppsInfo[i].m_pszTagName,arypszReadValues[i]);
}
if (S_FALSE == hr)
{
hr = S_OK;
}
Cleanup:
TraceError("HrReadElementWithParseData", hr);
return hr;
Error:
// we need to free the strings we've already allocated
{
ULONG j;
// i is the number of elements successfully read above
j = 0;
for ( ; j < i; ++j )
{
Assert(arypszReadValues[j]);
delete [] arypszReadValues[j];
arypszReadValues[j] = NULL;
}
}
goto Cleanup;
}
// Used for Validation duplicate tags in Description Document - Guru
// If the element is optional we check if the Tag is present At most Once
// IF the element is required we check if its present exactly once
HRESULT HrAreElementTagsValid(IXMLDOMNode *pxdnRoot,
const ULONG cElems,
const DevicePropertiesParsingStruct dppsInfo [])
{
HRESULT hr = S_OK;
ULONG i;
i = 0;
for ( ; i < cElems; ++i )
{
if (dppsInfo[i].m_fOptional)
{
hr = HrCheckForDuplicateElement(pxdnRoot,dppsInfo[i].m_pszTagName);
}
else {
hr = HrIsElementPresentOnce(pxdnRoot,dppsInfo[i].m_pszTagName);
}
if(FAILED(hr))
break;
}
TraceError("HrAreElementTagsValid", hr);
return hr;
}
// return TRUE if all of the non-optional values (as specified by the
// m_fOptional in dppsInfo) are non-null in arypszReadValues.
// equivalently, it returns FALSE if there exists a value marked as
// required (m_fOptional == FALSE) that is NULL in arypszReadValues.
BOOL
fAreReadValuesComplete (const ULONG cElems,
const DevicePropertiesParsingStruct dppsInfo [],
const LPCWSTR arypszReadValues [])
{
ULONG i;
BOOL fResult;
fResult = TRUE;
i = 0;
for ( ; i < cElems; ++i )
{
if (!dppsInfo[i].m_fOptional)
{
if (!(arypszReadValues[i]))
{
fResult = FALSE;
break;
}
}
}
return fResult;
}
//+---------------------------------------------------------------------------
//
// Function: HrGetTextValueFromAttribute
//
// Purpose: Retrieves the text value of an attribute on a DOM element node
//
// Arguments:
// pxdn [in] XML DOM Node representing the element whose
// attribute is to be read
// szAttrName [in] Name of the attribute
// pbstrAttrValue [in] Receives the attribute text value.
//
// Returns:
// If the function successfully reads the attribute value, the return value
// is S_OK and the attribute value is returned at pbstrAttrValue. If the
// attribute does not exist on the element, the function returns S_FALSE,
// and NULL at pbstrAttrValue. Otherwise the function returns one of the
// error codes defined in WinError.h.
//
// Author: spather 2000/11/9
//
// Notes:
//
HRESULT
HrGetTextValueFromAttribute(
IN IXMLDOMNode * pxdn,
IN LPCWSTR szAttrName,
OUT BSTR * pbstrAttrValue)
{
HRESULT hr = S_OK;
IXMLDOMElement * pxde = NULL;
BSTR bstrValue = NULL;
hr = pxdn->QueryInterface(IID_IXMLDOMElement,
(void **) &pxde);
if (SUCCEEDED(hr))
{
BSTR bstrAttrName = NULL;
Assert(pxde);
bstrAttrName = SysAllocString(szAttrName);
if (bstrAttrName)
{
VARIANT varValue;
VariantInit(&varValue);
hr = pxde->getAttribute(bstrAttrName,
&varValue);
if (SUCCEEDED(hr))
{
if (VT_NULL == varValue.vt)
{
Assert(S_FALSE == hr);
}
else if (VT_BSTR == varValue.vt)
{
bstrValue = V_BSTR(&varValue);
}
else
{
Assert(FALSE); // Should never be here.
}
}
else
{
TraceError("HrGetTextValueFromAttribute(): "
"Failed to get attribute value",
hr);
}
SysFreeString(bstrAttrName);
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrGetTextValueFromAttribute(): "
"Failed to allocate attribute name BSTR",
hr);
}
pxde->Release();
}
else
{
TraceError("HrGetTextValueFromAttribute(): "
"Failed to get IXMLDOMElement interface",
hr);
}
if (SUCCEEDED(hr))
{
*pbstrAttrValue = bstrValue;
}
else
{
if (bstrValue)
{
SysFreeString(bstrValue);
}
}
TraceHr(ttidError, FAL, hr, (hr == S_FALSE), "HrGetTextValueFromAttribute");
return hr;
}
/*
* Function: HrSetTextAttribute()
*
* Purpose: Sets an text-valued attribute on an XML element.
*
* Arguments:
* pElement [in] The element on which to set the attribute
* pcwszAttrName [in] The attribute name
* pcwszValue [in] The value to give the attribute
*
* Returns:
* S_OK if successful, other HRESULT otherwise.
*/
HRESULT
HrSetTextAttribute(
IXMLDOMElement * pElement,
LPCWSTR pcwszAttrName,
LPCWSTR pcwszValue)
{
HRESULT hr = S_OK;
VARIANT vValue;
BSTR bstrValue, bstrAttrName;
// Allocate BSTRs for the attribute name and value.
bstrValue = SysAllocString(pcwszValue);
if (bstrValue)
{
bstrAttrName = SysAllocString(pcwszAttrName);
if (!bstrAttrName)
{
hr = E_OUTOFMEMORY;
SysFreeString(bstrValue);
}
}
else
{
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
// danielwe: Bug 144883
VariantInit(&vValue);
vValue.vt = VT_BSTR;
V_BSTR(&vValue) = bstrValue;
hr = pElement->setAttribute(bstrAttrName, vValue);
SysFreeString(bstrValue);
SysFreeString(bstrAttrName);
}
return hr;
}
/*
* Function: HrCreateElementWithType()
*
* Purpose: Creates an XML element containing a binary value that is
* encoded in bin.base64.
*
* Arguments:
* pDoc [in] Document in which to create the element
* pcwszElementName [in] Name for the new element
* pszDataType [in] The type of the data encoded in the element.
* varData [in] Data to insert as the element's value
* ppElement [out] Address at which to place the pointer to the
* new element object
*
* Returns:
* S_OK if successful, other HRESULT otherwise.
*
* Notes:
* This function does not actually insert the new element into the document
* tree.
*/
HRESULT
HrCreateElementWithType(
IN IXMLDOMDocument * pDoc,
IN LPCWSTR pcwszElementName,
IN LPCWSTR pszDataType,
IN VARIANT varData,
OUT IXMLDOMElement ** ppElement)
{
Assert(pDoc);
Assert(pcwszElementName);
Assert(pszDataType);
Assert(ppElement);
HRESULT hr;
BSTR bstrElementName;
hr = S_OK;
*ppElement = NULL;
bstrElementName = SysAllocString(pcwszElementName);
if (bstrElementName)
{
IXMLDOMElement * pElement = NULL;
hr = pDoc->createElement(bstrElementName,
&pElement);
if (SUCCEEDED(hr))
{
BSTR bstrDataType;
bstrDataType = ::SysAllocString(pszDataType);
if (bstrDataType)
{
hr = pElement->put_dataType(bstrDataType);
if (SUCCEEDED(hr))
{
hr = pElement->put_nodeTypedValue(varData);
if (SUCCEEDED(hr))
{
*ppElement = pElement;
pElement->AddRef();
}
else
{
TraceError("HrCreateElementWithType(): "
"put_nodeTypedValue", hr);
}
}
else
{
TraceError("HrCreateElementWithType(): "
"put_dataType", hr);
}
::SysFreeString(bstrDataType);
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrCreateElementWithType(): "
"SysAllocString", hr);
}
pElement->Release();
}
else
{
TraceError("HrCreateElementWithType(): "
"Could not create element", hr);
}
::SysFreeString(bstrElementName);
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrCreateElementWithType(): "
"Could not allocate BSTR", hr);
}
return hr;
}
/*
* Function: HrCreateElementWithTextValue()
*
* Purpose: Creates an XML element containing a text string as
* a value.
*
* Arguments:
* pDoc [in] Document in which to create the element
* pcwszElementName [in] Name for the new element
* pcwszTextValue [in] Text to insert as the element's value
* ppElement [out] Address at which to place the pointer to the
* new element object
*
* Returns:
* S_OK if successful, other HRESULT otherwise.
*
* Notes:
* This function does not actually insert the new element into the document
* tree.
*/
HRESULT
HrCreateElementWithTextValue(
IN IXMLDOMDocument * pDoc,
IN LPCWSTR pcwszElementName,
IN LPCWSTR pcwszTextValue,
OUT IXMLDOMElement ** ppElement)
{
HRESULT hr = S_OK;
IXMLDOMElement * pElement = NULL;
BSTR bstrElementName, bstrTextValue;
*ppElement = NULL;
// Allocate BSTRs for the element name and value.
bstrElementName = SysAllocString(pcwszElementName);
if (bstrElementName)
{
bstrTextValue = SysAllocString(pcwszTextValue);
if (!bstrTextValue)
{
hr = E_OUTOFMEMORY;
SysFreeString(bstrElementName);
}
}
else
{
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
hr = pDoc->createElement(bstrElementName,
&pElement);
if (SUCCEEDED(hr))
{
IXMLDOMText * pText = NULL;
hr = pDoc->createTextNode(bstrTextValue,
&pText);
if (SUCCEEDED(hr))
{
VARIANT vAfter;
// danielwe: Bug 144882
VariantInit(&vAfter);
vAfter.vt = VT_EMPTY;
hr = pElement->insertBefore(pText,
vAfter,
NULL);
if (FAILED(hr))
{
TraceError("HrCreateElementWithTextValue(): "
"Failed to insert text node", hr);
}
pText->Release();
}
else
{
TraceError("HrCreateElementWithTextValue(): "
"Failed to create text node", hr);
}
if (SUCCEEDED(hr))
{
*ppElement = pElement;
pElement->AddRef();
}
pElement->Release();
}
else
{
TraceError("HrCreateElementWithTextValue(): "
"Could not create element", hr);
}
SysFreeString(bstrElementName);
SysFreeString(bstrTextValue);
}
else
{
TraceError("HrCreateElementWithTextValue(): "
"Could not allocate BSTRs", hr);
}
return hr;
}
/*
* Function: HrCreateElement()
*
* Purpose: Create an XML Element DOM object
*
* Arguments:
* pxdd [in] The document object in whose context the
* element will be created
* pcwszElementName [in] Name of the element to be created
* pcwszNamespaceURI [in] URI identifying the namespace to which
* the element name belongs
* ppxdnNewElt [out] The newly created element object
*
* Return Value:
* S_OK if successful, other HRESULT otherwise.
*
* Notes:
* (none)
*/
HRESULT
HrCreateElement(
IN IXMLDOMDocument * pxdd,
IN LPCWSTR pcwszElementName,
IN LPCWSTR pcwszNamespaceURI,
OUT IXMLDOMNode ** ppxdnNewElt)
{
HRESULT hr = S_OK;
IXMLDOMNode * pxdnNewElt = NULL;
BSTR bstrElementName = NULL;
BSTR bstrNamespaceURI = NULL;
Assert(pxdd);
Assert(pcwszElementName);
Assert(ppxdnNewElt);
// Allocate BSTRs
bstrElementName = SysAllocString(pcwszElementName);
if (bstrElementName)
{
if (pcwszNamespaceURI)
{
bstrNamespaceURI = SysAllocString(pcwszNamespaceURI);
if (NULL == bstrNamespaceURI)
{
hr = E_OUTOFMEMORY;
TraceError("HrCreateElement(): "
"Failed to allocate memory for name string",
hr);
}
}
if (SUCCEEDED(hr))
{
VARIANT varNodeType;
VariantInit(&varNodeType);
varNodeType.vt = VT_I4;
V_I4(&varNodeType) = (int) NODE_ELEMENT;
hr = pxdd->createNode(varNodeType,
bstrElementName,
bstrNamespaceURI,
&pxdnNewElt);
if (FAILED(hr))
{
TraceError("HrCreateElement(): "
"Failed to create element node",
hr);
}
if (bstrNamespaceURI)
{
SysFreeString(bstrNamespaceURI);
}
}
SysFreeString(bstrElementName);
}
else
{
hr = E_OUTOFMEMORY;
TraceError("HrCreateElement(): "
"Failed to allocate memory for name string",
hr);
}
if (SUCCEEDED(hr))
{
*ppxdnNewElt = pxdnNewElt;
}
TraceError("HrCreateElement(): "
"Exiting",
hr);
return hr;
}
/*
* Function: HrAppendProcessingInstruction()
*
* Purpose: Append a processing instruction to the DOM document
*
* Arguments:
* pxdd [in] The document object in whose context the
* element will be created
* pcwszName [in] Name of the processing instruction to be created
* pcwszValue [in] The text value of the p.i.
*
* Return Value:
* S_OK if successful, other HRESULT otherwise.
*
* Notes:
* (none)
*/
HRESULT
HrAppendProcessingInstruction(
IN IXMLDOMDocument * pxdd,
IN LPCWSTR pcwszName,
IN LPCWSTR pcwszValue)
{
HRESULT hr = S_OK;
Assert(pxdd);
Assert(pcwszName);
Assert(pcwszValue);
IXMLDOMProcessingInstruction* pxdpi = NULL;
BSTR bstrTarget = SysAllocString(pcwszName);
BSTR bstrData = SysAllocString(pcwszValue);
if (bstrTarget && bstrData)
{
hr = pxdd->createProcessingInstruction(bstrTarget, bstrData, &pxdpi);
}
else
{
hr = E_OUTOFMEMORY;
}
SysFreeString(bstrTarget);
SysFreeString(bstrData);
if (SUCCEEDED(hr))
{
IXMLDOMNode* pxdn = NULL;
hr = pxdpi->QueryInterface(IID_IXMLDOMNode, (void**)&pxdn);
if (SUCCEEDED(hr))
{
hr = pxdd->appendChild(pxdn, NULL);
pxdn->Release();
}
}
if (pxdpi)
{
pxdpi->Release();
}
return hr;
}