I thought I was going crazy.. I’d built an installer using WiX and set the product version to 184.108.40.206. The plan was that installing a new version would automatically uninstall any old versions.
I then updated the version to 220.127.116.11 and installed the new version, only to end up with two installations.
Turns out that Windows Installer only looks at the first 3 digits of the ProductVersion (as the first Note mentions in the Windows Installer documentation on Major Upgrades) – so twiddling that 4th digit is not going to have any effect.
Here’s a sample .wxs file that works for me:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<?define version = "18.104.22.168" ?>
<Product Id="*" Name="WixUpgrading $(var.version)" Language="1033" Version="$(var.version)" Manufacturer="WixUpgrading"
<Package InstallerVersion="200" Compressed="yes" />
<Media Id="1" Cabinet="WixUpgrading.cab" EmbedCab="yes" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLLOCATION" Name="WixUpgrading">
<Component Id="ProductComponent" Guid="c768bc29-e2d6-4f1c-b711-2dcf91641fab">
<File Id="TextFile1.txt" Name="TextFile1.txt" KeyPath="yes" Source="TextFile1.txt" />
<Feature Id="ProductFeature" Title="WixUpgrading" Level="1">
<ComponentRef Id="ProductComponent" />
<Property Id="PREVIOUSVERSIONSINSTALLED" Secure="yes" />
<Property Id="ALLUSERS" Value="1" />
<UpgradeVersion Property='PREVIOUSVERSIONSINSTALLED' OnlyDetect="no" IncludeMinimum='yes' Minimum='0.0.0' IncludeMaximum='no' Maximum='$(var.version)' />
<UpgradeVersion Minimum="$(var.version)" IncludeMinimum="no" OnlyDetect="yes" Language="1033" Property="NEWERPRODUCTFOUND" />
<Custom Action="PreventDowngrading" After="FindRelatedProducts">NEWERPRODUCTFOUND AND NOT Installed</Custom>
<RemoveExistingProducts Before='InstallInitialize' />
<CustomAction Id="PreventDowngrading" Error="Newer version already installed." />
<CustomAction Id="UIandAdvertised" Error="Something about the UI."/>
<Custom Action="UIandAdvertised" Sequence="3">ProductState=1</Custom>
Timothy Walters, Ben Laan and I will be the speakers at tomorrow’s Adelaide SQL User Group meeting.
We’ll be sharing some of our experiences using SQL Server 2008, including:
- Change Tracking
- Integrating with legacy SQL 2000 databases that have no referential integrity worth speaking of
Register if you’d like to come along – hope to see you there!
I finally decided on a new TV for the home theatre, after my attempts at getting the 1970’s era CRT to work failed.
I chose a Kogan 1080P-32 – a Full-HD LCD TV. Importantly, it supports HDMI input and as the name suggests can display full high definition – 1080 Progressive. Kogan are an online store, so it did mean that I had to order something sight-unseen – a bit scary when you’re spending a reasonable amount of money. Happily, the TV was couriered quickly and safely and I haven’t had any problems with stuck pixels.
I then had to connect the screen to the home theatre box. Some retails sell HDMI cables for more than $100, but instead I bought a 5m cable from MSY for $10.
It seems to work quite well with Media Center. The only issue I’ve had is that when you close Media Center and do other stuff in Vista, the display is a bit blurry. I’m not sure if that’s a problem with the graphics card or something else. Not a huge problem but it would be nice to sort it out.
I then hired a Blu-Ray DVD to try out. This also worked ok, once I realised I hadn’t installed all the PowerDVD bits. For some reason, PowerDVD doesn’t integrate with Media Center, so you either have to manually swap over to PowerDVD, or install another player like ArcSoft's TotalMedia Theatre. This is a bit better, though it still isn’t completely integrated as the stop/play buttons on the remote control didn’t work properly.
The net result is that everything is working well. The monitor that we were using for the TV is now back on my home computer, and we’re now looking around furniture shops for a nice home entertainment unit to put it all in.
One sad note – A couple of weeks ago we were watching TV when the machine started locking up (always in the most crucial part of the TV program!). I eventually discovered that my 1Tb WD disk had developed a bad case of badsectoritis. It was just over 1/2 full and had been fine up until now, so I can only guess that maybe there were a bunch of bad sectors in the second half of the disk that we started to hit, or some kind of electronic malfunction. In any case it will need to be sent back under warranty to get sorted out. In the meantime we can survive on the other disk.
For those who like to read in a more linear fashion, the PDF of the June 2008 release of Composite Application Guidance for WPF is now available.
The application we are developing will be installed at different locations and consequently will need to connect to a local SQL Server instance.
Rather than build separate configurations and installers for every site, we’re planning to store this information centrally. The application then uses a web service to retrieve the appropriate configuration information for its site.
As we are using NHibernate/ActiveRecord with the database configuration being loaded though the Castle ActiveRecord Integration Facility, the database name is indicated through naming a connection string (by specifying the config key ‘hibernate.connection.connection_string_name’). The difficulty is that this appears to be loaded the first time Windsor loads the config file. What we need is a way of replacing the connection string that was read from the app.config file with one that we’ve retrieved from the web service.
The main problem that needed to be overcome is that .NET doesn’t allow you to change the connection strings after they are read from the app.config file. If you try to do it, it will throw a ConfigurationErrorsException with the message “The configuration is read only”. The following code illustrates this:
var settings = ConfigurationManager.ConnectionStrings;
settings.ConnectionString = "blah";
Steve Michelotti describes how you can override ConnectionStringsSection’s IsReadOnly method, which would be fine for custom configuration sections but doesn’t work in this case as the class is sealed.
Dmitry suggests another approach, though at the time it was just an untested idea. ConnectionStringSection inherits from the abstract class ConfigurationElement. Using Reflector you can see that the default implementation of IsReadOnly just returns the value of a private field _bReadOnly, which is set to true.
We use reflection to locate the private field _bReadOnly and then force it to be false. eg.
var settings = ConfigurationManager.ConnectionStrings[ 0 ];
var fi = typeof( ConfigurationElement ).GetField( "_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic );
settings.ConnectionString = "Data Source=Something";
This is a bit of a hack and obviously would fail should Microsoft choose to change the name of this private field in the future, but as long as we do this before the container loads the configuration, it means that in the example above, any reference to the first connection string will return our new value.
An alternate approach might be to implement a custom NHibernate configuration provider, but I’ll leave that exercise to the reader :-)