Search This Blog

Monday, December 21, 2009

Using Microsoft Enterprise Library VAB with WCF services – Part 2

In Part 1 we looked at a simple solution on how to do service side validation for a simple WCF service and pass the resulting validation result back to the client.  In this article we’ll look at some other types of validators provided by the Enterprise Library and also touch the topic of rulesets and how they help.

So far we have looked at RangeValidator; let us look at some of its properties.

Bounds
The LowerBound and UpperBound of the validator are self-explanatory and denote the minimum and maximum value the validator can accept.

BoundType
The BoundType is synonymous to the bounds you have, it tells you whether the bounds are included, excluded or simply ignored.

Negated
If negated is set then we basically negate the rule, therefore in our example where we had the rangevalidator to accept only positive integers, setting negated=true would mean we only allow negative integers.  Kind of a redundant property in my opinion and simply complicates things.

Tag
Allows the developer to replace the name of the property which caused the validation fault to a custom string.  Useful when your property name is something like “accessDate” and you want to return “Access Date”.  Keep in mind that the custom Tag is only read when you have a custom message template.

MessageTemplate
Each validator provided by the Enterprise Library comes with its own message which gets returned on a validation fault, this can be customised by the developer.  For e.g. for the Range Validator we know (looking at the MSDN documentation) token {2} is the Tag, therefore our message template could be “{2} is invalid.” and we would get “Access Date is invalid.”

MessageTemplateResourceName/MessageTemplateResouceType
The following items come in handy when you have declared your messages in a resource file.

Ruleset
Allows the developer to target different validation rules depending on different criteria.  E.g. different clients may have different validation rules depending on different business logic.  This can be done by simply changing the config file to tell the validation block which ruleset to use as shown below:







To be continued…

Thursday, December 10, 2009

De/Se-rializing C# objects to XML

I have come across many projects over the years which need some sort of serializing and deserializing of C# objects to XML.  I usually remember the general syntax but more than often end up searching for the exact syntax.  There are tonnes of articles on this by the way and this blog is merely to give me a consistent way of doing this rather than anything else.

Say you have a couple of C# classes such that one class contains a collection of the other class and you want to serialize this into XML.

image

The way I usually do it is simply by adding some attributes to my C# classes which represent the structure of the resulting XML and run an XML serializer against it.  So my C# classes would potentially look like:

[XmlRoot(ElementName = "Customer", IsNullable = false)]
public class Customer
{
[XmlElement(ElementName = "Name")]
public string Name { get; set; }

[XmlElement(ElementName = "IsExpired")]
public bool IsExpired { get; set; }

[XmlElement(ElementName = "DOB")]
public DateTime DOB { get; set; }

[XmlArray("Orders")]
[XmlArrayItem("Order", Type = typeof(Order))]
public List<order> Orders { get; set; }
}

public class Order
{
[XmlAttribute(AttributeName = "OrderID")]
public int OrderID { get; set; }
}

With the above class we get an XML output as shown in the figure below:

image

And the code which actually does the serialization:

class Program
{
static void Main(string[] args)
{
Customer c = new Customer()
{
Name = "Ruskin Dantra",
DOB = DateTime.Today,
IsExpired = false,
Orders = new List<order>()
{
new Order() { OrderID = 1},
new Order() { OrderID = 2},
new Order() { OrderID = 3}
}
};

System.Xml.Serialization.XmlSerializer s = new System.Xml.Serialization.XmlSerializer(typeof(Customer));

XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;

XmlWriter writer = XmlWriter.Create(@"Customer.xml", settings);
s.Serialize(writer, c);
writer.Close();
}
}

If you want to deserialize the above created XML back into the object all you have to do is:

System.Xml.Serialization.XmlSerializer s = new System.Xml.Serialization.XmlSerializer(typeof(Customer));
XmlReader reader = XmlReader.Create(@"Customer.xml");
Customer deserializedCustomer = s.Deserialize(reader) as Customer;
reader.Close();

Its as easy as that :)

Wednesday, December 9, 2009

Using Microsoft Enterprise Library VAB with WCF services – Part 1

I am starting to write a series of blog articles, each varying in complexity, highlighting how we use can the Validation Application Blocks within the Microsoft Enterprise Library to do really neat validation.  Extremely flexible and configurable at runtime.  Although we will slowly build towards that.

In this first part let me just give a brief introduction to what the VAB actually is some simple ways of using it within a WCF service.

While writing WCF services we often cannot guarantee the data we get passed in, let it be simple data like integers or complex business objects such as Customers or even worse, process definitions.  Although, as developers we have to design some sort of clever program which can intelligently sorting out good data from bad data.  Good data is often derived from pages of use cases and business rules.  These rules tend to change often so it’s only advisable that we allow for this by allowing our program (in this case a WCF service) to read this rules at runtime.  This is our end goal.

In the early days I used to do simple checks on data to determine its validity.  Although strenuous and naive, it worked.  For e.g. if you have a service which finds the square root of a number then we know for a fact that it cannot accept a negative number.  So a developer has the following options:

  1. Don’t care about the erroneous input
  2. Cater for error scenarios (number being less than 0) and return a exception
  3. Cater for error scenarios (number being less than 0) and return a message
  4. Return an exception which has the message

Yes, the last option does look the most tempting but a bit harder to implement.  Let us try and work through the other options first, for all our scenarios we will have the same interface and client definition:

Interface:

namespace VABService
{
[ServiceContract]
public interface IMyService
{
[OperationContract]
double SquareRoot(double number);
}
}

Client:

namespace VABServiceClient
{
class Program
{
static void Main(string[] args)
{
service.MyServiceClient client = null;
try
{
client = new VABServiceClient.service.MyServiceClient();
double result = client.SquareRoot(4);
}
catch (Exception e)
{
}
finally
{
if (client != null && client.State != System.ServiceModel.CommunicationState.Faulted)
{
try
{
client.Close();
}
catch (Exception e)
{
// don't care
}
}
}
}
}
}


Don’t care about the erroneous input With this approach our implementation looks simple and we end up with:



namespace VABService
{
public class MyService : IMyService
{
#region IMyService Members
public double SquareRoot(double number)
{
return Math.Sqrt(number);
}
#endregion
}
}


