SAPI identifying more than 2 properties - c++

I found this on google while searching on some information on SAPI identifying phrases. This example shows if there is only one property in the rule. So what if there are 2 or more properties in that rule? How would one go about writing the code for this? I am still confused about SAPI and trying to understand it. Any help is welcome, thanks!
The alternate method is add a property to your list tag/items [you appear to
be familiar with properties], iterate through the property tree to find the
property, and then retrieve the associated item from the property. Note if
this is the only property in your recognized rule, then it is fairly easy to
retrieve the property [no need to navigate the property tree].
For example, you could change your rule to be the following:
<RULE ID="VID_Vcs">
<L PROPNAME="NAME_LIST">
<P VAL="1">Mike </P>
<P VAL="2">Davor </P>
<P VAL="3">Kurt </P>
<P VAL="4">Steve </P>
</L>
</RULE>
Use the following code to retrieve the list item value/phrase text
SPPHRASE* pPhrase = NULL;
hr = cpRecoResult->GetPhrase(&pPhrase);
// Check hr
// Let's assume that you only have one property in your rule, so there is only one property in the property tree.
// ASSERT: NULL == pPhrase->pProperties->pNextSibling && NULL == pPhrase->pProperties->pFirstChild
// ASSERT: NULL != pPhrase->pProperties->pszName && 0 == wcscmp(pPhrase->pProperties->pszName, L"NAME_LIST")
// retrieve the list item index [e.g. 1-4], see VAL XML tags in aforementioned grammar
long lRecognizedListItemIndex = pPhrase->pProperties->vValue.lVal;
// retrieve the phrase text
hr = cpRecoResult->GetText(pPhrase->pProperties->ulFirstElement, pPhrase->pProperties->ulCOuntOfElements, FALSE, &pwszListItem, NULL);
// Check hr
// pwszListItem now contains the recognized list item
//compared to the phrase tag of the dictionary (XML)
if(SUCCEEDED (hResult)) {
if ((pPhrase->pProperties != nullptr) && (pPhrase->pProperties->pFirstChild != nullptr)){
const SPPHRASEPROPERTY* pRule = pPhrase->pProperties->pFirstChild ;
if (pRule->SREngineConfidence->confidence Threshold) {
if ( wcscmp ( L"word one", pRule->pszValue) == 0 ) {
//do stuff here
}
else if ( wcscmp ( L"word two", pRule->pszValue) == 0 ) {
//do stuff here
}
else if ( wcscmp ( L"word three", pRule->pszValue) == 0 ) {
//do stuff here
}
else if ( wcscmp ( L"word four", pRule->pszValue ) == 0) {
//do stuff
}
}
}
}

