summaryrefslogtreecommitdiff
path: root/print/OEM Printer Customization Plug-in Samples/C++/PTPCPlPr/src/xmlhandler.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'print/OEM Printer Customization Plug-in Samples/C++/PTPCPlPr/src/xmlhandler.cxx')
-rw-r--r--print/OEM Printer Customization Plug-in Samples/C++/PTPCPlPr/src/xmlhandler.cxx1612
1 files changed, 1612 insertions, 0 deletions
diff --git a/print/OEM Printer Customization Plug-in Samples/C++/PTPCPlPr/src/xmlhandler.cxx b/print/OEM Printer Customization Plug-in Samples/C++/PTPCPlPr/src/xmlhandler.cxx
new file mode 100644
index 00000000..4bbee2e8
--- /dev/null
+++ b/print/OEM Printer Customization Plug-in Samples/C++/PTPCPlPr/src/xmlhandler.cxx
@@ -0,0 +1,1612 @@
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
+// PARTICULAR PURPOSE.
+//
+// Copyright 1998 - 2005 Microsoft Corporation. All Rights Reserved.
+//
+// FILE: xmlhandler.cxx
+//
+//
+// PURPOSE: Helper Class that handles all functionality related to
+// processing of input PrintTicket/PrintCapabilities XML documents.
+//
+//
+// Functions: Public and Private functions of class OEMPTXMLHandler.
+//
+
+#include "precomp.hxx"
+
+#include "globals.hxx"
+#include "printschema.hxx"
+#include "xmlhandler.hxx"
+
+// This indicates to Prefast that this is a usermode driver file.
+_Analysis_mode_(_Analysis_code_type_user_driver_);
+
+
+//Disable the warnings generated by PFEFAST for conversion between wchar_t * and BSTR
+//because MSXML defines all its interfaces parameters as BSTR but officially declares itself
+//to be wchar_t safe.
+
+#pragma prefast(disable:__WARNING_WCHAR_TO_BSTR)
+
+//
+// MSXML makes the guarantee that we can call it's methods with LPTSTRs instead
+// of BSTRs, so we need to do a const cast whenever we pass our strings to MSXML.
+//
+#define MSXMLSTR(lptstr_value) (const_cast<BSTR>(lptstr_value))
+
+/*++
+Routine Name:
+ OEMPTXMLHandler
+Routine Description:
+ Print Ticket Handler Constructor
+
+
+Arguments:
+ None
+
+Return Value:
+ None
+
+--*/
+
+OEMPTXMLHandler::OEMPTXMLHandler() :
+ m_pRootDocument(NULL),
+ m_pRootElement(NULL),
+ m_pNSManager(NULL),
+ m_dwNextIndex(0)
+{
+}
+
+/*++
+Routine Name:
+ ~OEMPTXMLHandler(
+Routine Description:
+ Print Ticket Handler Destructor
+
+Arguments:
+ None
+
+Return Value:
+ None
+
+--*/
+
+OEMPTXMLHandler::~OEMPTXMLHandler()
+{
+ if (m_pRootDocument)
+ {
+ m_pRootDocument->Release();
+ m_pRootDocument = NULL;
+ }
+ if (m_pRootElement)
+ {
+ m_pRootElement->Release();
+ m_pRootElement = NULL;
+ }
+ if (m_pNSManager)
+ {
+ m_pNSManager->Release();
+ m_pNSManager = NULL;
+ }
+}
+
+/*++
+Routine Name:
+ SetRoot
+
+Routine Description:
+
+ Initialization routine. Sets root of the DOM tree. Alos creates an instance of xml namespace manager
+ using plug-in helper interface method.
+
+ Note: This routine MUST BE called before any other xml handling routines are called.
+ Note also that it is not necessary to call CoInitializeEx because Unidrv has already
+ made that call while creating the DOM tree with appropriate arguments passed on to it
+ and when Unidrv makes a call to one of plug-in interface routines, since plug-in is
+ running in the same thread, it does not need to make that call again
+ In fact the plug-in SHOULD NOT make that call to CoInitializeEx , because Unidrv must have made
+ that call with some particular concurrency model settings which plug-in cannot find out,
+ so the plug-in might end up changing the concurrency setting made by Unidrv.
+
+Arguments:
+ pRoot - Root of the input DOM tree.
+
+
+Return Value:
+ HRESULT Completion status code
+
+--*/
+HRESULT
+OEMPTXMLHandler::SetRoot(
+ _In_ IXMLDOMDocument2 *pRoot,
+ _In_ IPrintCoreHelper *pHelper
+ )
+{
+ HRESULT hr = S_OK;
+
+ //
+ // check if input paramters are not NULL
+ //
+ if (!pRoot || !pHelper)
+ {
+ return E_INVALIDARG;
+ }
+
+ m_pRootDocument = pRoot;
+
+ m_pRootDocument->AddRef();
+
+
+ //
+ // Make use of Plug-in Helper Interface method provided by Unidrv to create an Instance of MSXML Namespace Manager.
+ // The plug-in must make use of Helper Interface provided by Unidrv while creating instance of namespacemanager
+ // of MSXML DOM document, the plug-in should not load MSXML6.DLL by itself but should rely on Unidrv for
+ // doing that
+ //
+ if (SUCCEEDED(hr))
+ {
+ //
+ // Note: Current versions of Unidrv support MSXML6 hence plug-in should also support that and write its
+ // code in compliance with MSXML6
+ //
+ hr = pHelper->CreateInstanceOfMSXMLObject(CLSID_MXNamespaceManager60, NULL, NULL, IID_IMXNamespaceManager, (void**)&m_pNSManager);
+ }
+
+ //
+ // Get the Root Element of the document and store it in pRootElement.
+ //
+ if (SUCCEEDED(hr))
+ {
+ hr = m_pRootDocument->get_documentElement(&m_pRootElement);
+ }
+
+ if (FAILED(hr))
+ {
+ if (m_pRootDocument)
+ {
+ m_pRootDocument->Release();
+ m_pRootDocument = NULL;
+ }
+
+ if (m_pRootElement)
+ {
+ m_pRootElement->Release();
+ m_pRootElement = NULL;
+ }
+
+ if (m_pNSManager)
+ {
+ m_pNSManager->Release();
+ m_pNSManager = NULL;
+ }
+ }
+
+ return hr;
+}
+
+/*++
+Routine Name:
+ CreateFeatureNode
+
+Routine Description:
+
+ Creates a new Feature Node
+
+Arguments:
+ pParent - parent node of the new feature node, if NULL, root is considered as parent
+ pszNamespaceURI - namesapce URI to which new node belongs
+ pszFeatureName - local name of the new feature node
+ ppFeatureNode - out pointer to newly created node
+
+Return Value:
+ HRESULT Completion status code
+ S_OK on success
+ E_* on failure
+
+--*/
+
+HRESULT OEMPTXMLHandler::CreateFeatureNode(
+ _In_opt_ IXMLDOMElement *pParent,
+ _In_ PCWSTR pszNamespaceURI,
+ _In_ PCWSTR pszFeatureName,
+ _Out_opt_ IXMLDOMElement **ppFeatureNode
+ )
+{
+ HRESULT hr = S_OK;
+ IXMLDOMElement *pNewElement = NULL;
+ BSTR qName = NULL;
+
+ if (!pszFeatureName)
+ {
+ return E_INVALIDARG;
+ }
+
+ if (!pParent)
+ {
+ pParent = m_pRootElement;
+ }
+
+
+ hr = CreateXMLElement(pParent, printschema::FEATURE_ELEMENT_NAME, printschema::FRAMEWORK_URI, &pNewElement);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = CreateQName(pNewElement, pszNamespaceURI, pszFeatureName, &qName);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+
+ // The 'name' attibute node is not namespace qualified; it
+ // belongs to the namespace of its parent (feature/option)
+ // node.
+
+ hr = CreateXMLAttribute(pNewElement, printschema::NAME_ATTRIBUTE_NAME, L"", qName);
+ }
+
+ if (qName)
+ {
+ SysFreeString(qName);
+ qName = NULL;
+ }
+
+ if (SUCCEEDED(hr) && ppFeatureNode)
+ {
+ *ppFeatureNode = pNewElement;
+ }
+ else if (pNewElement)
+ {
+ pNewElement->Release();
+ }
+
+ return hr;
+
+}
+
+/*++
+Routine Name:
+ CreateOptionNode
+
+Routine Description:
+
+ Creates a new Option Node
+
+Arguments:
+ pParent - parent node of the new Option node
+ pszNamespaceURI - namesapce URI to which new node belongs
+ pszFeatureName - local name of the new Option node
+ ppOptionElement - out pointer to newly created node
+
+Return Value:
+ HRESULT Completion status code
+ S_OK on success
+ E_* on failure
+
+--*/
+
+HRESULT OEMPTXMLHandler::CreateOptionNode(
+ _In_ IXMLDOMElement *pParent,
+ _In_ PCWSTR pszNamespaceURI, //print schema URI that corresponds to prefix "prn:"
+ _In_ PCWSTR pszOptionName, //"TopLeft" etc
+ _Out_opt_ IXMLDOMElement **ppOptionElement
+ )
+{
+ HRESULT hr = S_OK;
+ IXMLDOMElement *pNewElement = NULL;
+ BSTR qName = NULL;
+
+ if (!pParent)
+ {
+ return E_INVALIDARG;
+ }
+
+ hr = CreateXMLElement(pParent, printschema::OPTION_ELEMENT_NAME, printschema::FRAMEWORK_URI, &pNewElement);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = CreateQName(pNewElement, pszNamespaceURI, pszOptionName, &qName);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // The 'name' attibute node is not namespace qualified; it
+ // belongs to the namespace of its parent (feature/option)
+ // node.
+
+ hr = CreateXMLAttribute(pNewElement, printschema::NAME_ATTRIBUTE_NAME, L"", qName);
+ }
+
+ if (qName)
+ {
+ SysFreeString(qName);
+ qName = NULL;
+ }
+
+ if (SUCCEEDED(hr) && ppOptionElement)
+ {
+ *ppOptionElement = pNewElement;
+ }
+ else if (pNewElement)
+ {
+ pNewElement->Release();
+ }
+
+ return hr;
+}
+
+/*++
+Routine Name:
+ CreateXMLElement
+
+Routine Description:
+ Generic method to create new XML element node
+
+Arguments:
+
+ pParent - pointer to the parent node of the new nod eto be created, if NULL root of DOM tree is considered as parent
+ pszElementName - local name of the newly created element
+ pszNamespaceURI - namespace URI that the new element belongs to, the caller can only specify the namespace URI
+ and not the prefix
+ ppCreatedElement - double pointer to the newly created element returned by the procedure
+
+Return Value:
+ HRESULT Completion status code
+ S_OK - If the new element node is created successfully
+ E_* - on failure
+
+--*/
+
+HRESULT OEMPTXMLHandler::CreateXMLElement(
+ _In_ IXMLDOMElement *pParent,
+ _In_ PCWSTR pszElementName,
+ _In_ PCWSTR pszNamespaceURI,
+ _Out_ IXMLDOMElement **ppCreatedElement
+ )
+{
+ HRESULT hr = S_OK;
+ IXMLDOMNode *pNewNode = NULL;
+ BSTR bstrQName = NULL;
+
+ if (!pParent || !pszElementName || !pszNamespaceURI || !ppCreatedElement)
+ {
+ return E_INVALIDARG;
+ }
+
+ //
+ // create XML Qname
+ //
+ hr = CreateQName(pParent, pszNamespaceURI, pszElementName, &bstrQName);
+
+ if (SUCCEEDED(hr))
+ {
+ VARIANT nodeType;
+ VariantInit(&nodeType);
+ V_VT(&nodeType) = VT_I4;
+ V_I4(&nodeType) = NODE_ELEMENT;
+ hr = m_pRootDocument->createNode(nodeType, bstrQName, MSXMLSTR(pszNamespaceURI), &pNewNode);
+ if (SUCCEEDED(hr))
+ VariantClear(&nodeType);
+ }
+
+ if (SUCCEEDED(hr))
+ hr = pParent->appendChild(pNewNode, NULL);
+
+
+ if (SUCCEEDED(hr))
+ {
+ //
+ // we need to return Interface ptr to IXMLDOMElement.
+ //
+ hr = pNewNode->QueryInterface(IID_IXMLDOMElement, (void **)ppCreatedElement);
+
+ //
+ // if interface is not supported, E_* will be returned in hr,
+ // we simply return that to the caller.
+ //
+ }
+
+ if (pNewNode)
+ {
+ //
+ // release reference to local node Created since it is no longer being used
+ //
+ pNewNode->Release();
+ }
+
+ if (bstrQName)
+ {
+ SysFreeString(bstrQName);
+ bstrQName = NULL;
+ }
+
+ return hr;
+
+}
+
+/*++
+Routine Name:
+ CreateQName
+
+Routine Description:
+
+ Creates XML qname for given local name and namespace URI
+ Note: It is assumed that a prefix for given URI is already defined in root of DOM document
+ either by Unidrv or by plug-in itself.
+ Routine fails if it doesnot find a predefined prefix for given URI.
+
+Arguments:
+ pElement - context node
+ pszUri - Uri
+ pszLocalName - local name
+ pQName - a pointer to resultant qname string defined by the routine
+
+Return Value:
+ HRESULT Completion status code
+
+ S_OK - On success
+ E_FAIL - on failure to create Qname
+
+--*/
+HRESULT OEMPTXMLHandler::CreateQName(
+ _In_ IXMLDOMElement *pElement,
+ _In_ PCWSTR pszUri,
+ _In_ PCWSTR pszLocalName,
+ _Out_ BSTR *pQName
+ )
+{
+ HRESULT hr = S_OK;
+ BSTR bstrPrefixString = NULL;
+ size_t prefixLen = 0;
+ // Is it the default namespace? If so, don't stick a semicolon in the string
+ size_t localNameLen = 0;
+
+ if (!pszUri || !pszLocalName || !pQName)
+ {
+ return E_INVALIDARG;
+ }
+
+ *pQName = NULL;
+
+ //
+ // See if a prefix is already defined for the given URI
+ //
+
+ hr = getPrefix(pElement, pszUri, &bstrPrefixString);
+
+ //
+ // It is possible that during validation the prefix might not
+ // already be defined if we're converting public features to GPD
+ // features. In that case, we need to add a definition for the
+ // feature namespace.
+ //
+
+ if (S_FALSE == hr)
+ {
+ hr = declarePrefix(pElement, pszUri, NULL, &bstrPrefixString);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ //
+ // StrSafe function StringCchLength checks if the length of string is less than
+ // maximum length specified, and returns S_OK if the input string is non NULL and
+ // length is less that maximum length specified.
+ // It returns length of string EXCLUDING null terminator
+ //
+ hr = StringCchLength(pszLocalName, STR_MAX_CCH, &localNameLen);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (bstrPrefixString)
+ {
+ hr = StringCchLength(bstrPrefixString, STR_MAX_CCH, &prefixLen);
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (prefixLen > 0)
+ {
+ // non-default namespace
+
+ UINT cchLength = 0;
+
+ if (SUCCEEDED(hr = SizeTAdd(prefixLen, localNameLen, &localNameLen)) &&
+ SUCCEEDED(hr = SizeTAdd(localNameLen, 2, &localNameLen)) &&
+ SUCCEEDED(hr = SizeTToUInt(localNameLen, &cchLength)))
+ {
+
+ *pQName = SysAllocStringLen(NULL, cchLength);
+
+ if (*pQName)
+ {
+ // we got a terminator character as part of SysAllocString.
+ hr = StringCchPrintf(*pQName, cchLength, L"%s:%s", bstrPrefixString, pszLocalName);
+ }
+ else
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ }
+ }
+ else
+ {
+ // Default namespace ...
+
+ UINT cchLength = 0;
+
+ if (SUCCEEDED(hr = SizeTAdd(localNameLen, 1, &localNameLen)) &&
+ SUCCEEDED(hr = SizeTToUInt(localNameLen, &cchLength)))
+ {
+ *pQName = SysAllocStringLen(NULL, cchLength);
+
+ if (*pQName)
+ {
+ // we got a terminator character as part of SysAllocString.
+ hr = StringCchPrintf(*pQName, cchLength, L"%s", pszLocalName);
+ }
+ else
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ }
+ }
+ }
+
+ if (bstrPrefixString)
+ {
+ SysFreeString(bstrPrefixString);
+ bstrPrefixString = NULL;
+ }
+
+ if (FAILED(hr))
+ {
+ if (*pQName)
+ {
+ SysFreeString(*pQName);
+ *pQName = NULL;
+ }
+ }
+
+ return hr;
+}
+
+/*++
+Routine Name:
+ CreateXMLAttribute
+
+Routine Description:
+ Creates an attribute for the given XML element.
+
+Arguments:
+
+ pElement - pointer to the element node to which an attribute is to be added
+ pszAttributeName - local name of the attribute
+ pszNamespaceURI - namespace URI to which the attribute belongs
+ pszAttributeValue - value of the attribute
+
+Return Value:
+ HRESULT Completion status code
+ S_OK - on success
+ E_* - on failure
+
+--*/
+
+HRESULT OEMPTXMLHandler::CreateXMLAttribute(
+ _In_ IXMLDOMElement *pElement,
+ _In_ PCWSTR pszAttributeName, //"name="
+ _In_ PCWSTR pszNamespaceURI, //"pt:"
+ _In_ PCWSTR pszAttributeValue //"prn:"
+ )
+{
+ HRESULT hr = S_OK;
+ IXMLDOMAttribute *pNewAttribute = NULL ;
+ IXMLDOMNode *pNewNode = NULL ;
+
+ if (!pElement || !pszAttributeName || ! pszNamespaceURI || !pszAttributeValue)
+ {
+ return E_INVALIDARG;
+ }
+
+ VARIANT nodeType;
+ VariantInit(&nodeType);
+ V_VT(&nodeType) = VT_I4;
+ V_I4(&nodeType) = NODE_ATTRIBUTE;
+ hr = m_pRootDocument->createNode(nodeType, MSXMLSTR(pszAttributeName), MSXMLSTR(pszNamespaceURI), &pNewNode);
+ if (SUCCEEDED(hr))
+ VariantClear(&nodeType);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pNewNode->QueryInterface(IID_IXMLDOMAttribute, (void **)&pNewAttribute);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ VARIANT attrType;
+ VariantInit(&attrType);
+ V_VT(&attrType) = VT_BSTR;
+ V_BSTR(&attrType) = SysAllocString(pszAttributeValue);
+ hr = pNewAttribute->put_value(attrType);
+ if (SUCCEEDED(hr))
+ VariantClear(&attrType);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pElement->setAttributeNode(pNewAttribute, NULL);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (pNewAttribute)
+ {
+ pNewAttribute->Release();
+ }
+
+ }
+ else if (pNewAttribute)
+ pNewAttribute->Release();
+
+ if (pNewNode)
+ pNewNode->Release();
+
+ return hr;
+}
+
+/*++
+Routine Name:
+ GetXMLElement
+Routine Description:
+ Returns an XML element with given namespace URI and given attribute
+
+Arguments:
+
+ pParent - Parent of node to be found
+ pszElementNamespaceURI - Namespace URI to which the element belongs
+ pszElementName - Local Name of the element
+ pszAttrNamespaceURI - Namespace URI to which the attribute of the element belongs
+ pszAttrName - local name of the element attribute
+ ppElement - out pointer to xml element so retrieved
+
+Return Value:
+ HRESULT Completion status code
+ S_OK - On Successfully locating XML node specified
+ S_FALSE - If the specified XML node is not found
+ E_* - On Failure
+
+ Note: Since the module returns S_FALSE if the specified node is not found, the caller must be
+ careful to check hr status to be S_OK before proceeding, just checking for SUCCEEDED(hr) might
+ give wrong results
+
+--*/
+HRESULT OEMPTXMLHandler::GetXMLElement(
+ _In_ IXMLDOMElement *pParent,
+ _In_ PCWSTR pszElementNamespaceURI,
+ _In_ PCWSTR pszElementName,
+ _In_ PCWSTR pszAttrNamespaceURI,
+ _In_ PCWSTR pszAttrName,
+ _Outptr_result_maybenull_ IXMLDOMElement **ppElement
+ ) const
+{
+ HRESULT hr = S_OK;
+ LONG cChildren = 0;
+ IXMLDOMNodeList *pChildren = NULL;
+
+
+ if (!pParent || !pszElementNamespaceURI || !pszElementName || !pszAttrNamespaceURI || !pszAttrName)
+ {
+ return E_INVALIDARG;
+ }
+
+ *ppElement = NULL;
+
+ //
+ // Get a list of child nodes for the current DOM node
+ //
+ hr = pParent->get_childNodes(&pChildren);
+
+ //
+ // Get number of child nodes
+ //
+ if (SUCCEEDED(hr))
+ {
+ hr = pChildren->get_length(&cChildren);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ LONG i = 0;
+ BOOL bFound = FALSE;
+
+ for(i = 0;i < cChildren && !bFound;i++)
+ {
+ IXMLDOMNode *pCurNode;
+ IXMLDOMElement *pCurElement = NULL;
+
+ hr = pChildren->get_item(i, &pCurNode);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pCurNode->QueryInterface(IID_IXMLDOMElement, (void **)&pCurElement);
+ }
+ if (S_OK == hr)
+ {
+ BSTR bstrElementName = NULL;
+ BSTR bstrElementNamespaceURI = NULL;
+
+ //
+ // retrieve element name and namespace URI
+ //
+ hr = pCurElement->get_baseName(&bstrElementName);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pCurElement->get_namespaceURI(&bstrElementNamespaceURI);
+ }
+
+ if (SUCCEEDED(hr) && bstrElementNamespaceURI && bstrElementName)
+ {
+
+ if (0 == wcscmp(bstrElementName, pszElementName) && 0 == wcscmp(bstrElementNamespaceURI, pszElementNamespaceURI))
+ {
+ //
+ //check if element name matches
+ //
+ BSTR bstrAttrName = NULL;
+ BSTR bstrAttrNamespaceURI = NULL;
+ hr = GetXMLAttribute(pCurElement,&bstrAttrNamespaceURI,&bstrAttrName);
+ if (SUCCEEDED(hr) && bstrAttrName && bstrAttrNamespaceURI)
+ {
+ if (0 == wcscmp(bstrAttrName, pszAttrName) && 0 == wcscmp(pszAttrNamespaceURI, bstrAttrNamespaceURI))
+ {
+ bFound = TRUE;
+ *ppElement = pCurElement;
+ (*ppElement)->AddRef();
+ }
+ else
+ {
+ hr = S_FALSE;
+ }
+ }
+
+ SysFreeString(bstrAttrName);
+ bstrAttrName = NULL;
+
+ SysFreeString(bstrAttrNamespaceURI);
+ bstrAttrNamespaceURI = NULL;
+ }
+ }
+
+ SysFreeString(bstrElementName);
+ bstrElementName = NULL;
+
+ SysFreeString(bstrElementNamespaceURI);
+ bstrElementNamespaceURI = NULL;
+
+ if (pCurElement)
+ {
+ pCurElement->Release();
+ }
+ }
+
+ if (pCurNode)
+ {
+ pCurNode->Release();
+ }
+ }
+ }
+
+ if (pChildren)
+ pChildren->Release();
+
+ if (SUCCEEDED(hr) && (*ppElement))
+ {
+ hr = S_OK;
+ }
+ else
+ {
+ hr = S_FALSE;
+ }
+
+ return hr;
+
+}
+
+/*++
+Routine Name:
+ GetXMLElementWithoutAttribute
+
+Routine Description:
+ Returns an XML element with given namespace URI.
+
+Arguments:
+
+ pParent - Parent of node to be found
+ pszElementNamespaceURI - Namespace URI to which the element belongs
+ pszElementName - Local Name of the element
+ ppChildElement - out pointer to xml element so retrieved
+
+Return Value:
+ HRESULT Completion status code
+ S_OK - On Successfully locating XML node specified
+ S_FALSE - If the specified XML node is not found
+ E_* - On Failure
+
+ Note: Since the module returns S_FALSE if the specified node is not found, the caller must be
+ careful to check hr status before proceeding, just checking for SUCCEEDED(hr) might
+ give wrong results
+
+--*/
+HRESULT OEMPTXMLHandler::GetXMLElementWithoutAttribute(
+ _In_ IXMLDOMElement *pContext,
+ _In_ PCWSTR pszElementNamespace,
+ _In_ PCWSTR pszElementName,
+ _Outptr_result_maybenull_ IXMLDOMElement **ppChildElement
+ ) const
+{
+ if (!pContext || !pszElementName || !pszElementNamespace || !ppChildElement)
+ {
+ return E_INVALIDARG;
+ }
+
+ HRESULT hr = S_OK;
+ LONG cChildren = 0;
+ IXMLDOMNodeList *pContextChildren;
+ *ppChildElement = NULL;
+
+ //
+ // Get the list of child nodes
+ //
+ hr = pContext->get_childNodes(&pContextChildren);
+
+ if (SUCCEEDED(hr))
+ hr = pContextChildren->get_length(&cChildren);
+
+ if (SUCCEEDED(hr))
+ {
+ for(LONG i = 0;i < cChildren && !(*ppChildElement);i++)
+ {
+
+ IXMLDOMNode *pCurrentNode;
+ IXMLDOMElement *pCurrentElement;
+ hr = pContextChildren->get_item(i, &pCurrentNode);
+
+ //
+ // See if its a DOM Element. Don't set hr here because we
+ // fully expect that not everything in the context is an element.
+ //
+ if (SUCCEEDED(hr) && S_OK == pCurrentNode->QueryInterface(IID_IXMLDOMElement, (void**)&pCurrentElement))
+ {
+ BSTR pbstrBaseName = NULL;
+ BSTR pbstrNamespaceURI = NULL;
+
+ hr = pCurrentElement->get_baseName(&pbstrBaseName);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pCurrentElement->get_namespaceURI(&pbstrNamespaceURI);
+ }
+ if (SUCCEEDED(hr) &&
+ pbstrNamespaceURI &&
+ pbstrBaseName &&
+ 0 == wcscmp(pbstrNamespaceURI, pszElementNamespace) &&
+ 0 == wcscmp(pbstrBaseName, pszElementName))
+ {
+ *ppChildElement = pCurrentElement;
+ (*ppChildElement)->AddRef();
+ }
+
+ pCurrentElement->Release();
+
+ SysFreeString(pbstrBaseName);
+ pbstrBaseName = NULL;
+
+ SysFreeString(pbstrNamespaceURI);
+ pbstrNamespaceURI = NULL;
+ }
+
+ if (pCurrentNode)
+ {
+ pCurrentNode->Release();
+ }
+ }
+ }
+
+ if (pContextChildren)
+ {
+ pContextChildren->Release();
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (*ppChildElement)
+ {
+ hr = S_OK;
+ }
+ else
+ {
+ hr = S_FALSE;
+ }
+ }
+
+ return hr;
+}
+
+/*++
+Routine Name:
+ GetXMLAttribute
+
+Routine Description:
+ Returns an XML Attribute node with given namespace URI.
+
+Arguments:
+
+ pParent - Parent of node to be found
+ pszAttrNamespaceURI - Namespace URI to which the attribute belongs
+ pszAttrName - Local Name of the attribute
+ ppChildElement - out pointer to xml node so retrieved
+
+Return Value:
+ HRESULT Completion status code
+ S_OK - On Success
+ E_* - On Failure
+
+--*/
+HRESULT OEMPTXMLHandler::GetXMLAttribute(
+ _In_ IXMLDOMElement *pElement,
+ _Outptr_result_maybenull_ BSTR *ppAttrNamespaceURI,
+ _Outptr_result_maybenull_ BSTR *ppAttrName
+ ) const
+{
+ HRESULT hr = S_OK;
+ IXMLDOMNamedNodeMap *pAttrMap = NULL;
+ IXMLDOMNode *pAttrNode = NULL;
+
+ if (!pElement || !ppAttrNamespaceURI || !ppAttrName)
+ {
+ return E_INVALIDARG;
+ }
+
+ *ppAttrNamespaceURI = NULL;
+ *ppAttrName = NULL;
+
+ hr = pElement->get_attributes(&pAttrMap);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pAttrMap->getNamedItem(MSXMLSTR(printschema::NAME_ATTRIBUTE_NAME),&pAttrNode);
+ }
+
+ if (S_OK == hr)
+ {
+ BSTR bstrAttrQName = NULL;
+ IXMLDOMAttribute *pAttribute = NULL;
+
+ hr = pAttrNode->QueryInterface(IID_IXMLDOMAttribute, (void **)&pAttribute);
+
+ if (SUCCEEDED(hr))
+ hr = pAttribute->get_text(&bstrAttrQName);
+
+ if (S_OK == hr && bstrAttrQName)
+ hr = getUri(pElement, bstrAttrQName, ppAttrNamespaceURI);
+
+ if (S_OK == hr && bstrAttrQName)
+ hr = getLocalName(bstrAttrQName, ppAttrName);
+
+ if (pAttribute)
+ pAttribute->Release();
+
+ if (bstrAttrQName)
+ {
+ SysFreeString(bstrAttrQName);
+ bstrAttrQName = NULL;
+ }
+ }
+
+ if (pAttrMap)
+ pAttrMap->Release();
+ if (pAttrNode)
+ pAttrNode->Release();
+
+ if (FAILED(hr))
+ {
+ if (*ppAttrNamespaceURI)
+ {
+ SysFreeString(*ppAttrNamespaceURI);
+ *ppAttrNamespaceURI = NULL;
+ }
+ if (*ppAttrName)
+ {
+ SysFreeString(*ppAttrName);
+ *ppAttrName = NULL;
+ }
+ }
+ return hr;
+}
+
+/*++
+Routine Name:
+ GetFeatureNode
+
+Routine Description:
+
+ Returns the feature node with specified attribute under printschema namespace
+
+Arguments:
+ pParent - parent node of the feature node to be located
+ pszAttrNamespaceURI - namespace URI for the attribute node of given feature node
+ pszAttrName - attribute name for given feature node
+ ppElement - OUT pointer to feature node to be returned
+
+Return Value:
+ HRESULT Completion status code
+ S_OK - On Successfully locating Feature node
+ S_FALSE - If Feature node is not found
+ E_* - On Failure
+
+
+--*/
+HRESULT OEMPTXMLHandler::GetFeatureNode(
+ _In_opt_ IXMLDOMElement *pParent,
+ _In_ PCWSTR pszAttrNamespaceURI,
+ _In_ PCWSTR pszAttrName,
+ _Out_ _When_(return == S_FALSE, _At_(*ppElement, _Post_maybenull_))
+ _When_(return != S_FALSE, _At_(*ppElement, _Post_valid_))
+ IXMLDOMElement **ppElement
+ ) const
+{
+ HRESULT hr = S_OK;
+
+ //
+ // If no parent is specified, set root of DOM tree as parent
+ //
+ if (!pParent)
+ {
+ pParent = m_pRootElement;
+ }
+
+ hr = GetXMLElement(pParent, printschema::FRAMEWORK_URI, printschema::FEATURE_ELEMENT_NAME, pszAttrNamespaceURI, pszAttrName, ppElement);
+
+ return hr;
+}
+
+/*++
+Routine Name:
+ GetOptionNode
+
+Routine Description:
+
+ Returns the option node under printschema namespace
+
+Arguments:
+ pParent - parent node of the option node to be located
+ ppElement - OUT pointer to option node to be returned
+
+Return Value:
+ HRESULT Completion status code
+ S_OK - On Successfully locating Option node
+ S_FALSE - If Option node is not found
+ E_* - On Failure
+
+--*/
+HRESULT
+OEMPTXMLHandler::GetOptionNode(
+ _In_ IXMLDOMElement *pParent,
+ _Outptr_result_maybenull_ IXMLDOMElement **ppElement
+ ) const
+{
+ return GetXMLElementWithoutAttribute(pParent, printschema::FRAMEWORK_URI, printschema::OPTION_ELEMENT_NAME, ppElement);
+}
+
+/*++
+
+Routine Name:
+ getLocalName
+Routine Description:
+ Returns Local name for input XML Qname.
+
+Arguments:
+ pszQName - Qname for which local name is to be found out
+ ppbstrLocalName - OUT pointer to local name
+
+Return Value:
+ HRESULT Completion status code
+ S_OK - On Success
+ E_* - On Failure
+
+--*/
+HRESULT
+OEMPTXMLHandler::getLocalName(
+ _In_ PCWSTR pszQName,
+ _Out_ BSTR *ppbstrLocalName
+ ) const
+{
+ HRESULT hr = S_OK;
+
+ if (!pszQName || !ppbstrLocalName)
+ {
+ return E_INVALIDARG;
+ }
+
+ *ppbstrLocalName = NULL;
+
+ PCWSTR pszStartPos = wcschr(pszQName,L':');
+
+ if (pszStartPos)
+ {
+ pszStartPos++;
+ }
+ else
+ {
+ pszStartPos = pszQName;
+ }
+
+ *ppbstrLocalName = SysAllocString(pszStartPos);
+
+ if (!(*ppbstrLocalName))
+ {
+ hr = E_OUTOFMEMORY;
+ }
+
+ return hr;
+}
+
+
+/*++
+Routine Name:
+ getUri
+Routine Description:
+ Returns namespace URI for given input Qname and in context of given XML DOM node.
+Arguments:
+ pContext - Input XML DOM node for current context
+ pszQName - input qname
+ pbstrURI - OUT pointer to URI to be returned
+Return Value:
+
+ S_OK if found
+ S_FALSE if not found
+ E_OUTOFMEMORY if the string could not be allocated
+ E_INVALIDARG if a non-optional param is null.
+
+Notes:
+
+ This routine assumes that the string has already been
+ validated, which should have happened in the managed code base.
+ The string should either be of the form "prefix:localname",
+ or "localname"
+
+--*/
+HRESULT
+OEMPTXMLHandler::getUri(
+ IXMLDOMElement *pContext,
+ _In_ PCWSTR pszQName,
+ _Out_ BSTR *pbstrURI
+ ) const
+
+{
+ HRESULT hr = S_OK;
+ BOOL bNodePushed = FALSE;
+
+ if (!(pContext && pbstrURI && pszQName))
+ {
+ return E_INVALIDARG;
+ }
+
+ PWSTR pszSplitPos = (_TCHAR*) wcschr(pszQName, L':');
+ INT cchUri = 0;
+ *pbstrURI = NULL;
+
+ //
+ // if splitpos is null, then there is no namespace
+ // prefix, which means return the default namespace.
+ //
+ wchar_t wchSwapChar = L'\0';
+ if (pszSplitPos)
+ {
+ wchSwapChar = *pszSplitPos;
+ *pszSplitPos = (wchar_t)0;
+ }
+ else
+ {
+ pszQName = L"";
+ }
+
+ hr = m_pNSManager->pushNodeContext(pContext, TRUE);
+
+ if (SUCCEEDED(hr))
+ {
+ bNodePushed = TRUE;
+ // get the length first, then alloc the string
+#pragma prefast(suppress:__WARNING_INVALID_PARAM_VALUE_1, "According to MSDN, it is acceptable to pass a NULL buffer to getURI.")
+ hr = m_pNSManager->getURI(pszQName, NULL, NULL, &cchUri);
+ }
+
+ if (S_OK == hr)
+ {
+ if (SUCCEEDED(hr))
+ {
+ // account for null terminator
+ cchUri += 1;
+
+ *pbstrURI = SysAllocStringLen(NULL, cchUri);
+ if (!(*pbstrURI))
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = m_pNSManager->getURI(pszQName, NULL, *pbstrURI, &cchUri);
+ }
+
+ if (pszSplitPos)
+ {
+ *pszSplitPos = wchSwapChar;
+ }
+
+ if (FAILED(hr) && (*pbstrURI))
+ {
+ SysFreeString(*pbstrURI);
+ *pbstrURI = NULL;
+ }
+ }
+
+ if (bNodePushed)
+ {
+ hr = m_pNSManager->popContext();
+ }
+
+ return hr;
+}
+
+/*++
+
+Routine Name:
+ getPrefix
+Routine Description:
+
+ Returns predefined Prefix for the input namespace URI if one already exists
+
+Arguments:
+ pContext - Current context node
+ pszUri - namespace URI for which prefix is to be obtained
+ pbstrPrefix - prefix string to be returned, if NULL routine just checks for
+ existence of prefix
+
+Return Value:
+ HRESULT Completion status code
+ S_OK - on success
+ S_FALSE - on failure
+
+--*/
+HRESULT
+OEMPTXMLHandler::getPrefix(
+ _In_ IXMLDOMElement *pContext,
+ _In_ PCWSTR pszUri,
+ _Outptr_opt_result_maybenull_ BSTR *pbstrPrefix
+ ) const
+{
+ HRESULT hr = S_OK;
+ BOOL bNodePushed = FALSE;
+ INT cchPrefix = 0;
+
+ if (!pContext || !pszUri)
+ return E_INVALIDARG;
+
+ if (pbstrPrefix)
+ {
+ *pbstrPrefix = NULL;
+ }
+
+ //Push context "Deep" to get info abt all URI prefix mappings defined so far in the current DOM document
+ hr = m_pNSManager->pushNodeContext(pContext, TRUE);
+
+ if (SUCCEEDED(hr))
+ {
+ bNodePushed = TRUE;
+
+ // get the length first, then alloc the string
+
+ hr = m_pNSManager->getPrefix(pszUri, 0, NULL, &cchPrefix);
+ }
+
+
+ if (S_OK == hr && pbstrPrefix)
+ {
+
+ *pbstrPrefix = NULL;
+ cchPrefix++;
+
+ if (SUCCEEDED(hr))
+ {
+ *pbstrPrefix = SysAllocStringLen(NULL, cchPrefix);
+
+ if (!(*pbstrPrefix))
+ hr = E_OUTOFMEMORY;
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = m_pNSManager->getPrefix(pszUri, 0, *pbstrPrefix, &cchPrefix);
+
+ }
+
+ if (FAILED(hr) && (*pbstrPrefix))
+ {
+ SysFreeString(*pbstrPrefix);
+ *pbstrPrefix = NULL;
+ }
+ }
+
+ // Note: MSXML NSManager returns S_FALSE for an undeclared URI, and E_FAIL for an undeclared
+ // prefix, hence it is safe to check for both here.
+
+ if (E_FAIL == hr || S_FALSE == hr)
+ {
+ // NS manager didn't find the prefix. Tweak the result to S_FALSE
+ hr = S_FALSE;
+ }
+
+ if (bNodePushed)
+ {
+ m_pNSManager->popContext();
+ }
+
+ return hr;
+}
+
+/*++
+
+Routine Name:
+ declarePrefix
+
+Routine Description:
+
+ This declares a new prefix for a given URI. This routine will always generate a new
+ prefix definition, regardless of whether the URI is already defined. Use getPrefix
+ to prevent duplicate definitions of namespace URI prefixes. If the preferred prefix is
+ already in use or no preferred prefix is supplied the namespace will be declared using
+ a generated namespace name.
+
+Arguments:
+ pContext - The context in which you want to ensure that the prefix is properly declared
+ in. This is where we check to see if the prefix is already in use. However, if not
+ in use, the prefix will always be declared in the root of the document. This gaurantees
+ 2 things: The prefix is declared in the entire document (though it may be overridden
+ locally in other places), and the prefix has the expected meaning at the passed in pContext.
+ pszUri - The URI to associate with the prefix.
+ pszPreferredPrefix - prefix to use if not already defined, can be a BSTR or PCWSTR, either
+ is acceptable
+ pbstrNewPrefix - The actual prefix that was declared, which may be different from the
+ preferred prefix
+
+Return Value:
+ HRESULT Completion status code
+ S_OK - on success
+ S_FALSE - on failure
+
+--*/
+HRESULT
+OEMPTXMLHandler::declarePrefix(
+ _In_ IXMLDOMElement *pContext,
+ _In_ PCWSTR pszUri,
+ _In_opt_ PCWSTR pszPreferredPrefix,
+ _Out_ BSTR *pbstrNewPrefix
+ )
+{
+ HRESULT hr = S_OK;
+
+ // These two strings are tied ... the second string is used to print the number over top of a
+ // copy of the first string, so chances are, if you change one, you need to change the other.
+ PCWSTR szPrefixChars = L"ns0000";
+ PCWSTR szPrefixPrintfPattern = L"ns%.4X";
+
+ if (!(pContext && pszUri && pbstrNewPrefix))
+ {
+ return E_INVALIDARG;
+ }
+
+ *pbstrNewPrefix = NULL;
+
+ // Check if the preferred prefix exists...
+ //
+ // If we got S_OK, the prefix exists, if we got E_*, bail out
+ // anyway. Only declare the prefix if we successfully decided
+ // it doesn't exist.
+
+ if (pszPreferredPrefix)
+ {
+ INT unused = 0;
+
+ if (SUCCEEDED(hr))
+ {
+ hr = m_pNSManager->pushNodeContext(pContext, TRUE);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+#pragma prefast(suppress:__WARNING_INVALID_PARAM_VALUE_1, "According to MSDN, it is acceptable to pass a NULL buffer to getURI.")
+ hr = m_pNSManager->getURI(pszPreferredPrefix, NULL, NULL, &unused);
+
+ // If the prefix is already defined, MSXML will return S_FALSE. In that
+ // case, we don't want to declare another namespace with the same prefix
+ if (S_FALSE != hr)
+ {
+ pszPreferredPrefix = NULL;
+ }
+
+ hr = m_pNSManager->popContext();
+ }
+
+ if (SUCCEEDED(hr) && pszPreferredPrefix)
+ {
+ *pbstrNewPrefix = SysAllocStringLen(pszPreferredPrefix, (UINT) wcslen(pszPreferredPrefix));
+ }
+ }
+
+ // If we don't have a workable prefix, try to generate a namespace
+ // Namespaces are in the form of NSxxxx where the x's are hex digits,
+ // and there are sizeof(m_nextPrefix)*2 of them
+ if (SUCCEEDED(hr) && !(*pbstrNewPrefix))
+ {
+ BOOL bFound = TRUE;
+ UINT cchLength = (UINT) wcslen(szPrefixChars);
+ *pbstrNewPrefix = SysAllocStringLen(NULL, cchLength);
+ if (!(*pbstrNewPrefix))
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ if (SUCCEEDED(hr))
+ {
+ do
+ {
+ INT unused = 0;
+
+ // We're really unlikely to hit this. If we do we probably
+ // have bigger problems. but put a reasonable cap on the
+ // generated namespaces anyway.
+ if (((UINT)m_dwNextIndex) >= (UINT)0xFFFF)
+ {
+ // We could reset the counter here, but it's very unlikely to
+ // help matters, and could end up costing tons of CPU time if
+ // caller ignored the error or retried.
+ hr = E_FAIL;
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // now fill in the real value.
+ hr = StringCchPrintf(*pbstrNewPrefix, cchLength+1, szPrefixPrintfPattern, (INT)m_dwNextIndex);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // look up the URI associated with the prefix.
+#pragma prefast(suppress:__WARNING_INVALID_PARAM_VALUE_1, "According to MSDN, it is acceptable to pass a NULL buffer to getURI.")
+ bFound = (S_OK == m_pNSManager->getURI(*pbstrNewPrefix, pContext, NULL, &unused));
+ }
+
+ m_dwNextIndex++;
+
+ } while(SUCCEEDED(hr) && bFound);
+
+ if (bFound)
+ {
+ SysFreeString(*pbstrNewPrefix);
+ *pbstrNewPrefix = NULL;
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) && *pbstrNewPrefix)
+ {
+ // We'll always put declarations at the root
+ if (SUCCEEDED(hr))
+ {
+ // Create the namespace declaration string
+ UINT cchDeclLength = (UINT)(wcslen(L"xmlns:") + wcslen(*pbstrNewPrefix));
+
+ BSTR bstrDecl = SysAllocStringLen(NULL, cchDeclLength);
+ if (!bstrDecl)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+
+ // Add the new declaration to the root of the document. This is important because
+ // we really don't want to re-declare the namespace at every element. Just starts
+ // to look really messy, and adds a lot of bloat.
+ if (SUCCEEDED(hr))
+ {
+ hr = StringCchPrintf(bstrDecl, cchDeclLength+1, L"xmlns:%s", *pbstrNewPrefix);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = CreateXMLAttribute(m_pRootElement, bstrDecl, L"", pszUri);
+ }
+
+ SysFreeString(bstrDecl);
+ bstrDecl = NULL;
+
+ // Explicitly let the namespace manager know about our prefix
+
+ if (SUCCEEDED(hr))
+ {
+ hr = m_pNSManager->declarePrefix(*pbstrNewPrefix, pszUri);
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ SysFreeString(*pbstrNewPrefix);
+ *pbstrNewPrefix = NULL;
+ }
+
+ return hr;
+}
+
+/*++
+Routine Name:
+ DeleteFeatureNode
+Routine Description:
+
+ Deletes given input Feature node and all its children from DOM tree
+
+Arguments:
+
+ pParent - Parent node of the XML DOM node to be deleted
+ pElement - XML DOM node to be deleted
+
+Return Value:
+ HRESULT Completion status code
+ S_OK - On Success
+ E_* - On failure
+--*/
+HRESULT
+OEMPTXMLHandler::DeleteFeatureNode(
+ _In_opt_ IXMLDOMNode *pParent,
+ _In_ IXMLDOMNode *pElement
+ )
+{
+ HRESULT hr = S_OK;
+
+ IXMLDOMNode *pRemovedElement = NULL;
+
+ if (!pElement)
+ {
+ return E_INVALIDARG;
+ }
+
+ //
+ // if no parent node specified assume root of DOM tree as parent
+ //
+
+ if (!pParent)
+ {
+ pParent = m_pRootElement;
+ }
+
+ //
+ // MSXML function removeChild removes the given child of the XML
+ // element and all its children, even the Attribute nodes defined
+ // for given element node if any are removed. Hence we do not need
+ // to worry about recursively removing all the children of the
+ // given node before removing it
+ //
+
+ hr = pParent->removeChild(pElement, &pRemovedElement);
+
+ if (pRemovedElement)
+ {
+ pRemovedElement->Release();
+ }
+
+ return hr ;
+}