diff options
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.cxx | 1612 |
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 ; +} |