Alright, so sorry for the wait. I whipped up a simple program that may help you figure out what you're trying to do.
So here's my grammar file:
<GRAMMAR LANGID="409">
<DEFINE>
<ID NAME="LIKE_VAL" VAL="1"/>
<ID NAME="SUBJECT_VAL" VAL="2"/>
<ID NAME="COMBINED_VAL" VAL="3"/>
<ID NAME="EXIT_VAL" VAL="4"/>
</DEFINE>
<RULE NAME="LIKE_VAL" TOPLEVEL="ACTIVE">
<L>
<P>I <O>really</O> like</P>
<P>I <O>really</O> do not like</P>
</L>
</RULE>
<RULE NAME="SUBJECT_VAL" TOPLEVEL="ACTIVE">
<P>ponies.</P>
</RULE>
<RULE NAME="COMBINED_VAL" TOPLEVEL="ACTIVE">
<RULEREF NAME="LIKE_VAL"/>
<RULEREF NAME="SUBJECT_VAL"/>
</RULE>
<RULE NAME="EXIT_VAL" TOPLEVEL="ACTIVE">
<L>
<P>Exit</P>
<P>Quit</P>
<P>Terminate</P>
<P>Deluminate</P>
</L>
</RULE>
</GRAMMAR>
And here's a full program that uses it:
#include "sphelper.h"
#include <Windows.h>
#include <string>
int main(int argc, char* argv[])
{
CComPtr<ISpRecognizer> cpReco;
CComPtr<ISpRecoContext> cpRecoCtx;
CComPtr<ISpRecoGrammar> cpRecoGram;
ULONGLONG ullEvents = SPFEI(SPEI_RECOGNITION)|
SPFEI(SPEI_FALSE_RECOGNITION);
ISpObjectToken* pInputToken;
ISpRecoResult* cpRecoRslt;
HRESULT hr = S_OK;
hr = ::CoInitialize(NULL);
hr = cpReco.CoCreateInstance(CLSID_SpInprocRecognizer);
hr = cpReco->CreateRecoContext(&cpRecoCtx);
hr = cpRecoCtx->CreateGrammar(0, &cpRecoGram);
hr = cpRecoCtx->SetNotifyWin32Event();
hr = cpRecoCtx->SetInterest(ullEvents, ullEvents);
hr = SpGetDefaultTokenFromCategoryId(SPCAT_AUDIOIN, &pInputToken);
hr = cpReco->SetInput(pInputToken, FALSE);
hr = cpRecoGram->LoadCmdFromFile(L"Your_Test_File.cfg",SPLO_STATIC);
hr = cpReco->SetRecoState(SPRST_ACTIVE);
hr = cpRecoCtx->SetContextState(SPCS_ENABLED);
hr = cpRecoGram->SetGrammarState(SPGS_ENABLED);
hr = cpRecoGram->SetRuleState(NULL, NULL, SPRS_ACTIVE);
std::wstring strExit = L"Exit";
std::wstring strExitRuleName = L"EXIT_VAL";
CSpEvent spEvent;
bool isListening = true;
do{
hr = cpRecoCtx->WaitForNotifyEvent(INFINITE);
if(spEvent.GetFrom(cpRecoCtx) == S_OK)
{
switch(spEvent.eEventId){
case SPEI_RECOGNITION:{
WCHAR* strReco = 0;
cpRecoRslt = spEvent.RecoResult();
cpRecoRslt->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &strReco, NULL);
printf("%ls\n",strReco);
SPPHRASE *phrase = NULL;
cpRecoRslt->GetPhrase(&phrase);
if(phrase){
std::wstring ruleName = phrase->Rule.pszName;
if(strExitRuleName.compare(strExit)==0){
isListening = false;
}
}
break;
}
case SPEI_FALSE_RECOGNITION:{
printf("False Recognition\n");
break;
}
}
}
}while(isListening);
cpRecoGram.Release();
cpRecoCtx.Release();
cpReco.Release();
::CoUninitialize();
printf("Press any key to continue...");
getchar();
return 0;
}
You'll have to change the path of where the load grammar call is loading from. From what I understand what you're attempting to do is create grammar in a context free grammar file AND try to do this programmatically as well. Typically you start with a grammar file and modify when you need to.
If, however, you REALLY REALLY need to add new grammars programmatically, such as when someone's typing in new grammar to be recognized, THEN you'd chance SPLO_STATIC to SPLO_DYNAMIC and start implementing the code you see in the later half of the MSDN post you saw.
I completely left out any error checking. If you need to access other properties of the rule you're looking at, use the pPhrase->GetPhrase(&phrase) area. Other than just the rule's name you can also get it's ID.

Related

Attempt at using libxml2 to validate xml file with xsd file