This service implementation will allow for the correct square root to be returned but say instead of passing in a positive integer we pass in a negative integer: –4; can the service guarantee a deterministic result?  I don’t think so, the most the developer can say is that it will do whatever the .Net environment is set up to do.  Left as is the result coming back will be NaN.



image



If the client was a high risk financial application and the service returned this value, the result would be quite catastrophic unless both the client and the service came to some sort of prior agreement about this returned value.  Although wouldn’t it be nice if we raised an exception or an error of some kind sighting the problem?



Cater for error scenarios (with an Exception) To allow for an exception to be raised we have to slightly change the service implementation as shown below:



namespace VABService
{
public class MyService : IMyService
{
#region IMyService Members
public double SquareRoot(double number)
{
if (number < 0)
throw new ArgumentOutOfRangeException(string.Format("Square root of negative numbers cannot be determined:{0}.", number));
return Math.Sqrt(number);
}
#endregion
}
}


Now with the same client as before if we pass in a negative number we get a  exception although the client is nonetheless wiser than it was before:



image



You see the above exception because of an attempt to hide the implementation details of the service from the clients calling it.  Just as the exception states, if we set the flag IncludeExceptionDetailsInFaults to be true, we will actually see why the exception occurred:



image



Although this is opening up our service unnecessarily and clients can get a clear view of our implementation when things go wrong and lets face it, things always can go wrong!  So we want to avoid setting IncludeExceptionDetailsInFaults to true in a production environment.



Cater for error scenarios (with a message) To allow for this we have to add even more code to the implementation and also add another parameter to our interface which we can use to pass back the error message, which in this case is the exception text.  We all know how this is going to look, yes UGLY!  We don’t want to do that!!! This brings us to our last option and the most elegant solution.



Return an exception which has the message This is where Geers Blog comes in handy.  WCF exposes really neat things which can be used to do all sorts of validation but why do this when we can go one step further and use an already well thought and designed framework wrapper.  Yes, the VAB is exactly that.



The Meat

Yes so far all you have seen is bones and why, what…and blah blah, boring stuff.  The meat of the post is now here…let us use the VAB to do simple validation while finding the square root of a number.



First step would be to install the enterprise library and add a reference to (within your service project):





  • Microsoft.Practices.EnterpriseLibrary.Validation.integration.WCF




  • Microsoft.Practices.EnterpriseLibrary.Validation.dll




  • Microsoft.Practices.EnterpriseLibrary.Common.dll




After doing so we’ll need to make 3 changes…




  1. Service interface change

    Our service interface will now contain the following code.

    using System;
    using System.ServiceModel;
    using Microsoft.Practices.EnterpriseLibrary.Validation.Integration.WCF;
    using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;

    namespace VABService
    {
    [ServiceContract]
    public interface IMyService
    {
    [OperationContract]
    [FaultContract(typeof(ValidationFault))]
    double SquareRoot(
    [RangeValidator(0, RangeBoundaryType.Inclusive, Double.MaxValue, RangeBoundaryType.Inclusive)]
    double number);
    }
    }

    Note that we now have a rangevalidator for the parameter “number” and also declare that this particular interface method has a fault contract of type ValidationFault associated to it


  2. Config file
































    Note that we have only added new a endpoint behaviour, a behaviour extension and mapped our already existing endpoint to the newly added endpoint behaviour.


  3. Client change
    static void Main(string[] args)
    {
    service.MyServiceClient client = null;
    try
    {
    client = new VABServiceClient.service.MyServiceClient();
    double result = client.SquareRoot(-4);
    }
    catch (FaultException fe)
    {
    }
    catch (Exception e)
    {
    }
    finally
    {
    if (client != null && client.State != System.ServiceModel.CommunicationState.Faulted)
    {
    try
    {
    client.Close();
    }
    catch (Exception e)
    {
    // don't care
    }
    }
    }
    }

    With this change in the client, now instead of getting simply exceptions without any message we get a particular type of exception which we can catch and investigate as shown in the image below: 
    image



We’ll look at other validators in Part 2.

Debugging VB6 COM DLLs via .Net

I am sure there are other ways of doing this but this post is just a simple step by step guide on one of them.

More often than not we have to use legacy COM components while writing new applications, let it be for database access, business logic or to simply avoid duplicating code.  Every time I end up using a COM object in my .Net application I lose the ability to debug inside that COM object and mostly have to assume that whatever happens within that call is logically correct.  Sometimes I am not convinced so I need to see proof of it first hand, so what do I do, I debug the COM call.

Adding a COM object to .Net cannot be easier, simply add a reference to the COM object which is registered on your computer and .Net automatically creates what we call a CCW (COM Callable Wrapper).  This is widely documented here.

In order to demonstrate how to debug a COM call let us first create a very simple VB6 DLL called Project1.dll.  In this COM DLL I have a single class called Class1.cls which contains a very simple Add method as shown below:

Option Explicit

Public Sub Add(ByVal a As Integer, ByVal b As Integer, ByRef result As Integer)
result = a + b
Exit Sub
End Sub

After creating this method, I built the DLL to a location using the default build options within the VB6 IDE by going to File>Make Project1.dll.


After this was done I created a new ConsoleApplication solution in C# to reference this COM DLL.  Added a reference to Project1 which allowed me to do this:


Project1.Class1Class class1 = new Project1.Class1Class();
short res = 0;
class1.Add(1, 2, ref res);

Great, I can now use the add method which I wrote in the COM object, say if the COM method returns the wrong value and I want to step into that code, I simply go Debug>Step Into (F11).  But what happens is that the debugger simply steps over that code and gets the result.  What has happened is that none of the symbols for that Project1.dll were loaded into my IDE:

image


This can be achieved simply by building the VB6 DLL using symbolic information.  You can do this by right clicking on the VB6 project in your IDE and going to Properties>Compile and check the boxes which say “Create Symbolic Debug Info” and also select “No Optimization”.


image

After the above is done, rebuild the Project1.dll.  You’  Simply doing the above step will not allow you to step into the “Add” function because the .Net IDE still hasn’t loaded the symbols.  To allow for this we have to enabled “Unmanaged Debugging” for our project, this can be done by going into the Project Properties>Debug and checking the box “Enable unmanaged code debugging” as shown below.

image


Doing the above process will now load the symbols:

image


Doing the above steps will still not allow us to step into the VB6 code, for this we have to do a manual step.  Open up the code file which has the “Add” function defined and put a breakpoint at the desired line of code.  Now when you step into the “Add” method call you will hit the breakpoint on the VB6 side as shown below:


image


Voila! :)

Saturday, October 24, 2009

Using VB to show IIS sites within Installshield 2010

Quite often I have worked on projects where an installer is needed.  So far I am quite familiar with Installshield and its inner workings so this blog describes a simple piece of code which solves a generic issue I had.

More often than not we program in a web based world, a web based installer is needed and the obvious incline is to use IIS, well at least for me considering I am mainly of a .Net developer.  In Installshield everything is easy when it comes to installing something on the local IIS but it gets complicated when you want to customise this process and allow users to choose the website within which the application will be installed or even the name of the application.

Customising the name of the application is easy and can be done simply by using what Installshield refers to as “properties”.  Expose a simple edit box and then in your “Internet Information Services” node simply change the “Name” to point to the property mapped to the edit box (e.g. “[MY_VIRTUAL_DIRECTORY]”).

Things get complicated when you want to show the user a list of websites to which your application can be added to, by default it goes to the “Default Web Site” or at least that’s what I assume but not always as stated by this table here.

Saying that, there is no easy way of showing the user a list of already existing websites on the local computer so after a bit of hunting around I found several solutions on the Installshield forums which advised me of a particular VB script which does what we need.

Dim objViewDim objDB
Dim objInstaller
Dim objRecord
Dim objW3SVC, objIisWebSite
Dim lngOrder
Set objDB = Session.DatabaseSet
objInstaller = Session.Installer

'Delete any records that may exist
Set viewList = Database.OpenView("SELECT * FROM ComboBox")
viewList.ExecuteSet recList = viewList.Fetch

'delete any existing records
While Not (recList Is Nothing)
viewList.Modify 6, recList ' 6 = deleteSet
recList = viewList.Fetch
Wend
viewList.CloseSet

objView = objDB.OpenView("select * from ComboBox")

' enumerate webservers on localhost and populate ComboBox tableSet
objW3SVC = GetObject("IIS://localhost/W3SVC")
lngOrder = 1


For Each objIisWebSite In objW3SVC
If objIisWebSite.Class = "IIsWebServer" Then
If LCase(objIisWebSite.ServerComment) <> "administration web site" Then
' add site name to ComboBox table (which is used by our dialog ComboBox)
Set objRecord = objInstaller.CreateRecord(4)
objRecord.StringData(1) = "WEBSITE_DROPDOWN" ' property name
objRecord.IntegerData(2) = lngOrder ' order
objRecord.StringData(3) = objIisWebSite.Name ' value
objRecord.StringData(4) = objIisWebSite.ServerComment ' text
Call objView.Execute(objRecord) ' now add the record to the table
Call objView.Modify(7, objRecord) ' (7 = msiViewModifyInsertTemporary)

lngOrder = lngOrder + 1
End If
End If
Next
Call objView.Close

Note that the value of the combobox is “objIisWebSite.Name” and you might think this is the human readable string which appears as the website name, but its not.  The name of the website is a unique number (ID) assigned to it as shown in the figure below:

image

Therefore when you want to specify where your application will be added to, you have to modify the “Default Web Site” node (which you will add as the default behaviour and put all your applications under it).  A bit counter intuitive as earlier I simply thought that instead of naming it “Default Web Site” I have to map it to my property which corresponds to the drop down list, i.e. "WEBSITE_DROPDOWN”.  But the trick is to associate the “WEBSITE_DROPDOWN” property to the “Site Number” setting in the Default Web Site node.

Wednesday, April 29, 2009

Bhai Log Game 5 (28/04/09)

The game yesterday had some bittersweet moments.  Although we won it was not a victory we ought to cherish nor take into our heads…there is a long long road ahead of us.

We fielded first and luckily I did not have to eat my words when I said “yes we have 8”, when actually we only had 4 players.  It was a mediocre start in the first over which I bowled conceding 11 runs, 9 of those extras although it set up Jacinth with two strikes from the last two deliveries which he executed with class getting a wicket.  Our bowling wasn’t great…it just did enough as the batsman weren’t up to par so I think there is a still a lot of work to be done in that department.  Also our fielding was a bit shaky, we have to communicate!  The pick of the bowlers I have to say was Shashank.  Great pace and always on target.

Our batting was good…well good with respect to the deliveries and fielding of the other team.  We have to start hitting the side nets and not attempt hitting the back nets every time.  Lucky for us it paid of this time around.  We were also helped by poor bowling from the other side as they conceded 25 extras (~ 75 runs).  We also saw some good clean hitting by Kasun and Jessie.

Overall a good performance by the majority of the team although the senior players had a sub-par day, maybe that’s why one of them decided to stay back and make up for it ;).

Let us carry this winning way over to next week when we play the top of the table, an unbeaten team, we’ll change that next week :).

image

imageimage

Thursday, April 23, 2009

Bhai Log Game 4 (21/04/09)

Bhai Log continues to dive further into slum.  Read more about it here.  After a disappointing two weeks of cricket, Bhai Log could not record a win in what could be defined as the easiest game so far.  After restricting the opposition for a meagre 106, Bhai Log failed to deliver with the bat.

A mediocre bowling and fielding attempt from Bhai Log was enough to restrict the opposition to 106 so imagine if Bhai Log fired every time at the right times, they would be the oppositions biggest nightmare.  This week we took a turn in the below par performances where our batting was, no other word for it, absolutely shocking.  We scored a mere 40 runs off the first two partnership with an atrocious third partnership with –4.  I seriously don’t understand what part of “hit the side nets” is so complicated to understand.  The last partnership was a consolidation attempt as in no way were we expecting them to get more than 50 runs.

Jessie was the star of the match which was his first game after he returned from a long vacation in India.  Jessie, were you training there?  Can you tell us how you do it?

Hoping we pick up our act and start getting into the winning habit which has been the essence of Bhai Log since we remember.

Here are the stats.

image

image 

image

Wednesday, April 15, 2009

Bhai Log Game 3 (14/04/09)

Following on from our below par performance from last week Bhai Log surely lived up to it.  This is a team which came from winning the last season it played to being demolished by two mediocre opponents in two consecutive weeks.  Mind you the team is not the same as last season but yet, it is not the losing that matters, it is the way the loss occurs.

This week we had a 9.35 game and only five team members showed up on time.  You would’ve thought that a late game would mean that everyone would turn up on time.  Worst part is that one team member didn’t show up at all.  Quite a disappointment but that was expected given the history.  So the game began and the only thing we have won so far is the toss.  Elected to bowl considering we have a better chance of chasing and winning as we know how our game should be depending on the score, although that didn’t help at all.

First over was a shocker and unfortunately I was to blame for that giving away 13 runs although the shambles did not stop there.  We bowled atrociously, short balls galore and the batsmen did well to utilise our underpowered field.  There was no urgency in our fielding and I think that’s what lost us the game.  The opponent posted a huge total of 178.  In indoor cricketing terms that is close to impossible to attain.

Our batting started off well with 46 and 40 coming in the first two partnerships although the third partnership didn’t go as well as hoped leaving us to get 58 in the last partnership.  This was a huge ask on its own.

To sum things up, it was yet another below average performance from Bhai Log and shabby umpiring work was partly to blame.

I hope we bounce back in the next game to record our first graded round game win.  Neil and I bowled three overs each.  Matthew Scott batted twice as we were short one player.

image

image 

image

Sunday, April 12, 2009

Reading XML Schemas

XML information is everywhere in these times.  We heavily rely on XML to send and analyse information.  After dealing with XML in every aspect of development I realised that the internet is loaded with information on how to read, write and manipulate XML in every possible language (script) there is.  And to make things worse, I myself have a post up on how to read XML documents using DOM.

This motivated me to write this document and to give XML Schemas the respect they require.  What are XML schemas?  They can be simply explained as a skeleton for a given XML file.  An XML file can have any desired structure, XML schemas are what restrict them and keep them well-formed.  So an XML schema is something like a parent which looks after it’s child (the XML document).

Let us look at an example:

<?xml version="1.0" encoding="utf-8" ?>
<blog>
<topic date="2003-08-22">My first XML document</topic>
<topic date="2006-10-18">My second XML document</topic>
</blog>

The schema for the above XML is:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Blog">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="Topic">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="Date" type="xs:date" use="required" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

Now that we are familiar with XML schemas let us look at the actual reason of this post.  Given an XML schema, how do we read it?  How do we store the values within the schema, its relationships and other mandatory information required by XML data files.

The way I addressed this problem is using two passes:


  1. Reading all the information given in XML schema
    This was necessary as there can be attribute and element references throughout the file.  Therefore we first need to store all the information about the XML schema and then determine the relationships between each of the data elements.

  2. Forming relationships between elements
    After we store all the information about the XML schema, we need to traverse through it to form the relationships.  Relationships in this aspect represent the nested nature of the XML schema.

Let us look at how we start reading an XML schema.  For that we have to understand a little about the different aspects of an XML schema.  A good introduction is given by the W3C schools here.  Let me briefly go over the main types here as well just to have a reference to them:


  1. Simple types
    1. Elements
    2. Attributes
    3. Restrictions

  2. Complex types
    1. Elements
    2. Empty
    3. Elements only
    4. Text only
    5. Mixed
    6. Indicators
    7. <any>
    8. <anyAttribute>
    9. Substitution

  3. Data types

I decided that each of the above types can be represented by data structures which I can understand to make things simpler.  You don’t have to do this, you can simply rely on the .NET representation of these schema types.  Let us look at how I chose to represent each of these structures.  Let me introduce a class diagram which we will analyse a bit later.


image


The above diagram shows three interfaces which help us make things much simpler and it also abstracts us away from the .NET representation the schema:


  1. IAttribute
    Any attribute like schema object will inherit from this interface.  Note that I said “attribute type”, as I also want simple types (both implicitly and explicitly declared) to be used as attributes.

  2. IElementComplex
    Represents any complex type object within the schema.  This includes both, implicitly and explicitly declared types.

  3. IRelationship
    Merely a wrapper around the complex element type which makes us easier to form relationships between them which we will use in our second pass.

Note that the above classes were developed a while ago while working on my Part IV engineering project (in 2005), only now have I realised that there is little to no help on how to read an XML schema, therefore I decided to publish this post.  By no means is this “the only” or “the best” way.  And also I am sure there are better ways of doing this.

Let me briefly give you an example of what implicitly and explicitly declared schema elements are with the XML schema below:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="ImplicityExplicit" xmlns="http://tempuri.org/ImplicityExplicit.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mstns="http://tempuri.org/ImplicityExplicit.xsd" elementformdefault="qualified" targetnamespace="http://tempuri.org/ImplicityExplicit.xsd">
<!--Explicit complex type-->
<xs:complextype name="CTEntity" />

<!--Implicit complex type-->
<xs:element name="CTElemEntity">
<xs:complextype></xs:complextype>
</xs:element>

<!--Explicit simple type-->
<xs:simpletype name="STEntity">
<xs:restriction base="xs:string" />
</xs:simpletype>

<!--Implicit simple type-->
<xs:element name="STElemEntity">
<xs:simpletype>
<xs:restriction base="xs:string" />
</xs:simpletype>
</xs:element>

<!--Implicit shy simple type-->
<xs:element name="STElem" />

<!--An attribute-->
<xs:attribute name="attribute" />
</xs:schema>

Now that we know the basic different types of XML schema elements let us look at how we can read/extract them.  For this we rely heavily on the .NET XML classes.  Reading the XML schema is quite simple, it is done using the following code:

using System;
using System.Xml;
using System.Xml.Schema;

namespace XmlLibrary
{
public class SchemaReader
{
public String FileName { get; set; }
public XmlSchema Schema { get; set; }

private bool SchemaLoaded { get; set; }

public SchemaReader(string fileName)
{
FileName = fileName;
ReadSchema();
}

public void ExtractElements()
{
if (SchemaLoaded && Schema != null)
{
XmlSchemaObjectCollection itemsColl = Schema.Items;
foreach (XmlSchemaObject item in itemsColl)
{
// TODO: Implementation
}
}
}

private void ReadSchema()
{
if (String.IsNullOrEmpty(FileName))
{
SchemaLoaded = false;
Schema = null;
}
else
{
try
{
Schema = XmlSchema.Read(new XmlTextReader(FileName), null);
SchemaLoaded = true;
}
catch (XmlException)
{
SchemaLoaded = false;
Schema = null;
}
catch (XmlSchemaException)
{
SchemaLoaded = false;
Schema = null;
}
}
}

}
}

