With my altogether too short break out of the way, its time for another post about something software related.
This weeks topic? Embedding websites into windows desktop applications for fun and profit.
Not exactly the sexiest of topics, but over the years I’ve run into a few situations where the most effective solution to a problem involved embedding a website into another application. Usually an external party has some functionality that they want your users to access and they’ve put a bunch of effort into building a website to do just that. If you’re unlucky, they haven’t really though ahead and the website is the only option for integration (what’s an API?), but all is not lost.
Real value can still be delivered to the users who want access to this amazing thing you have no control over.
So Many Options!
I’ve been pretty clear in the posts on this blog that one of things my team is responsible for is a legacy VB6 desktop application. Now, its still being actively maintained and extended, so its not dead, but we try not to write VB6 when we can avoid it. Pretty much any new functionality we implement is written in C#, and if we need to present something visual to the user we default to WPF.
Hence, I’m going to narrow the scope of this post down to those technologies, with some extra information from a specific situation we ran into recently.
Right at the start of the whole “hey, lets jam a website up in here” though process, the first thing you need to do is decide whether or not you can “integrate” by just shelling out to the website using the current system default browser.
If you can, for the love of all that is good and holy, do it. You will save yourself a whole bunch of pain.
Of course, if you need to provide a deeper integration than that, then you’re going to have to delve into the wonderful world of WPF web browser controls.
- The built-in WebBrowser
- Awesomium (which appears to have disappeared?, also, needs monies)
There are definitely other offerings, but I don’t know what they are. I can extrapolate on the first two (because I’ve used them both in anger), but I can’t really talk about the third one. I only included it because I’ve heard about it specifically.
CEFSharp is a .NET (both WPF and WinForms) wrapper around the Chromium Embedded Framework, and to be honest, its pretty great.
The CEFSharp library is usually my first port of call when it comes to embedding a website in a desktop application.
Unfortunately, we we tried to leverage CEFSharp.WPF into our VB6/C# franken-application we ran into some seriously weird issues.
Our legacy application is at its core VB6. All of the .NET code is triggered from the VB6 via a COM interop, which essentially amounts to a message bus with handlers on the .NET side. VB6 raises event, .NET handles it. Due to the magic of COM, this means that you can pretty much do all the .NET things, including using the various UI frameworks like WinForms and WPF. There is some weirdness with windows and who owns them, but all in all it works pretty well.
To get to the point, we put a CEFSharp.WPF browser into a WPF screen, triggered it from VB6 and from that point forward the application would crash randomly with Access Violations any time after the screen was closed.
We tried the obvious things, like controlling the lifetime of the browser control ourselves (and disposing of it whenever the window closed), but in the end we didn’t get to the bottom of the problem and gave up on CEFSharp. Disappointing but not super surprising, given that that sort of stuff is damn near impossible to diagnose, especially when you’re working in a system built by stitching together a bunch of technological corpses.
Then there is the built-in WPF WebBrowser control, which accomplishes basically the same thing.
Why not go with this one first? Surely built in components are going to be superior and better supported compared to third party components?
Well, for one, its somewhat dependent on Internet Explorer, which can lead to all sorts of weirdness.
A good example said weirdness if the following issue we encountered:
- You try to load a HTTPS website using TL 1.2 through the WebBrowser control
- It doesn’t work, giving a “page cannot be loaded error” but doesn’t tell you why
- You load the page in Chrome and it works fine
- You load the page in Internet Explorer and it tells you TLS 1.2 is not enabled
- You go into the Internet Explorer settings and enable support for TLS 1.2
- Internet Explorer works
- Your application also magically works
The second pertinent piece of weirdness relates specifically to the controls participation in the WPF layout and rendering engine.
The WebBrowser control does not follow the same rendering logic as a normal WPF control, likely because its just a wrapper around something else. It works very similar to a WinForms control hosted in WPF, which is a nice way of saying it works pretty terribly.
For example, it renders on top of everything regardless of how you think you organised it, which can lead to all sorts of strange visual artefacts if you tend to use the Z-axis to create layered interfaces.
With CEFSharp causing mysterious Access Violations that we could not diagnose, the default WPF WebBrowser was our only choice. We just had to be careful with when and how we rendered it.
Luckily, the website we needed to use was relatively small and simple (it was a way for us to handle sensitive data in a secure way), so while it was weird and ugly, the default WebBrowser did the job. It didn’t exactly make it easy to craft a nice user experience, but a lot of the pain we experienced there was more the fault of the website itself than the integration.
That’s a whole other story though.
In the end, if you don’t have a horrifying abomination of technologies like we do, you can probably just use CEFSharp. Its solid, well supported and has heaps of useful features, assuming you handle it with a bit of care.