I am creating a tool that gets an xml file that contains input data for set tool, generated by some other program. Before I can use this input data, I should validate the xml file, make sure all the data is there. I am attempting to create an xsd file to do the validation (using the libxml2 library).
The c++ code that attempts at validating this, is quite rudimentary for now:
XmlValidator inputXmlValidator{};
res = inputXmlValidator.ConfigureValidationSchema("../temp_test_inputs/xml_validation/input.xsd");
if (!res)
return -1;
res = xmlValidator.ValidateXml("../temp_test_inputs/xml_validation/input.xml");
if (!res)
return -1;
bool XmlValidator::ConfigureValidationSchema(const char* xsdFilename)
{
if (schemaValidationContext != nullptr)
{
F1_ERROR("This instance already has a validation scheme configured!");
return false; // there is already a schema present...
}
xmlSchemaParserCtxtPtr schemaParserContext = nullptr;
schemaParserContext = xmlSchemaNewParserCtxt(xsdFilename);
if (schemaParserContext)
{
xmlSchemaPtr parsedSchema = nullptr;
xmlSchemaSetParserStructuredErrors(schemaParserContext, ProcessParsingError, nullptr);
parsedSchema = xmlSchemaParse(schemaParserContext);
xmlSchemaFreeParserCtxt(schemaParserContext);
if (parsedSchema)
schemaValidationContext = xmlSchemaNewValidCtxt(parsedSchema);
}
return schemaValidationContext != nullptr;
}
bool XmlValidator::ValidateXml(const char* xmlFilename)
{
if (schemaValidationContext == nullptr)
{
F1_ERROR("No validation scheme configured! Failed to validate '{0}'.", xmlFilename);
return false; // there is no validation schema present...
}
// read the xml file
xmlTextReaderPtr xmlTextReader = xmlReaderForFile(xmlFilename, NULL, 0);
if (xmlTextReader == nullptr)
{
F1_ERROR("Failed to open '{0}'.", xmlFilename);
return false; // failed to read xml file...
}
// configure schema validation
int hasSchemeErrors = 0;
xmlTextReaderSchemaValidateCtxt(xmlTextReader, schemaValidationContext, 0);
xmlSchemaSetValidStructuredErrors(schemaValidationContext, ProcessValidatorError, &hasSchemeErrors);
// process the xml file
int hasValidationErrors = 0;
do
{
hasValidationErrors = xmlTextReaderRead(xmlTextReader);
} while (hasValidationErrors == 1 && !hasSchemeErrors);
// process errors
//if (hasValidationErrors != 0)
//{
// xmlErrorPtr err = xmlGetLastError();
// F1_ERROR("Failed to parse '{0}' at line {1}, col {2}! Error {3}: {4}", err->file, err->line, err->int2, err->code, err->message);
//}
// free up the text reader memory
xmlFreeTextReader(xmlTextReader);
return hasValidationErrors == 0; // return true if no errors found
}
I am quite sure this code works, as I attempted it with the shiporder example. Now I attempt at moving away from the example and alter it for my own xml file.
I went with the most basic xml file (only the root element and an attribute)
<?xml version="1.0" encoding="UTF-8"?>
<root timestamp="20220714 1324">
</root>
And I cannot create a suitable xsd file that can validate this:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema elementFormDefault="qualified" attributeFormDefault="unqualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema" >
<xs:element name="root">
<xs:complexType>
<xs:attribute type="xs:string" name="timestamp"/>
</xs:complexType>
</xs:element>
</xs:schema>
I keep getting the error
Failed to validate '../temp_test_inputs/xml_validation/input.xml' at line 2, col 0! Error 1845: Element 'root': No matching global declaration available for the validation root.
After 3 days of searching, I could not find any solution that got me further than this error... Any ideas? In need I could alter the xml file, but preferably I would like to only tweak the xsd file (if possible). I'll take any solution at this point...
I found the mistake... And of course it's a stupid one...
My main function should have looked like
auto res = xmlValidator.ConfigureValidationSchema("../temp_test_inputs/xml_validation/shiporder.xsd");
if (!res)
return -1;
res = xmlValidator.ValidateXml("../temp_test_inputs/xml_validation/shiporder.xml");
if (!res)
return -1;
F1_TEST_COMMON::XmlValidator inputXmlValidator{};
res = inputXmlValidator.ConfigureValidationSchema("../temp_test_inputs/xml_validation/input.xsd");
if (!res)
return -1;
res = inputXmlValidator.ValidateXml("../temp_test_inputs/xml_validation/input.xml");
if (!res)
return -1;
However, it had a 'minor' mistake, where it was using the shiporder.xsd (xmlValidator) rather than the input.xsd (inputXmlValidator)...
xmlValidator.ValidateXml("../temp_test_inputs/xml_validation/input.xml");
Only one variable mistake while copy pasting... Wasted over a week of debugging...

XSL Transformation in C++ catch message with terminate flag

