Written by WATYF on Friday, 23 March 2007 (18516 hits)
Category: .NET Programming
OK. Here's the problem... I want my .NET application (in this case, TaskRunner) to open a new instance of the user's default browser and direct it to a URL. Sounds simple, right? Well of course not.... this is Microsoft we're talking about.
Now, this is the part where you offer your pointless suggestions and ask irrelevant questions and tell me how I'm being an idiot for ignoring the oh-so-obvious solution to this problem... ("Hey stupid... just use Process.Start and pass it the URL!!"). But notice that I said that I want to open a NEW instance of the user's browser. Unfortunately, if the user has Internet Explorer as their default browser, Process.Start (with a URL) will very often... not always, but sometimes (for reasons only known to Bill Gates himself) reuse an existing browser and direct it to the URL. So let's say you're sitting there replying to an email.... you've been up all night typing out some sappy love note to your girlfriend to try to get her to change her mind about dumping you... and you open TaskRunner's "WebSearch" app so you can search for snippets of crappy poety to include in your letter. Well, if IE is your default browser, then the WebSearch may very well commandeer the browser that you were using to compose that masterpiece of an email and direct you away from the work that you've spent hours pouring your heart into, thus erasing all that effort (and waste of emotion ).
Why, you ask? Because Process.Start(URL) does NOT always open a new instance of IE... but fortunately, I figured out how.... so you don't have to go through all the pain and emotional anguish of losing that email.
Now... here's where you probably have more bright ideas to challenge my methodology. "Hey dummy... IE has a setting in Advanced Internet Options called 'Reuse windows for launching shortcuts'... just uncheck that, and you'll be fine". To which I'd say... sorry chuckles... but that option is already unchecked (by default, in fact) and yet my problem still occurs. But even if unchecking that option did solve the problem, I'm not gonna ask my users to go into their IE options and change some settings just to get my program to work properly. That's lame.
So... here's where we're at. If I use Process.Start with a URL, it will open the default browser, BUT if the user has IE as the default, sometimes it will reuse an existing window (which is very annoying... in fact, I don't even use my own WebSearch tool because I hadn't fixed this issue yet). If I use Process.Start with a file path AND a URL, it will always open a new window, but I'd have to know where their default browser is... I can't just pass it "iexplorer.exe" and the URL. What if the user doesn't use IE? It'd be pretty silly for my app not to open web pages in their default browser. What we really need is to determine the default browser's file path and then user Process.Start with the path of their default browser and the URL. So... how do we do this.
Well, I stumbled onto something close here. Unfortunately, this solution involves using an API call (something which I am currently trying to wean TaskRunner off of). It also involves creating and cleaning up a temporary file on the user's drive, which I thought was a little much for what I was trying to do. The solution did work... but it was not optimal. Fortunately, someone later in the thread suggested an alternative... checking the registry (which is what TaskRunner does to determine your default Email App for the Email Monitor). Obviously, that thought had already crossed my mind, but I couldn't find a reliable registry key to get the info from. The .htm and .html keys in HKEY_CLASSES_ROOT were no help, and the key that the person in that thread suggested was, in fact, not a reliable place to get it... HKEY_CLASSES_ROOT\htmlfile\shell\open\command is usually not accurate.... at least... not on either of my machines at work. I did some testing and no matter what I set my default browser to, htmlfile always shows IE's path as the default. But I figured that the path had to exist somewhere... I mean... if FindExecutable (the API suggested above) could return the path properly, then it must be somewhere.... and that's when I found it... HKEY_CLASSES_ROOT\http\shell\open\command. Seems pretty obvious, but honestly, I wasn't looking very hard for it before today.
So... now that we know where to find the path of the default browser, here's the code we need to open any URL, in a new window.
Sub OpenBrowser(ByVal sURL As String)
Dim sBrws As String
'Return the path used to access http from the registry
sBrws = LCase(My.Computer.Registry.ClassesRoot.OpenSubKey("http\shell\open\command", False).GetValue(Nothing))
'Trim the file path (removing everything after the ".exe") and get rid of quotation marks
sBrws = Replace(Left(sBrws, InStrRev(sBrws, ".exe") + 3), """", "")
'Open the URL using the path
'Add redundant attempts to open the URL, in case the code fails
'First just try using Process.Start, which will open the default browser, but might reuse IE's window
'If Process.Start fails (due to known bug), try launching IE directly and passing it the URL
Dim psi As New ProcessStartInfo("IExplore.exe", sURL)
psi = Nothing
Catch ex As Exception
MsgBox("Could not launch the browser. Details: " & ex.Message)
A couple things to note. I used the .NET 2.0 method of checking the registry (but I'm assuming that you know how to hit the registry if you're using .NET 1.1). Also, I added some redundancy in the Catch statement. First off, if for some crazy reason, the registry hit fails, you can just fall back on the "Process.Start(URL)" method (with all it's wonderful flaws). And there's a known bug with Process.Start(URL) (the details of which escape me at the moment, but I know there is one, because I added a catch for it in all my Process.Start(URL) instances), so if for some reason both the Process.Start(defaultpath, URL) and Process.Start(URL) attempts fail, it will try just opening the URL using IE... and finally, if that fails, it informs the user.