I have been busily coding WPF and Silverlight 2 for the last couple of months, and I will be for at least a few more weeks to come; however, during this development frenzy I ran into an issue with unit testing some new code. I also found an interesting solution to the problem, which required more than a simple query by way of , so I thought I’d share the problem and the solution here, just help the boys and girls at Google out a little.
The code under test relies on certain embedded resources. Said resources are to be consumed by a WPF bootstrapped assembly; therefore each resource is embedded in the assembly by using the build type of Resource, as opposed to Embedded Resource, which in turn means each resource has to be referenced by using a Pack URI. Therein lies the rub: whenever the unit test attempted to call the code that pulled out any of resources I would get the following UriFormatException:
System.UriFormatException was unhandled
Message="Invalid URI: A port was expected because of there is a colon (':') present but the port could not be parsed."
Source="System"
StackTrace:
at System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind)
at System.Uri..ctor(String uriString, UriKind uriKind) …
Bugger! You can easily reproduce this error by running the following code snippet in anything other than a WPF application:
var packUri = new Uri(
"pack://application:,,,/ReferencedAssembly;component/Subfolder/ResourceFile.xaml",
UriKind.Absolute);
As the test harness is not bootstrapped by a WPF application it knows absolutely nothing about Pack URIs; and in my case the System.Uri parsing code ended up trying to parse the Pack URI as if it were a Web URI, which is clearly not what I wanted and creates a situation where all roads lead to Exception City.
However, all is not lost, here is the simple solution I used for my tests:
if (!UriParser.IsKnownScheme("pack"))
{
UriParser.Register(new GenericUriParser
(GenericUriParserOptions.GenericAuthority), "pack", -1);
}
If you run this code before you try to parse any Pack URIs all will be well. It is worth mentioning that you need to only call this code once per process. This code registers pack as a valid scheme (in the form of pack://) and then puts all URI parsing into a generic mode, only parsing against registered schemes (which pack is now one), and that is it .. the job, as they say, is a good ‘un.
I’ve written a small test application that tests the URI parsing across threads to prove that the Register call is truly process wide, which you can download from here: PaulJ.PackUriParsing.zip
Above is a screenshot of the app .. the first call shows the parse failing, before the call to Register, then the second two calls show the exact same Pack URI being parsed successfully on two different threads (the main UI thread, and a newly created thread for the test).
I hope this helps to make for a better Google experience for someone; as usual if you have any comments, questions, flames, enhancements I would love to hear from you. In the meantime think deeply and enjoy.