The important method in the above code is the “ReadSchema” call.  Note that it takes in a string as a parameter which points to the schema file on disk and then reads this in the populates the System.Xml.Schema.XmlSchema object.  From this point forward we will only be concentrating on the ExtractElements method.


Let us examine the ExtractElements method closely.  Currently it is stubbed out, although if you remember I mentioned that XML has a nested/recursive nature to it, therefore we expect to call a distinctive method recursively.  Let us examine how we do it.  After fleshing out the method a bit further we have the following:

public void ExtractElements()
{
if (SchemaLoaded && Schema != null)
{
XmlSchemaObjectCollection itemsColl = Schema.Items;
foreach (XmlSchemaObject item in itemsColl)
{
ManipulateSchemaObject(item);
}
}
}

private void ManipulateSchemaObject(XmlSchemaObject schemaObject)
{
IndentLevel++;
if (schemaObject is XmlSchemaElement || schemaObject is XmlSchemaComplexType || schemaObject is XmlSchemaSimpleType)
{
#region XmlSchemaElement
if (schemaObject is XmlSchemaElement)
{
XmlSchemaElement schemaElement = schemaObject as XmlSchemaElement;
Console.WriteLine(String.Format("{0}XmlSchemElement: {1} ({2})", GetIndentationString(), schemaElement, schemaElement.Name));

ManipulateSchemaElement(schemaElement);
}
#endregion XmlSchemaElement
#region XmlSchemaComplexType
else if (schemaObject is XmlSchemaComplexType)
{
XmlSchemaComplexType explicitComplexType = schemaObject as XmlSchemaComplexType;
Console.WriteLine(String.Format("{0}XmlSchemaComplexType: {1} ({2})", GetIndentationString(), explicitComplexType, explicitComplexType.Name));
}
#endregion XmlSchemaComplexType
#region XmlSchemaSimpleType
else if (schemaObject is XmlSchemaSimpleType)
{
XmlSchemaSimpleType explicitSimpleType = schemaObject as XmlSchemaSimpleType;
Console.WriteLine(String.Format("{0}XmlSchemaSimpleType: {1} ({2})", GetIndentationString(), explicitSimpleType, explicitSimpleType.Name));
}
#endregion XmlSchemaSimpleType
}
else
{
#region AttributeGroup
if (schemaObject is XmlSchemaAttributeGroup)
{
XmlSchemaAttributeGroup schemaAttGroup = schemaObject as XmlSchemaAttributeGroup;
Console.WriteLine(String.Format("{0}XmlSchemaAttributeGroup: {1} ({2})", GetIndentationString(), schemaAttGroup, schemaAttGroup.Name));
}
#endregion AttributeGroup
#region Attribute
else if (schemaObject is XmlSchemaAttribute)
{
XmlSchemaAttribute schemaAtt = schemaObject as XmlSchemaAttribute;
Console.WriteLine(String.Format("{0}XmlSchemaAttribute: {1} ({2})", GetIndentationString(), schemaAtt, schemaAtt.Name));
}
#endregion Attribute
#region Group
else if (schemaObject is XmlSchemaGroup)
{
XmlSchemaGroup schemaGroup = schemaObject as XmlSchemaGroup;
Console.WriteLine(String.Format("{0}XmlSchemaGroup: {1} ({2})", GetIndentationString(), schemaGroup, schemaGroup.Name));
}
#endregion Group
else
{
Console.WriteLine(GetIndentationString() + schemaObject + " is not handled yet");
}
}
}

#region schema element
private void ManipulateSchemaElement(XmlSchemaElement schemaElement)
{
XmlSchemaType schemaElementSchemaType = schemaElement.SchemaType;

#region schema type defined
if (schemaElementSchemaType != null)
{
Type xmlSchemaTypeType = schemaElementSchemaType.GetType();
#region Complex type
if (schemaElementSchemaType is XmlSchemaComplexType)
{
ManipulateSchemaElementComplexType(schemaElement);
}
#endregion Complex type
#region Simple type
else if (schemaElementSchemaType is XmlSchemaSimpleType)
{
ManipulateSchemaElementSimpleType(schemaElement);
}
#endregion Simple type
}
#endregion schema type defined
#region Element with no schematype, thus simple
else
{
if (schemaElement.RefName.IsEmpty) // not a reference
{
Console.WriteLine(String.Format("{0}Implicitly implicit simple type: {1}", GetIndentationString(), schemaElement.Name));
}
}
#endregion
}
#endregion schema element

#region simple type
private void ManipulateSchemaElementSimpleType(XmlSchemaElement schemaElement)
{
XmlSchemaSimpleType simpleElement = schemaElement.SchemaType as XmlSchemaSimpleType;
Console.WriteLine(GetIndentationString() + String.Format("Simple type encountered: {0}", schemaElement.Name));
}
#endregion simple type

#region complex type
private void ManipulateSchemaElementComplexType(XmlSchemaElement schemaElement)
{
XmlSchemaComplexType complexElement = schemaElement.SchemaType as XmlSchemaComplexType;

XmlSchemaObjectCollection attColl = complexElement.Attributes;
foreach (XmlSchemaObject attCollObj in attColl)
ManipulateSchemaObject(attCollObj);

if (complexElement.Particle != null)
{
XmlSchemaParticle complexElementParicle = complexElement.Particle;
ParticleHandlingForElement(complexElementParicle);
}
else if (complexElement.ContentModel != null)
{
XmlSchemaContentModel complexElementContentModel = complexElement.ContentModel;
if (complexElementContentModel is XmlSchemaSimpleContent)
{
XmlSchemaSimpleContent contentModelSimpleType = complexElementContentModel as XmlSchemaSimpleContent;
XmlSchemaContent schemaContent = contentModelSimpleType.Content;
if (schemaContent is XmlSchemaSimpleContentExtension)
{
XmlSchemaSimpleContentExtension xmlSchemaExtension = schemaContent as XmlSchemaSimpleContentExtension;
XmlSchemaObjectCollection objColl = xmlSchemaExtension.Attributes;
foreach (XmlSchemaObject item in objColl)
{
ManipulateSchemaObject(item);
}
}
}
else
{
Console.WriteLine(GetIndentationString() + complexElementContentModel.ToString());
// Handle restrictions and extensions here (Complex or simple type)
}
}
}
#endregion complex type

#region particle handling for element
private void ParticleHandlingForElement(XmlSchemaParticle complexElementParicle)
{
Type particleType = complexElementParicle.GetType();
if (complexElementParicle is XmlSchemaSequence)
{
XmlSchemaSequence particleAsSeq = complexElementParicle as XmlSchemaSequence;
ManipulateSchemaSequence(particleAsSeq);
}
else if (particleType.Equals(typeof(XmlSchemaChoice)))
{
XmlSchemaChoice complexChoice = complexElementParicle as XmlSchemaChoice;
ManipulateSchemaChoice(complexChoice);
}
}

#region choice
private void ManipulateSchemaChoice(XmlSchemaChoice groupChoice)
{
XmlSchemaObjectCollection objColl = groupChoice.Items;
foreach (XmlSchemaObject objCollObj in objColl)
{
XmlSchemaObject schemaObj = objCollObj;
if (schemaObj.GetType().Equals(typeof(XmlSchemaSequence)))
{
XmlSchemaSequence choiceSeq = schemaObj as XmlSchemaSequence;
ManipulateSchemaSequence(choiceSeq);
}
else if (schemaObj.GetType().Equals(typeof(XmlSchemaChoice)))
{
XmlSchemaChoice choiceChoice = schemaObj as XmlSchemaChoice;
ManipulateSchemaChoice(choiceChoice);
}
else
ManipulateSchemaObject(schemaObj);
}
}
#endregion choice

#region Sequence
private void ManipulateSchemaSequence(XmlSchemaSequence choiceSeq)
{
XmlSchemaObjectCollection objColl = choiceSeq.Items;
foreach (XmlSchemaObject objCollObj in objColl)
ManipulateSchemaObject(objCollObj);
}
#endregion Sequence

There are quite a lot of methods in the above snippet, although they are quite straight forward and most of them recursively call our ExtractElements method.  Let us look at each of the methods which handle each different aspect of XML schema.

  1. ManipulateSchemaObject(XmlSchemaObject schemaObject)
    Handles various schema objects, this is the recursive method call which is centre of our XML schema reader.  This method acts like a proxy which spreads out the flow depending on the type of the XML schema element.
  2. ManipulateSchemaElement(XmlSchemaElement schemaElement)
    Handles various schema elements, Complex and Simple.
  3. ManipulateSchemaElementSimpleType(XmlSchemaElement schemaElement)
    Handles the simple type.
  4. ManipulateSchemaElementComplexType(XmlSchemaElement schemaElement)
    Handles the complex type.
  5. ParticleHandlingForElement(XmlSchemaParticle complexElementParicle)
    Handles particles, which includes sequences and choice elements.

We have looked at how to cleanly handle the XML schema and recursively traverse through its structure.  I’ll get back to this topic in a later post and show you how to I formed relationships between my custom schema based data structures using custom business objects.

Wednesday, April 8, 2009

Bhai Log Game 2 (07/04/09)

After a convincing win in Game 1 we could not have done any worse.  After a bismal fielding performance on field we continued along the downward spiral with our batting.  Some brilliant moments did occur but not enough to reward us the win.  The lost was brought upon by our own undoing.  Below are the graphs for the bowling, batting and all round performances of each team member.  Hoping to avenge our downfall in the next game.  You can also read more about the game here.

image

 image

image

Friday, April 3, 2009

StackOverflowException aka “The Uncatchable”

Ever wrote a great little application just to find that at some odd moment calling into a third party DLL throws a nice little StackOverflowException.  Then you go start digging into your code only to find that your call to this third party DLL is surrounded by not only one but two to three try/catch/finally blocks at different levels.  Hmm…weird.

The below code can quickly get this error for you and no matter if you surround it one or even 10 try/catch blocks, it will still throw the exception.

class Program
{
static void Main(string[] args)
{
try
{
Recurse();

Console.WriteLine("No exception thrown");
}
catch (StackOverflowException e)
{
Console.WriteLine("No exception caught");
}
}

private static void Recurse()
{
Recurse();
}
}

image


Does the catch block not catch StackOverflowException?

The answer is simple…only after you think about it for a little while.  No! The StackOverflowException cannot be caught by the .NET runtime because simply…according to Microsoft .NET it is bad coding practice.


Below is extracted from http://msdn.microsoft.com/en-us/library/system.stackoverflowexception.aspx.







Version Considerations

In prior versions of the .NET Framework, your application could catch a StackOverflowException object (for example, to recover from unbounded recursion). However, that practice is currently discouraged because significant additional code is required to reliably catch a stack overflow exception and continue program execution.


Starting with the .NET Framework version 2.0, a StackOverflowException object cannot be caught by a try-catch block and the corresponding process is terminated by default. Consequently, users are advised to write their code to detect and prevent a stack overflow. For example, if your application depends on recursion, use a counter or a state condition to terminate the recursive loop. Note that an application that hosts the common language runtime (CLR) can specify that the CLR unload the application domain where the stack overflow exception occurs and let the corresponding process continue. For more information, seeICLRPolicyManager Interface and Hosting the Common Language Runtime.



I found two great articles on how to handle the StackOverflow exception:



  1. http://stackoverflow.com/questions/206820/how-do-i-prevent-and-or-handle-a-stackoverflowexception-c

  2. AppDomains.pdf

Wednesday, April 1, 2009

Bhai Log Game 1 (31/03/09)

Our first graded game was played last night.  For a change everyone turned up with Shadow aka Rainbow aka Neil making his indoor debut.  Read more about it here.  Here are the stats graphs for the game.

image

image

image

image

Tuesday, March 31, 2009

INotifyPropertyChanged made easier using PostSharp

How many times have you written a C# business class like the one below only to realise that if you want the UI to react to changes you need to implement this cumbersome INotifyPropertyChanged interface.

So the above class will provide no change notification, you’ll have to implement the interface after which your code will look something like this (Note that this is .NET 2.0 syntax):

public class Customer : System.ComponentModel.INotifyPropertyChanged 
{
private String _Name;
private DateTime _DOB;

public String Name
{
get
{
return _Name;
}
set
{
_Name = value;
OnPropertyChanged("Name");
}
}

public DateTime DOB
{
get
{
return _DOB;
}
set
{
_DOB = value;
OnPropertyChanged("DOB");
}
}

#region INotifyPropertyChanged Members

public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}

