Monday, 27 June 2005

Old blog - Application.ThreadException not firing in debug mode

Obligatory entry to avoid a whole month with no updates :-)

I've always used Application.ThreadException in windows forms apps to provide a last gasp exception handler (or in a lot of cases the only exception handler).

Simply do

AddHandler Application.ThreadException, AddressOf Me.HandleException

in the form load.

Then you provide a method such as

Public Sub HandleException(byval sender as object, byval args as System.Threading.ThreadExceptionEventArgs)

MessageBox.Show (args.Exception.ToString)

End Sub

The most annoying thing I always found about it was when debugging it never seemed to get invoked and VS.NET would show its unhandled exception dialog.

Turns out I've actually been doing it wrong for years. Instead of sticking it in form load put it in the constructor BEFORE the call to InitializeComponent. If you do that then HandleException gets invoked even when debugging.


Friday, 29 April 2005

Old blog - Ever needed to install more than one instance of the same .NET service on the same machine?

What I needed was a way to install more than one instance of my service on a single machine (to support different test environments using a single box). Using the .NET project installer approach there's a pretty simple way to do this. At runtime the Context property of the project installer contains an assemblyName parameter that points to the service exe being installed. From this you can work out the name of the config file, load it up and pull out information such as the service name. You can then simply set the ServiceName property of your service installer and away you go. Simply running installutil against an instance of your service (with its own config file) means you can install more than one instance.

To support more than one service instance your service should set its ServiceName property to match the name you've set during installation. Whilst failing to do this doesn't make your machine melt it can cause issues e.g. an event source is created at install time based on the service name. If your service has a different name then at run time it will fall over if it doesn't have permission to create a new event source when it tries to log that it's started.

