Friday 18 November 2011

The Case of the Hung Visual Studio Installer

(with apologies to Mark Russinovich :-) )

I got my shiny new work laptop on Tuesday this week, a HP EliteBook 8560P. Mmm, shiny.

So a portion of the remainder of my week has been spent on installing things like SQL Server and other development tools on it. Until I got to Visual Studio 2010, for which the installer hung at the end of the first screen (the one where it installs Setup components before you get to choose which bits of VS you actually want to install).

A few details. I'm running Windows 7 Enterprise, and I was installing Visual Studio 2010 Ultimate. I was using Virtual CloneDrive to mount an ISO image of the Visual Studio DVD from a USB hard drive. All things I've done before without any issues.

Thought 1: My ISO image has got subtly corrupted somehow. I downloaded from MSDN a new ISO image of Visual Studio. No joy.

Thought 2: There's a problem with running the installer off a mounted ISO. Despite being sure I'd successfully run off a mounted ISO before, I burned a DVD and ran the installer from there. No joy.

At this point I tweeted that I was struggling:
I was grousing more than expecting anyone to offer advice, but I received these tweets back:
Fair enough, I'll give that a go:

Thought 3: It is working, I'm just not giving it long enough to work. I ran the installer for at least 8 hours overnight Wednesday. No joy.

At this point I was Googling pretty hard for issues relating to the Visual Studio hanging, but no one seemed to have had a problem at the same stage as me. It did lead to me trying

Thought 4: A missing Registry key
I added the key. No joy.
and
Thought 5: The installer was trying to write temporary files to the external USB drive. I copied the ISO onto the hard drive, disconnected the external USB drive and re-ran the installer. No joy.

Desperation was setting in at this point, and I began to wonder if the problem was my install order.

Thought 6: Because I've installed SQL Server (and SQL Express), something in SQL Server is interfering with the install. I uninstalled both instances of SQL Server 2008 and the shared components. No joy.

Here comes the science bit...
I'd already been running SysInternals' Process Explorer to try and see what was going on with the installer process but it didn't really show me enough detail. However I notioced from a discussion on the Microsoft forum someone else using Process Monitor, so I fired that up instead, and filtered it so I was just looking at setup.exe. This is what I saw:

My first thought here was: why is setup.exe trying to write files to live.sysinternals.com?, closely followed by: have I done this right or is Process Monitor somehow screwing up these results? And suddenly, in one of my occasional flashes of intuition, it clicked.

The Answer
I have a drive mapped to \\live.sysinternals.com\tools, and the drive that I have mapped is the I: drive. 'Cos, y'know, Internals begins with I. Turns out that the Visual Studio installer maps a folder in your %TEMP% folder to I: too and then uses it for decompressing some of the CAB files that the installer uses. Since I already had an I: drive, the installer was trying to use it but failing as I (obviously) don't have write privileges to the SysInternals folder, and the whole thing ended up in an infinite loop. I unmapped my I: drive from the SysInternals folder, re-ran the installer and everything installed successfully.
Now, clearly, the set of people who a) have an I: drive mapped but b) don't have write access to it and c) need to install Visual Studio, is going to be pretty small, but at the same time I can't help feeling the installer should have handled this better. In the end it was a simple workaround for me to resolve it, but it took me too long to discover the problem. Logged on Connect at https://connect.microsoft.com/VisualStudio/feedback/details/705657.

Tuesday 8 November 2011

World Clocks with jClock and TimeZoneInfo

Everyone else seems to be writing about the clocks changing this week, I don't see why I should miss out...

Last year while I was working on our group intranet project, I wrote a web part for diplaying times around the world corresponding to some of our major offices. It uses the jClock jQuery plugin so it does actually tick every second instead of being static (and in a future release I'm going to change this so it doesn't show the seconds and only ticks every minute instead). The list of locations is driven by an XML file so we can dynamically add more if we open offices in other time zones.










jClock by default shows you your local time, or you can give it an offset (in decimal) from GMT/UTC if you want to display the time from a different timezone. So the XML file that we went into production with last year was along the lines of;
<locations>
    <location>
        <name>London</name>
        <offset>0</offset>
    </location>
    <location>
        <name>Stockholm</name>
        <offset>1.0</offset>
    </location>
</locations>

The serverside code in the web part then uses a HtmlTextWriter to generate a set of DIVs and a block of JavaScript that sets up jClock.

<script type="text/javascript">
    $(document).ready(function ()
    {
        $('#jclock').jclock();
        $('#jclockLondon').jclock({ utc: true, utc_offset: 0 });
        $('#jclockStockholm').jclock({ utc: true, utc_offset: 1 });
    });
</script>

<h2>WORLD CLOCKS</h2>
<div style="text-align: center;">
    <div>
        <span>Local time:</span>
        <span id="jclock"></span>
    </div>
    <div style='background-color: #F0F0F0; width: 100%;'>
        <span>London</span>
        <span id='jclockLondon'></span>
    </div>
    <div>
        <span>Stockholm</span>
        <span id='jclockStockholm'></span>
    </div>
</div>
The  two generated strings are put into a Pair and then cached as one object to cut down some of the work for the server. And all this was fine, until the clocks changed in the spring. I worked out the new offsets and updated the XML file on the server, so then we had;
<locations>
    <location>
        <name>London</name>
        <offset>1.0</offset>
    </location>
    <location>
        <name>Stockholm</name>
        <offset>2.0</offset>
    </location>
</locations>

When the clocks changed again last week, I realised that this was going to be unsustainable and needed reworking. My first thought was to add a set of dates to the XML that denoted when to switch between winter and summer times. I coded this up, tested it, and checked it in, thinking I was done. Until it was pointed out to me that not everyone changes their clocks on the same date. 

With a steer from Gustaf, I looked into the System.TimeZoneInfo class. Now I wish I'd looked at this last year...

To get an instance of the TimeZoneInfo class, you call the static method FindSystemTimeZoneById and pass it the string Id of the timezone you want e.g. 'GMT Standard Time'. You can see all the timezones that your system supports by calling GetSystemTimeZones, which returns a ReadOnlyCollection of all the timezones Windows knows about. Each timezone has an id but also a DisplayName e.g. ' - it's the DisplayName that you see if you go into the Control Panel to change your system's timezone:










The id is constant across all installations of Windows, however the Displayname is localised - this was an important point for me as our intranet runs on servers in Sweden. For our World Clocks web part, the key function of TimeZoneInfo is GetUTCOffset. This takes a DateTime parameter which allows you to calculate the UTC offset for any given timezone on any given date. The key point is that this automatically factors in daylight savings times e.g. British Summer Time. I'd expected British Summer Time (or equally Central European Summer Time) to be listed as separate timezones, but they aren't, the timezone knows when it should apply changes for daylight savings e.g.

TimeZoneInfo info = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
Console.WriteLine(string.Format("Id: {0} - DisplayName: {1}",
info.Id,info.DisplayName));
Console.WriteLine(string.Format("Summer offset is {0}"
info.GetUtcOffset(new DateTime(2011,5,7))));
Console.WriteLine(string.Format("Winter offset is {0}"
info.GetUtcOffset(new DateTime(2011,11,7))));
Console.ReadLine();

produces

One of my colleagues asked what will happen if the rules for a given timezone change e.g. suppose the UK changes the dates on which the clocks change. The answer is this is now Microsoft's problem instead of mine, all the timezone information is held in the Registry and if any timezone changes, Microsoft will release a Windows Update that contains the new information.

So I've got rid of an annoying manual job in changing the XML twice a year, and the XML itself is much simpler because now for each location all it needs is a name, and a timezone id. Simples.