#endregion
}

That class already looks extremely overloaded and ugly…now imagine if you are able to use .NET 3 or 3.5 and you want to utilise it’s extremely cool feature of auto-implemented properties (e.g. public int Age { get; set; }). Guess what…you can’t!!!! Because where would you put your calls to OnPropertyChanged(…)?

This is where PostSharp saves you.

The above class, with auto-implemented properties and the INotifyPropertyChanged interface.

public class Customer : System.ComponentModel.INotifyPropertyChanged 
{
[Notify]
public String Name { get; set; }

[Notify]
public DateTime DOB { get; set; }

#region INotifyPropertyChanged Members

public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}

#endregion
}

That’s cool…from ~50 lines of code to ~25 lines of code. You’ll be wondering, how will the PropertyChanged event fire now? The answer is within the [Notify] attribute. This attribute is a special attribute which inherits from the PostSharp class OnMethodBoundaryAspect. For this you’ll have to add references to the PostSharp DLLs.


image 



The implementation is shown below:

[Serializable]
[global::System.AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public sealed class NotifyAttribute : OnMethodBoundaryAspect
{
public override void OnExit(MethodExecutionEventArgs eventArgs)
{
if (eventArgs == null)
return;

//Why are property sets not properties? they are methods?
if ((eventArgs.Method.MemberType & System.Reflection.MemberTypes.Method) == System.Reflection.MemberTypes.Method && eventArgs.Method.Name.StartsWith("set_"))
{
Type theType = eventArgs.Method.ReflectedType;
string propertyName = eventArgs.Method.Name.Substring(4);

// get the field storing the delegate list that are stored by the event.
FieldInfo[] fields = theType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo field = null;
foreach (FieldInfo f in fields)
{
if (f.FieldType == typeof(PropertyChangedEventHandler))
{
field = f;
break;
}
}

if (field != null)
{
// get the value of the field
PropertyChangedEventHandler evHandler = field.GetValue(eventArgs.Instance)as PropertyChangedEventHandler;

// invoke the delegate if it's not null (aka empty)
if (evHandler != null)
{
evHandler.Invoke(eventArgs.Instance, new PropertyChangedEventArgs(propertyName));
}
}
}
}
}

This works perfectly, on every method “exit” the above OnExit(…) method will fire and invoke the PropertyChangedEventHandler for that particular property with it’s property name.


Neat isn’t it? Let us run some tests against it to make sure that it is actually happening. For this we revert to NUnit.


[TestFixture]
public class CustomerTest
{
private Customer _customer;
private String _propertyChanged;

[SetUp]
protected void Setup()
{
_customer = new Customer();
_customer.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(Customer_PropertyChanged);
}

[TearDown]
protected void TearDown()
{
_propertyChanged = String.Empty;
}

void Customer_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
_propertyChanged = e.PropertyName;
}

[Test]
public void TestNotifyName()
{
_customer.Name = "Ruskin";
Assert.AreEqual("Name", _propertyChanged);
}

[Test]
public void TestNotifyDOB()
{
_customer.DOB = DateTime.Now;
Assert.AreEqual("DOB", _propertyChanged);
}
}

image 
You can go one step further…ever had calculated properties in your business classes, for e.g. if you have a property for “Age” which gets calculated depending on the DOB, therefore, if DOB changes, Age changes. In our current implementation we can only mark properties to fire PropertyChanged events for themselves, but we can’t make them trigger events for other properties. This can be easily solved by adding another standard .NET attribute shown below:

[Serializable] 
[global::System.AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public sealed class NotifyChildrenAttribute : Attribute
{
public NotifyChildrenAttribute(params string[] dependents)
{
Dependents = dependents;
}

public NotifyChildrenAttribute() { }

public string[] Dependents { get; set; }
}

Now we add an “Age” property to our Customer class and let us look at the changed version: 

public class Customer : System.ComponentModel.INotifyPropertyChanged
{
[Notify]
public String Name { get; set; }

[Notify]
[NotifyChildren("Age")]
public DateTime DOB { get; set; }

public int Age { get { return DateTime.Now.Year - DOB.Year; } }

#region INotifyPropertyChanged Members

public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}

#endregion
}

As we can see, we have marked our DOB property to fire a PropertyChanged event not only for itself but also for the Age property. We still need to modify our implementation of the OnExit(…) method slightly to cater for this new attribute. Our implementation will look as follows:

[Serializable]
[global::System.AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public sealed class NotifyAttribute : OnMethodBoundaryAspect
{
public override void OnExit(MethodExecutionEventArgs eventArgs)
{
if (eventArgs == null)
return;

//Why are property sets not properties? they are methods?
if ((eventArgs.Method.MemberType & System.Reflection.MemberTypes.Method) == System.Reflection.MemberTypes.Method && eventArgs.Method.Name.StartsWith("set_"))
{
Type theType = eventArgs.Method.ReflectedType;
string propertyName = eventArgs.Method.Name.Substring(4);

List<string> allPropertiesThatNeedNotification = new List<string>();
if (!String.IsNullOrEmpty(propertyName))
{
PropertyInfo pInfo = theType.GetProperty(propertyName);
if (pInfo != null)
{
// get all the properties which may be affected
object[] notifyChildren = pInfo.GetCustomAttributes(typeof(NotifyChildrenAttribute), false);
foreach (var item in notifyChildren)
{
if (item is NotifyChildrenAttribute)
{
NotifyChildrenAttribute att = item as NotifyChildrenAttribute;
allPropertiesThatNeedNotification.AddRange(att.Dependents);
}
}
}
allPropertiesThatNeedNotification.Add(propertyName);
}

// get the field storing the delegate list that are stored by the event.
FieldInfo[] fields = theType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo field = null;
foreach (FieldInfo f in fields)
{
if (f.FieldType == typeof(PropertyChangedEventHandler))
{
field = f;
break;
}
}

if (field != null)
{
// get the value of the field
PropertyChangedEventHandler evHandler = field.GetValue(eventArgs.Instance) as PropertyChangedEventHandler;

// invoke the delegate if it's not null (aka empty)
if (evHandler != null)
{
foreach (var item in allPropertiesThatNeedNotification)
{
evHandler.Invoke(eventArgs.Instance, new PropertyChangedEventArgs(item));
}
}
}
}
}
}

Updating the tests…note that our previously defined tests won’t work now because for every singular property changed there may be a series of PropertyChanged events, we need some kind of collection which we need to test against. This testing is a little bit primitive but this is only for demonstration purposes.

[TestFixture]
public class CustomerTest
{
private Customer _customer;
private List<String> _propertiesChanged;

[SetUp]
protected void Setup()
{
_customer = new Customer();
_customer.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(Customer_PropertyChanged);
}

[TearDown]
protected void TearDown()
{
_propertiesChanged = new List<string>();
}

void Customer_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
Assert.IsTrue(_propertiesChanged.Contains(e.PropertyName), String.Format("Property {0} is not contained within the list", e.PropertyName));
}

[Test]
public void TestNotifyName()
{
_propertiesChanged.Add("Name");
_customer.Name = "Ruskin";
}

[Test]
public void TestNotifyDOB()
{
_propertiesChanged.Add("DOB");
_propertiesChanged.Add("Age");
_customer.DOB = DateTime.Now;
}
}