Below is the minimum amount of code you need in your project installer class (assumes you've added one using the Add Installer option on the properties window)


Public Overrides Sub Install(ByVal stateSaver As System.Collections.IDictionary)

Dim document As New Xml.XmlDocument
document.Load(Me.Context.Parameters("assemblyPath") & ".config")

Dim node As Xml.XmlNode = document.SelectSingleNode("/configuration/appSettings/add[@key='ServiceName']/@value")

Me.ServiceInstaller1.ServiceName = node.Value
Me.ServiceInstaller1.DisplayName = node.Value

stateSaver.Add("ServiceName", node.Value)

MyBase.Install(stateSaver)

End Sub

Public Overrides Sub Uninstall(ByVal savedState As System.Collections.IDictionary)
Me.ServiceInstaller1.ServiceName = savedState("ServiceName")
MyBase.Uninstall(savedState)

End Sub

Wanting to make things a little bit more user friendly I created a application that would wrap up the whole process of creating a new directory, copying over the service exe and required local files, add in the config file and then call install the service.

A little digging through the installutil code using Anakrino revealed that all the work is done by the System.Configuration.Install.ManagedInstallerClass class. You simply call the InstallHelper method, passing an array containing the same command line arguments you would pass to installutil.

Ignoring the "Do not use from your own code" MSDN warning I plugged this into my application and merrily started creating service instances. Unfortunately it didn't work all of the time. It turns out that this ManagedInstallerClass remembers the exe you first installed and then only ever works with this one until you restart your application. This doesn't bother installutil because it quits once it has done one installation.

It turns out though that it's pretty easy to create your own replacement for the ManagedInstallerClass.

Installing a service

To install a service you can create an instance of ServiceInstaller, set a few properties and call the Install method.

Public Sub Install(ByVal ServicePath As String, ByVal ServiceName As String)

Dim context As New System.Configuration.Install.InstallContext("c:\install.log", Nothing)
context.Parameters.Add("assemblyPath", ServicePath)

Dim serviceProcessInstaller As New System.ServiceProcess.ServiceProcessInstaller
serviceProcessInstaller.Account = ServiceProcess.ServiceAccount.LocalSystem

Dim serviceInstaller As New System.ServiceProcess.ServiceInstaller

With serviceInstaller
.Context = context
.ServiceName = ServiceName
.DisplayName = ServiceName
.Parent = serviceProcessInstaller
End With

serviceInstaller.Install(New Hashtable)
End Sub

Removing the service is even easier, all you need is the service name.


Public Sub Uninstall(ByVal ServiceName As String)

Dim context As New System.Configuration.Install.InstallContext("c:\uninstall.log", Nothing)

Dim serviceInstaller As New System.ServiceProcess.ServiceInstaller

With serviceInstaller
.Context = context
.ServiceName = ServiceName
End With

serviceInstaller.Uninstall(Nothing)

End Sub

Installing performance counters

If your service uses performance counters you can take a similar approach except this time you'd use an instance of System.Diagnostics.PerformanceCounterInstaller. Set its Context property, PerformanceCounterCategoryName and then add the counters you wish to install to its Counters collection. Then call Install.

Capturing installation output

As well as writing to the log file you supply with the Context object the installers write information to the Console.

To capture this output assign your own TextWriter to the standard output stream using Console.setOut. I created a TextWriter that wrote everything to a text box.

Wednesday, 27 April 2005

Old blog - 27 Apr 2005

Why is it that my lovely Logitech wireless keyboard (with 17, yes, that's right - 17 extra silver buttons along the top) doesn't have a caps lock light? Or a num lock light. Or a scroll lock one (though who cares about that one).

Sunday, 17 April 2005

Old blog - When is an XmlValidatingReader not an XmlValidatingReader?

It's a pretty common thing to want to do - someone gives you an XML document and you want to check it matches your schema. In the .NET world you'd use the XmlValidatingReader and some code like this:

Private Sub validate()

Dim doc As New Xml.XmlDocument

Dim reader As New Xml.XmlTextReader("c:\input.xml")

Dim validatingReader As New Xml.XmlValidatingReader(reader)

validatingReader.Schemas.Add("my namespace", "c:\myschema.xsd")

AddHandler validatingReader.ValidationEventHandler, AddressOf Me.ValidationEventHandler

doc.Load(validatingReader)


End Sub


Private Sub ValidationEventHandler(ByVal sender As Object, ByVal Arguments As Xml.Schema.ValidationEventArgs)

'do something with the errors

End Sub

Unfortunately if someone has sent you a document that's part of a completely different namespace to your schema the XmlValidatingReader simply loads merrily away and doesn't throw any errors at all.

The validation engine says to itself "hmm, can't find a schema for namespace A, therefore I can't do any validation'. Once you think about this it makes perfect sense - you've given it the schemas to use, it's not its fault if the schema it needs isn't in there.

A very simple work around is just to do something like the following:

If doc.DocumentElement.NamespaceURI <> "my target namespace" Then
Throw New ApplicationException("Target namespace of the document was crap")

End If

This very simple gotcha passed me by for a long time till one of our apps started to fall over because the XML didn't look like what it was expecting even though it had validated it successfully.

Thursday, 31 March 2005

Old blog - 31 March 2005!

For a fair while now I've been contemplating starting a blog. Part of the blogging process obviously involves having something to blog about. One idle thought was to pack it with weird and wonderful links - such as the guy who shows people his cock











Since that's my only slightly funny image and the world of weird and wonderful blogging is already adequately covered by a A Welsh View I dumped that idea.

Another approach was to pack a blog with all my opinions on things tech. I started writing down why I haven't made the switch to Firefox in what I hoped was a witty and informative manner (mainly because I'm too lazy and tabbed browsing sucks) before realising that:

  1. Most people were unlikely to give a damn about what I thought of Firefox and
  2. Anyone who is sad enough to start crying that I don't like tabbed browsing should really get out more and I don't really want to hear off them.

A third avenue was to blog about coding and what a super talented code monkey I was. I quickly realised that I didn't work for Microsoft and was therefore unable to come out with interesting windows titbits.

So with all that in mind, welcome to my non-blog. It's called that because basically it's just a static web page that I might up date from time to time.