Sample Code
Windows Driver Samples/ OEM Printer Customization Plug-in Samples/ C++/ PTPCPlPr/ src/ xmlhandler.cxx/
// 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 ; }
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials