This blog has moved, permanently, to http://software.safish.com.

Tuesday, August 31, 2010

Using the WiX Installer

I’ve been playing with the Wix Installer for the last few days, and I’m absolutely amazed by it.  It’s a real bastard to get into, but once you’re up and running it’s pretty incredible how much can be customised.  There is a LOT of documentation out there, but the most useful link I found was this tutorial – it really is worth your while to read the entire tutorial BEFORE trying to create your own installation project.

Instead of creating another tutorial, I thought I’d list the items that tripped me up.

Visual Studio 2010 Integration

I downloaded Wix 3.0 (64 bit), and after installation, it wasn’t available as a project within Visual Studio.  Download the beta version of 3.5 – it seems pretty stable and integrates with Visual Studio,.

No Wizard

No wizard!?  WTF!?  It turns out it’s XML-based – you end up doing a lot of the setup by hand.  Deal with it.

References in Visual Studio

You will probably require extensions so you can customise the UI, or perhaps ensure the user has a .NET Framework installed.  I’m working in Visual Studio 2010, and I wasn’t sure how to do this so I added compiler and linker options in the project properties.  You do not need to do this.  All you need to do is click “Add Reference” on the project, and select the extension from the list that appears.  For example, if you want to add the .NET Framework prerequisites, right-click References, and select WixNetFxExtension.  That is all you need to do.

Default UI

The default installation dialog really sucked, and it took me a while to work out that you need to specify a different UI type.  That’s what you get for not reading the above tutorial before creating your project.  You need to create a <UI> element, and specify the UIRef you want to use.

Dialog Sequence

Creating custom dialogs is pretty easy once you get the hang of it, but I struggled a little with navigation between dialogs.  Eventually I found the reference on the Wix site to a full list of Wix dialogs that you can navigate between at http://wix.sourceforge.net/manual-wix3/WixUI_dialogs.htm.

Using the IIS Extension (For deploying web sites)

This foxed me a little too.  Mostly, using the extensions just worked in Visual Studio, but the IIS extension doesn’t.  You need to add the namespace reference, something along the lines of:

<wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension" xmlns:netfx="http://schemas.microsoft.com/wix/NetFxExtension">

You can then use the extension elements as follows:

<iis:WebSite Id='DefaultWebSite' Description='Default Web Site'>
       <iis:WebAddress Id='AllUnassigned' Port='80' />
     </iis:WebSite>

Thursday, August 26, 2010

Unit Testing and DateTime Comparisons

 

One thing I’ve found annoying for the long time, is trying to assert DateTime equality when unit testing, particularly when the DateTimes have been parsed from strings in the underlying method.  Two DateTime constructs, despite being identical in terms of their values, often won’t assert as being equal and your unit test fails.  As such, you end up doing other sorts of tests, for example checking the individual values.

To help test dates, what I’ve done is created a helper class for asserting two dates are “equal”, to a specifed level of precision:

public static class AssertHelper
    {
        /// 
        /// Asserts that two dates are equal by checking the year, month, day, 
        /// hour, minute, second and millisecond components.
        /// 
        /// The current date
        /// 
        public static void AreDatesEqual(DateTime expected, DateTime actual, DateTimePrecision precision)
        {
            if (precision >= DateTimePrecision.Year && expected.Year != actual.Year)
            {
                throw new NUnit.Framework.AssertionException("Year in dates do not match as expected.");
            }
            if (precision >= DateTimePrecision.Month && expected.Month != actual.Month)
            {
                throw new NUnit.Framework.AssertionException("Month in dates do not match as expected.");
            }
            if (precision >= DateTimePrecision.Day && expected.Day != actual.Day)
            {
                throw new NUnit.Framework.AssertionException("Day in dates do not match as expected.");
            }
            if (precision >= DateTimePrecision.Hour && expected.Hour != actual.Hour)
            {
                throw new NUnit.Framework.AssertionException("Hour in dates do not match as expected.");
            }
            if (precision >= DateTimePrecision.Minute && expected.Minute != actual.Minute)
            {
                throw new NUnit.Framework.AssertionException("Minute in dates do not match as expected.");
            }
            if (precision >= DateTimePrecision.Second && expected.Second != actual.Second)
            {
                throw new NUnit.Framework.AssertionException("Second in dates do not match as expected.");
            }
            if (precision >= DateTimePrecision.Millisecond && expected.Millisecond != actual.Millisecond)
            {
                throw new NUnit.Framework.AssertionException("Millisecond in dates do not match as expected.");
            }


        }
    }

    public enum DateTimePrecision
    {
        Year = 0,
        Month = 1,
        Week = 2,
        Day = 3,
        Hour = 4,
        Minute = 5,
        Second = 6,
        Millisecond = 7
    }
This effectively allows me to unit test that the dates were close enough, without worrying to much about the exact equality. This solution, although working, somehow feels dirty. I'd love to know if anyone else has any better solutions for unit testing date equality in .NET.