I've made a project basing on this document to retrieve information from XML file basing on XSL file.
I am trying to throw an error in XSL file:
<xsl:if test="not(PIN/Length/text() = '4')">
<xsl:message terminate="yes">PIN length in input suppose to be 4</xsl:message>
</xsl:if>
But it seems not to work (no errors during work) - just like it is successfully done.
Can I somehow catch this message in C++?
void ManageXML::XML2Generic(string sOrgFilePath, string sOrgXSLFilePath, string sCpfPath)
{
wstring sTempFilePath = s2ws(sOrgFilePath);
LPCWSTR sFilePath = sTempFilePath.c_str();
wstring sTempXSLFilePath = s2ws(sOrgXSLFilePath);
LPCWSTR sXSLFilePath = sTempXSLFilePath.c_str();
HRESULT hr = S_OK;
IXMLDOMDocument *pXMLDom = nullptr;
IXMLDOMDocument *pXSLDoc = nullptr;
CHK_HR(CreateAndInitParserDOM(&pXMLDom));
CHK_HR(LoadXMLFile(pXMLDom, sFilePath, sOrgFilePath)); //cast to LPCWSTR
CHK_HR(CreateAndInitParserDOM(&pXSLDoc));
CHK_HR(LoadXMLFile(pXSLDoc, sXSLFilePath, sOrgXSLFilePath)); //cast to LPCWSTR
// Transform dom to a string:
CHK_HR(TransformDOM2Data(pXMLDom, pXSLDoc, sGenericResult));
CleanUp:
SAFE_RELEASE(pXSLDoc);
SAFE_RELEASE(pXMLDom);
this->CreateGenericFile(sCpfPath);
CoUninitialize();
}
One bad solution that comes to my mind is to make XSL like this:
<xsl:if test="not(PIN/Length/text() = '4')">
<xsl:text>MSXML_ERROR: PIN length in input suppose to be 4</xsl:message>
</xsl:if>
And
CleanUp:
SAFE_RELEASE(pXSLDoc);
SAFE_RELEASE(pXMLDom);
if (sGenericResult.find("MSXML_ERROR") != string::npos)
throw runtime_error("blah blah blah");
this->CreateGenericFile(sCpfPath);
CoUninitialize();

Getting runtime error information from IXMLDOMDocument::transformNode function

