Friday 29 August 2008

NUnit missing assembly reference error

Recently I have been getting a few errors running Nunit on our automatic build server. Nunit is being called through an MSBuild file, which is being called by CruiseControl.NET. The CruiseControl build log has been recording the error messages below:
EventLogListenerTest.cs (6,7): errorCS0246: The type or namespace name 'NUnit' could not be found (are you missing a using directive or an assembly reference?)
EventLogListenerTest.cs (18,10): errorCS0246: The type or namespace name 'TestFixtureSetUp' could not be found (are you missing a using directive or an assembly reference?)
EventLogListenerTest.cs (18,10): errorCS0246: The type or namespace name 'TestFixtureSetUpAttribute' could not be found (are you missing a using directive or an assembly reference?)
EventLogListenerTest.cs (25,10): errorCS0246: The type or namespace name 'TestFixtureTearDown' could not be found (are you missing a using directive or an assembly reference?)
EventLogListenerTest.cs (25,10): errorCS0246: The type or namespace name 'TestFixtureTearDownAttribute' could not be found (are you missing a using directive or an assembly reference?)
These errors are only happened when I ran CruiseControl as a service... so I logically assumed that this must be a security issue with the Local service account not being able to see the Nunit assemblies. However, the issue was not resolved by running the service as the logged in user.

The solution I found was to manually add the Nunit.Framework.dll from the Nunit directory in "c:\program files" to the Global Assembly Cache (GAC). This resolved all the missing assembly reference" errors.

Wednesday 20 August 2008

XML Namespaces and LINQ to XML

Recently I've been writing a program to automatically generate an MSBuild file containing all of our projects. This is then used by CruiseControl.NET to continuously build and test our source code.

I decided to use the new LINQ to XML to write the MSBuild file, but came across a problem when I ran the resulting file through MSBuild. The error message I received was:
The element beneath element may not have a custom XML namespace.

Investigation revealed that the root node of the MSBuild XML has a XML namespace (xmlns="http://schemas.microsoft.com/developer/msbuild/2003") and when I added a new XElement to the root element it automatically added a blank namespace attribute to the XElement(xmlns=""). Thus causing the error message.

The solution is to always add a XNamespace object of the SAME address to the XElement you are adding (see below).
XNamespace _xlmns = "http://schemas.microsoft.com/developer/msbuild/2003";

XElement root = new XElement(_xlmns + "Project",
new XAttribute("DefaultTargets", "Build");

XElement newBuildTarget = new XElement(_xlmns + "MSBuild",
new XAttribute("Projects", notShownSolutionPath),
new XAttribute("Targets", notShownTargets));
root.Add(XElement);

Bizarrely enough this will actually then remove the xmlns attribute from the added element. But thankfully it solves the problem.

Thursday 14 August 2008

Windows Management Instrumentation (WMI) queries

Often when coding you need to access the configuration of the server that your software is running on. This might be to determine if a particular device is connected or just to provide remote support information about the machine that the problem occured on.

Microsoft have built a feature called WMI into the Windows Driver Model. This provides an interface which you can query (using WMI queries) to access information about the system and it's components.

.NET provides a namespace (System.Management) that contains classes to query and access the results returned. The example below shows a simple example of using a query in C# to access Drive information on a specific machine.
using System.Management;

public static void DisplayDrives()
{
ManagementScope scope = new ManagementScope("\\\aMachine\\root\\cimv2");
ObjectQuery query = new ObjectQuery(
"SELECT Name, Size FROM Win32_LogicalDisk where DriveType = 3"
);
//N.B. 3 = local fixed drives ONLY

ManagementObjectCollection
drives = query.Get();
foreach(ManagementObject drive in drives)
{
Console.WriteLine(string.Format("Drive:{0} Size={1}",
drive["Name"].ToString(), drive["Size"].ToString()));

}
}
As you can see it's quite straightforward to use. However, one of the problems I have come across is how you know that the Win32_LogicalDisk is the object that contains information about the drives?

I use this page, which I found hidden away in the MSDN (I won't start ranting about how difficult it is to find anything in MSDN). It contains a break down of the different types of objects that are available for querying. You can even view the properties they contain.

I've just come across this application that Ben Coleman has created for running WMI queries on local or remote machines. It's quite useful for testing your WMI queries before adding them into your code.

Wednesday 13 August 2008

Free Project Management Tools

I have spent the last few days evaluating free project management tools, as we have decided that we'd like to plan out our latest development in a bit more detail. I've never really been much of a fan of Microsoft Project (a bit to overly complicated), so I have been looking for something that is easy to use and web based.

The solution that I have decided to use is Mingle by Thoughtworks. This is free if you have 5 users, or if you are lucky enough to work for a non-profit or charity. The only requirements are a web server with a mySQL 5.00 database.

Mingle is based around a wiki based system and uses items it calls "Cards" to store information about the project. For example, you can create a card for each of your Use Cases and then add a card property of Requirement status which would allow you to move these cards through pre-defined project states (such as Analysis, Design, Implementation and Completed). You can even create card trees, which in this example would allow you to break the Requirement into software engineering tasks (such as Code, Unit Test etc).

Cards can also be used to keep track on defects and other project items. Mingle is highly configurable and can be adapted to a variety of project management uses. Thoroughly recommended.

Wednesday 6 August 2008

Custom Event Log Trace Listener

We all love .NET tracing, who wouldn't? It's so easy to use (see here if you've never come across tracing before).

However, recently I've come across a mild annoyance. The standard .NET event log listener logs everything as info... great!

Luckily the .NET framework provides the ability to write your own custom trace listeners, so I decided to write my own. I have included the code for this trace listener below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace aCompany.Diagnostics
{
public class EventLogListener: TraceListener
{
#region Field(s)
private EventLog _eventLog;
#endregion

#region Constructor(s)
public EventLogListener(string sourceName)
{
_eventLog = new EventLog();
_eventLog.Source = sourceName;
}
#endregion

#region Public Method(s)
public override void Write(string message)
{
_eventLog.WriteEntry(message, EventLogEntryType.Information);
}

public override void WriteLine(string message)
{
this.Write(message + Environment.NewLine);
}

public override void Fail(string message)
{
_eventLog.WriteEntry(message, EventLogEntryType.Error);
}

public override void Fail(string message, string detailMessage)
{
_eventLog.WriteEntry(message + Environment.NewLine + detailMessage, EventLogEntryType.Error);
}

public override void WriteLine(string message, string category)
{
EventLogEntryType entryType = (EventLogEntryType)Enum.Parse(typeof(EventLogEntryType), category);
_eventLog.WriteEntry(message, entryType);
}
#endregion
}
}

This listener assumes that an info event should be created if a Trace.Write() or Trace.WriteLine() is called. An error event is created if Trace.Fail() is called.

There is also a WriteLine() that takes a category as an argument. I have used this to represent a string of EventLogEntryType, so this can be used to created any type of event.

The event source that the events will be added to is passed in as an argument to the constructor.

Hope you find this useful.