Tuesday, August 3, 2010

ILMerge

I Used ILMerge today for the first time, and it's great. Aspnet_merge.exe is actually based on this tool, and it allows you to combine all your assemblies into a single exe or dll - great for simple deployment.

It gets installed by default to C:\Program Files (x86)\Microsoft\ILMerge, with fairly decent documentation. To consolidate a fairly simple Windows exe with three dlls into a single exe, all I did was the following:

ILMerge.exe /t:winexe /out:Output.exe /ndebug Program.exe Lib1.dll Lib2.dll Lib3.dll

Wednesday, July 28, 2010

Running MSBuild using Powershell Script

I wanted to build a Visual Studio solution using MSBuild using a Powershell script. Surprisingly, I couldn't find a reference on the web that actually worked for me. I thought I'd post it here for future reference, as it applies to the execution of any executable from within Powershell. It was "Invoke-Expression" that I hadn't used before.
 
$baseDir = "C:\Test\"
$outputFolder = $baseDir + "Output"
$msbuild = "C:\Windows\Microsoft.NET\Framework64\v3.5\MSBuild.exe"
$options = "/noconsolelogger /p:Configuration=Release"
$releaseFolder = $baseDir + "MyProject\bin\Release"

# if the output folder exists, delete it
if ([System.IO.Directory]::Exists($outputFolder))
{
 [System.IO.Directory]::Delete($outputFolder, 1)
}

# make sure our working directory is correct
cd $baseDir

# create the build command and invoke it 
# note that if you want to debug, remove the "/noconsolelogger" 
# from the $options string
$clean = $msbuild + " ""MySolution.sln"" " + $options + " /t:Clean"
$build = $msbuild + " ""MySolution.sln"" " + $options + " /t:Build"
Invoke-Expression $clean
Invoke-Expression $build

# move all the files that were built to the output folder
[System.IO.Directory]::Move($releaseFolder, $outputFolder)

Friday, May 14, 2010

Debugging log4net

I ran into an issue with log4net today where it just would NOT log to the database. I knew the database connection settings were fine, and the config file was fine, but some minor changes I had made to field sizes resulted in the whole thing not logging.
The problem is, log4net, for obvious reasons, doesn't raise exceptions when it fails, so short of downloading the source and debugging, I was at a bit of a loss. Luckily, those clever fellas have given a way to log internal messages! It's pretty straightforward:

Add the following in your App.config/Web.config, under the configuration section:
 
 <system.diagnostics>
    <trace autoflush="true">
      <listeners>
        <add
          name="textWriterTraceListener"
          type="System.Diagnostics.TextWriterTraceListener"
          initializeData="C:\temp\log4net.txt" />
      </listeners>
    </trace>
  </system.diagnostics> 
Then, add the following key to the appSettings section in your web.config:
  <add key="log4net.Internal.Debug" value="true"/>

Thursday, April 22, 2010

NCover and NUnit AppDomainUnloadedException

I lost a few chunks of hair this morning. I needed to set up some kind of code coverage so I could prove to a client that our unit tests were actually doing what we said they were doing. I chose NCover as I had used it a few years ago - unfortunately I had to opt for the old, free community edition but maybe one day we'll be able to upgrade to the commercial version.

Anyway, getting ncover wasn't a big deal, I added the following section to the ccnet.config file for our project:
  
<exec>
 <executable>C:\Program Files\NCover\NCover.Console.exe</executable>
 <baseDirectory>C:\Builds\myproject\Source</baseDirectory>
 <buildArgs>"C:\Program Files\NUnit 2.5.2\bin\net-2.0\nunit-console-x86.exe" MyProjectUnitTests.nunit /xml:..\Artifacts\nunit.xml //x ..\Artifacts\ncover.xml //a Assembly.1;Assembly.2;Assembly.3</buildArgs>
 <buildTimeoutSeconds>360</buildTimeoutSeconds>
</exec>

This worked first shot. The unit tests ran, and the code in the assemblies I'd defined was analysed and added to the CruiseControl report (make sure you include the output xml files in your publisher/merge section too). All fine and well, except now my builds were failing. Looking at the logs, the unit tests were ALL passing, everything was fine, except there was an unhandled AppDomain exception being raised by NUnit. Running this on my machine didn't produce the same result - it was only happening on the build server. The exact exception message was:

System.AppDomainUnloadedException: Attempted to access an unloaded AppDomain.


It turns out, this is strange bug in NUnit 2.5.2, and as of now, there is no fix. Fortunately, this doesn't happen with version 2.4.7 - so for this project all I did was point to a different version of NUnit, and the errors went away. The older version does seem siginificantly slower than version 2.5.2 so it's not optimal, but until a newer version comes out with a fix, it's good enough for me.

Tuesday, March 30, 2010

Online Favicon Creation

Need a favicon for your site? Look no further than http://www.favicon.cc/.

Nice!