image 


Downloads: PostSharp

Monday, March 30, 2009

Common InstallShield functions and their Usage

Getting a property string

   1: NUMBER nvBufferLen;
   2: STRING svInstallDir;
   3: MsiGetProperty(hMSI, "INSTALLDIR", svInstallDir, nvBufferLen);

Where:
hMSI = handle of the MSI (gets automatically passed into functions)
INSTALLDIR = The name of the property to get
svInstallDir = The variable in which to store the returned property value
nvBufferLen = The length of the buffer, often set to 255.


Setting a property string 

   1: MsiSetProperty(hMSI, "INSTALLDIR", "C:\Program Files\InstallationDirectory");

Where:
hMSI = handle of the MSI (gets automatically passed into functions)
INSTALLDIR = The name of the property to set
Value = The value of the property to set to


Converting a number to string and vice versa

   1: number nTemp;
   2: string svTemp; 
   3: nTemp = 25;
   4:  
   5: NumToStr(svTemp, nTemp); // 25 -> "25" 
   6: StrToNum(nTemp, svTemp); // "25" -> 25

String comparison

   1: if (StrCompare("String A", "String B") == 0) then
   2:     // do something here
   3: endif;

String concatenation

   1: STRING svTemp;
   2: Sprintf(svTemp,  
   3:     "A string taking two parameters %s %s",
   4:     "Parameter 1", "Parameter 2");

String Trim(…) (Extracted from http://community.installshield.com/showthread.php?t=24423)

   1: function Trim(aString)   
   2:     BOOL done;    
   3:     NUMBER startIndex;   
   4:     NUMBER endIndex;    
   5:     NUMBER aByte;
   6: begin    
   7:     startIndex = 0;  
   8:     endIndex = StrLength( aString ) - 1;
   9:     done = ( startIndex > endIndex );
  10:     while ( !done )
  11:         GetByte( aByte, aString, startIndex );        
  12:         if ( aByte = ASCII_SPACE ) then
  13:             startIndex = startIndex + 1;
  14:             done = ( startIndex > endIndex );
  15:         else
  16:             done = TRUE;
  17:         endif;
  18:     endwhile;
  19:     done = ( startIndex > endIndex );
  20:  
  21:     while ( !done )
  22:         GetByte( aByte, aString, endIndex );
  23:         if ( aByte = ASCII_SPACE ) then
  24:             endIndex = endIndex - 1;
  25:             done = ( startIndex > endIndex );
  26:         else
  27:             done = TRUE;
  28:         endif;
  29:     endwhile;
  30:  
  31:     if ( startIndex > endIndex ) then
  32:         aString = "";
  33:     else
  34:         StrSub( aString, aString, startIndex, endIndex - startIndex + 1 );    
  35:     endif;
  36:     return 0;
  37: end;


Reading XML using MS XML DOM 6 (Inspired by http://community.installshield.com/showthread.php?t=101600&page=2)


   1: OBJECT oDoc, oNode, oNodeList;
   2: NUMBER nvBufferLen, nTemp;
   3: STRING svXPathQuery, svValue, svTemp, svMessage, svError, svLine, svLinePos;
   4:  
   5: set oDoc = CreateObject("Msxml2.DOMDocument.6.0");  
   6: if (IsObject(oDoc) = FALSE) then
   7:     MessageBox("MSXML DOM-Document Connection Failed", 0);
   8:     return -1;
   9: endif;
  10:       
  11: oDoc.async = FALSE;  
  12: oDoc.setProperty("SelectionLanguage", "XPath");
  13: oDoc.load( svInstallDir ^ UI_APP_CONFIG );
  14:  
  15: if ( oDoc.parseError.errorCode != 0 ) then
  16:     NumToStr ( svError , oDoc.parseError.errorCode );
  17:     NumToStr ( svLine , oDoc.parseError.line );
  18:     NumToStr ( svLinePos , oDoc.parseError.linepos );
  19:     MessageBox ( "The XML File did not load correctly.\n" + 
  20:                  "Error Code: " + svError + "\n" +
  21:                  "Error Line: " + svLine + "\n" + 
  22:                  "Error Line Position: " + svLinePos + "\n" +
  23:                  oDoc.parseError.reason , 0 );
  24: else 
  25:     svXPathQuery = "//XPathQuery";
  26:  
  27:     try 
  28:         set oNodeList = objDOMDOC.selectNodes( strXPathQuery );
  29:     catch
  30:         nTemp = Err.Number; 
  31:         NumToStr(svTemp, nTemp);
  32:         MessageBox("Error while selecting " + strXPathQuery + " (Error + " + svTemp + ")", 0);
  33:     endcatch;
  34:     
  35:     if (oNodeList.length == 0) then
  36:        MessageBox( "No " + strXPathQuery + " elements found.", 0 );
  37:     else 
  38:         for i = 0 to ( oNodeList.length - 1 );
  39:             set oNode = oNodeList.nextNode;
  40:             try
  41:                 svValue= oNode.text;
  42:                 //svValue = oNode.attributes.getNamedItem( strAttributeName ).value; // to get attribute value
  43:             catch
  44:                 MessageBox("No value found for " + strXPathQuery, 0);
  45:             endcatch;
  46:         endfor;
  47:     endif;
  48: endif;