I'm using IXMLDOMDocument::transformNode to apply an XSLT transform using C++ code that looks a little bit like this:
CComPtr<IXMLDOMDocument2> spXMLDoc;
// load spXMLDoc
CComPtr<IXMLDOMDocument2> spXSLDoc;
// load spXSLDoc
BSTR *pResult;
HRESULT hr = spXMLDoc->transformNode( spXSLDoc, pResult );
if (FAILED(hr))
{
// Handle me
}
This code works, however if the transform fails then I don't know how to get any information about where or why it failed - at the moment its failing (on a complex XSLT with multiple includes) with a HRESULT of E_FAIL - is there any way I can get some more detail on why its failing?
I've already tried the parseError property to get more error detail:
IXMLDOMParseError *parseError = NULL;
hr = spXMLDoc->get_parseError(&parseError);
if ( !FAILED( hr ) )
{
long errorCode = 0;
hr = parseError->get_errorCode(&errorCode);
// etc...
The call to get_parseError succeeds, however the call to get_errorCode fails with HRESULT S_FALSE, indicating that there was not a parse error. This page tells me that there are two types of error - parse errors and runtime errors. It shows how to handle both, however appears to be JavaScript oriented - in my case no C++ runtime errors are thrown.
Sorry, I'm not sure from C++. You might try a quick command line transformation to help find the error in XSLT. There's a number of errors where the XSL will load, but can't transform. For an example pop an somewhere in the XSL file to trigger this type of error.
Here's a sample command line transformation tool. Write to transform.js and run cscript.exe transform.js from a command line
var strDOMObject = "MSXML2.FreeThreadedDOMDocument";
var strHTTPObject = "MSXML2.XMLHTTP";
var strTemplateObject = "MSXML2.XSLTemplate";
function transform( xml, xsl ) {
var xslt = new ActiveXObject( strTemplateObject );
var xmlReturn = new ActiveXObject( strDOMObject );
var xslProc;
try {
xslt.stylesheet = xsl;
} catch( e ) {
throw e;
}
xslProc = xslt.createProcessor();
xslProc.input = xml;
xslProc.transform();
return xslProc.output;
}
try {
var xml = new ActiveXObject( strDOMObject );
xml.load( "id.xml" );
var xsl = new ActiveXObject( strDOMObject );
xsl.load( "id.xsl" );
WScript.Echo( transform( xml, xsl ) );
} catch( err ) {
WScript.Echo (err.description );
}
Hope this helps, and that you can find out the C++ answer.

Getting IIS 7 Site properties

I have a C++ application that needs to retrieve an IIS 7 site's properties (such as metabase properties similar to those in IIS6 - Path, AppFriendlyName etc).
With IIS 7, my code does this:
Get the AppHostWritableAdminManager and commit the path MACHINE/WEBROOT/APPHOST/Default Web Site/.
Call GetAdminSection with the section name appSettings.
Then look at the returned collection and look for the property (Path for example).
This works in IIS 6 but not on IIS7/7.5.
What changes do I need to make in order to make this work?
In IIS7 the configuration data is not stored in a "metabase" and also the metabase properties that we're accustomed to in IIS6 aren't the same. IIS7 stores the bulk of its configuration data in the following file:
%systemroot%\System32\InetSrv\Config\applicationHost.config
There are other files, but for the purposes of answering this question, this is the file we're interested in.
The documentation for applicationHost.config can be found here:
<system.applicationHost> - IIS.NET
configuration Element [IIS 7 Settings Schema]
system.applicationHost Section Group [IIS 7 Settings Schema]
You can find a list of IIS6 metabase -> IIS7 XML configuration mappings here:
Converting Metabase Properties to Configuration Settings [IIS 7]
For example in IIS6 the path to a site's /root is stored in the Path attribute of IIsWebVirtualDir. i.e.:
<IIsWebServer Location="/LM/W3SVC/67793744" AuthFlags="0" ServerAutoStart="TRUE"
ServerBindings="217.69.47.170:80:app2.dev" ServerComment="app2" />
<IIsWebVirtualDir Location="/LM/W3SVC/67793744/root"
AccessFlags="AccessRead | AccessScript"
AppFriendlyName="Default Application"
AppIsolated="2"
AppRoot="/LM/W3SVC/67793744/Root"
AuthFlags="AuthAnonymous | AuthNTLM"
DirBrowseFlags="DirBrowseShowDate | DirBrowseShowTime | DirBrowseShowSize |
DirBrowseShowExtension | DirBrowseShowLongDate | EnableDefaultDoc"
Path="D:\websites\ssl-test\www\kerboom"
ScriptMaps="...">
But in IIS7 it's stored differently:
<sites>
<site name="Default Web Site" id="1" serverAutoStart="true">
<!-- this is the functional equivalent of the /root app in IIS6 -->
<application path="/">
<virtualDirectory path="/"
physicalPath="%SystemDrive%\inetpub\wwwroot" />
</application>
</site>
<sites>
However, if your code must work with both IIS6 and IIS7 then you can install the IIS6 Management Compatibility components. This will allow you to access IIS7 site properties using traditional IIS6 metabase API's such as ADSI, System.DirectoryServices etc. The compatibility layer will map these properties to the new IIS7 schema for you.
The first part of this article explains how to install this on Vista/Windows7/Windows 2008:
How to install ASP.NET 1.1 with IIS7 on Vista and Windows 2008 - see step #1
Update:
Unfortunately C++ isn't my strong point. However I put together an example in C# using COM Interop to demonstrate using the AppHostWritableAdminManager:
IAppHostWritableAdminManager wam = new AppHostWritableAdminManager();
IAppHostElement sites =
wam.GetAdminSection("system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST");
IAppHostElementCollection sitesCollection = sites.Collection;
long index = FindSiteIndex(sitesCollection, "MySite");
if(index == -1) throw new Exception("Site not found");
IAppHostElement site = sitesCollection[index];
IAppHostElementCollection bindings = site.ChildElements["bindings"].Collection;
for (int i = 0; i < bindings.Count; i++)
{
IAppHostElement binding = bindings[i];
IAppHostProperty protocolProp = binding.GetPropertyByName("protocol");
IAppHostProperty bindingInformationProp =
binding.GetPropertyByName("bindingInformation");
string protocol = protocolProp.Value;
string bindingInformation = bindingInformationProp.Value;
Debug.WriteLine("{0} - {1}", protocol, bindingInformation);
}
static long FindSiteIndex(IAppHostElementCollection sites, string siteName)
{
for (int i = 0; i < sites.Count; i++)
{
IAppHostElement site = sites[i];
Debug.WriteLine(site.Name);
IAppHostProperty prop = site.GetPropertyByName("name");
if(prop.Value == siteName)
{
return i;
}
}
return -1;
}
The code above locates a site named "MySite" in the <sites> collection. It then retrieves the site's <bindings> collection and print's each bindings protocol and bindingInformation attributes.
Your should be able to convert this to C++ fairly easily.
To answer the question in your comment -
For example, the path
system.applicationHost/Sites will get
me to the list of sites. Is there an
absolute way to get to my server
bindings like this (for example by
doing
system.applicationHost/Sites/Default
Web Site/Bindings
When using the AppHostWritableAdminManager there isn't a shortcut to getting directly to the site you want to inspect/modify or it's properties. In the example above, you'll see that I need to loop through the sites collection to find the site I'm interested in. The reason for this is that AppHostWritableAdminManager sees everything as elements and collections of elements. It's a fairly basic API. Even when using the managed Microsoft.Web.Administration API you find that whilst there are some nice properties such as Site.Bindings, these are thinly disguised wrappers around AppHostWritableAdminManager.
In fact if I want to find a site I still have to search the Sites collection either in a for loop or by adding some LINQ sugar if I'm using C#3.5 or later:
using(ServerManager serverManager = new ServerManager())
{
Site x = serverManager.Sites.FirstOrDefault(s => s.Name == "MySite");
}
Site's base class is ConfigurationElement which under the bonnet wraps access to IAppHostElement.
Once you're past some basic shortcut wrapper properties much of what we do in managed code to configure IIS (for example IIS FTP) is elements, attributes and collections of elements.
Update 2:
Please bear in mind I've never written a line of C++ in my life. There's no cleanup of strings or objects:
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <ahadmin.h>
#include <crtdbg.h>
static IAppHostElement*
FindSite(IAppHostElementCollection *pCollection, BSTR bstrSiteName);
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
IAppHostWritableAdminManager *pMgr = NULL;
IAppHostElement *pElem = NULL;
IAppHostElementCollection *pSitesCollection = NULL;
IAppHostElement *pSite = NULL;
IAppHostElement *pBindings = NULL;
IAppHostElement *pBinding = NULL;
IAppHostElementCollection *pBindingsCollection = NULL;
IAppHostChildElementCollection *pChildElements = NULL;
IAppHostProperty *pProtocol = NULL;
IAppHostProperty *pBindingInformation = NULL;
BSTR bstrSectionName = SysAllocString( L"system.applicationHost/sites" );
BSTR bstrConfigCommitPath = SysAllocString( L"MACHINE/WEBROOT/APPHOST" );
BSTR bstrSiteName = SysAllocString( L"MySite" );
BSTR bstrBindingsConst = SysAllocString( L"bindings" );
BSTR bstrBindingProtocol = SysAllocString( L"protocol" );
BSTR bstrBindingInformation = SysAllocString( L"bindingInformation" );
VARIANT vtPropertyName;
VARIANT vtIndex;
HRESULT hr = S_OK;
hr = CoCreateInstance( __uuidof(AppHostWritableAdminManager), NULL,
CLSCTX_INPROC_SERVER, __uuidof(IAppHostWritableAdminManager), (void**) &pMgr);
hr = pMgr->GetAdminSection(bstrSectionName, bstrConfigCommitPath, &pElem);
hr = pElem->get_Collection(&pSitesCollection);
pSite = FindSite(pSitesCollection, bstrSiteName);
hr = pSite->get_ChildElements(&pChildElements);
vtPropertyName.vt = VT_BSTR;
vtPropertyName.bstrVal = bstrBindingsConst;
hr = pChildElements->get_Item(vtPropertyName, &pBindings);
hr = pBindings->get_Collection(&pBindingsCollection);
DWORD bindingsCount;
hr = pBindingsCollection->get_Count(&bindingsCount);
for(int i = 0; i < bindingsCount; i++)
{
vtIndex.lVal = i;
vtIndex.vt = VT_I4;
hr = pBindingsCollection->get_Item(vtIndex, &pBinding);
hr = pBinding->GetPropertyByName(bstrBindingProtocol, &pProtocol);
hr = pBinding->GetPropertyByName(bstrBindingInformation, &pBindingInformation);
BSTR bstrProtocol;
BSTR bstrBindingInformation;
hr = pProtocol->get_StringValue(&bstrProtocol);
hr = pBindingInformation->get_StringValue(&bstrBindingInformation);
_tprintf(_T("Protocol: %s, BindingInfo: %s\n"), bstrProtocol, bstrBindingInformation);
}
CoUninitialize();
return 0;
}
IAppHostElement* FindSite(IAppHostElementCollection *pCollection, BSTR bstrSiteName)
{
DWORD count = -1;
pCollection->get_Count(&count);
BSTR bstrPropName = SysAllocString( L"name");
for(DWORD i = 0; i < count; i++)
{
IAppHostElement *site = NULL;
IAppHostProperty *prop = NULL;
BSTR bstrPropValue;
HRESULT hr = S_OK;
VARIANT vtCount;
VariantInit(&vtCount);
vtCount.lVal = i;
vtCount.vt = VT_I4;
hr = pCollection->get_Item(vtCount, &site);
hr = site->GetPropertyByName(bstrPropName, &prop);
hr = prop->get_StringValue(&bstrPropValue);
if(wcscmp(bstrPropValue, bstrSiteName) == 0)
{
return site;
}
}
return NULL;
}

problem parsing a xml file with MSXML4 in C++

Here is my parsing code:
MSXML2::IXMLDOMNodePtr pNode = m_pXmlDoc->selectSingleNode(kNameOfChild.c_str());
MSXML2::IXMLDOMNodeListPtr pIDOMNodeList = NULL;
MSXML2::IXMLDOMNodePtr pIDOMNode = NULL;
long numOfChildNodes= 0;
BSTR bstrItemText;
HRESULT hr;
MSXML2::IXMLDOMElementPtr pChildNode = m_pXmlDoc->getElementsByTagName(kNameOfChild.c_str());
hr = m_pXmlDoc->get_childNodes(&pIDOMNodeList);
hr = pIDOMNodeList->get_length(&numOfChildNodes);
And my xml file:
<?xml version="1.0"?>
<GovTalkMessage>
<EnvelopeVersion>1.0</EnvelopeVersion>
<Header>
<MessageDetails>
<Class>MOSWTSC2</Class>
<Qualifier>acknowledgement</Qualifier>
<Function>submit</Function>
<TransactionID>20021202ABC</TransactionID>
<CorrelationID>B07B9ED3176193DDC4EC39063848A927</CorrelationID>
<ResponseEndPoint PollInterval="10">
https://secure.gateway.gov.uk/poll
</ResponseEndPoint>
<GatewayTimestamp>2001-01-31T10:20:18.345</GatewayTimestamp>
</MessageDetails>
<SenderDetails/>
</Header>
<GovTalkDetails>
<Keys/>
</GovTalkDetails>
<Body/>
</GovTalkMessage>
kNameOfchild is "Qualifier"
pNode is always NULL
pChildNode is always NULL
hr returns S_OK
numOfChildNodes is always 0
So, what am I doing wrong?
Thanks
Try /GovTalkMessage/Header/MessageDetails/Qualifier for the XPath query.
You need to provide the xpath for the selectSingleNode call. There may be multiple "Qualifier" present in XML file, so in case you just pass "Qualifier" parser doesn't know which one to return. I haven't used XPath too many times, but I think this string should work for querying: "Header/MessageDetails[0]/Qualifier"