| Comments

One of the features that a lot of developers seem to enjoy is the out-of-browser feature in Silverlight.  This is the feature that allows you to take your Silverlight application and run it like a desktop application (without some of the trust levels right now).  If you aren’t familiar with the feature, take a moment and familiarize yourself with it…here’s some info:

Now that you have some basis of understanding, allow me to share a thought I’ve had.  I’m seeing a few people wanting to force the out-of-browser (OOB) experience.  That is to say, they want their application to be only run in OOB mode. 

NOTE: If you feel you want this, you should understand the limitations in OOB mode.  Namely the HTML bridge features and certain network implications might affect your applications depending on your needs.

Honestly I am not sure I entirely see the value in that (forcing it), so if you have one, please enlighten me (maybe gaming I suppose).  After all, IsolatedStorage is shared between OOB and in-browser Silverlight applications from the same source.  But I digress. 

The Problem

One thing that I’ve seen is folks asking for a method on how they can force the OOB mode in their app.  One of the security features of OOB is that the Install action must be user initiated.  This means it must be from the end-user action…not automated.  So you can’t just send a URL to someone and have it suddenly install the application OOB.  So how would you go about this?

A Solution

Easy, I think.  Here’s the pattern that I think might work.  I’ll note that this is just my opinion of a solution…not the defacto in all situations.  By all means please consider your own scenario needs before blindly adopting.  That being said, I think it might be helpful to explore.  This solution involves leveraging the OOB APIs available to the developer and creating some different views in your application.

Step 1: Create some views

What you’ll need here really is two views (for lack of a better term at this point – if you want to call them controls, that is fine too) in addition to your main application.  I’ve called one Installer and the other Installed.  The purpose of Installer is that this will be the view shown when a user does not have the application installed OOB.  The purpose of Installed is that it will be the view shown when a user does have the OOB application installed *and* is attempting to view the application in-browser.  The third “view,” your application, is what will be rendered when the user views the OOB application.  Put Installer and Installed wherever you’d like in your app.  I created a folder called Views and stuck them there. 

For Installer, you’ll want to put some UI in here for the Installer “badge.”  I highly recommend putting some time into this using Expression Blend to make it all look great and perhaps add any visual states you may want.  Remember, this is your user’s first impression.  This is still a Silverlight application, so go nuts with styling and using controls.  But you don’t need to make these the same size as your application.  For my example, my OOB application is 800x600, but my Installer app is only 300x162.  This Installer control/view will contain your actual install logic.  So somewhere here you need to have a button or something that the user can initiate as an action that you can call the Install method.  A button is easiest and easy to style.  In the Click event all you have to do (in simplest form, of course you’d want to add some error handling) is add the API call to install:

   1: Application.Current.Install();

For Installed, you’ll do the same thing, except make the UI/UX for the experience of letting someone know they already have it installed.  This could be something as simple as a text message, or complete instructions, etc., whatever.  You decide.

When you are done with this step you should have two XAML UserControls: Installer.xaml and Installed.xaml both for their specific purpose.

Step 2: Wire up the application startup logic

What I’ve chosen to do is take control of the App_Startup logic to determine the state.  I felt it would be better here based on the scenario I’m trying to accomplish rather than to load a default UserControl and have to do some funky app logic to swap out the RootVisual.  What I’ve chosen to do is to check the state of the trigger to run and follow some logic:

  • If the app is installed and the user is running in-browser: Show Installed
  • If the app is not installed and the user running in-browser: Show Installer
  • Else: Show App

The logic finds the correct state in a simple if…elseif…else statement and decides which RootVisual to show.  Here’s the complete code:

   1: private void Application_Startup(object sender, StartupEventArgs e)
   2: {
   3:     if ((App.Current.InstallState == InstallState.Installed) && (!App.Current.IsRunningOutOfBrowser))
   4:     {
   5:         this.RootVisual = new Installed();
   6:     }
   7:     else if (!App.Current.IsRunningOutOfBrowser)
   8:     {
   9:         this.RootVisual = new Installer();
  10:     }
  11:     else
  12:     {
  13:         this.RootVisual = new MainPage();
  14:     }
  15: }

Simple, isn’t it?  You can see that the “app” that gets run is the correct starting point given the state.

Step 3: Configure your OOB settings

Visual Studio 2008 adds a dialog feature for you to easily generate the OOB settings.  You can read more about that here and see some screenshots: Silverlight Out-of-browser Settings Dialog.  Once you’ve done this, when you compile your application it enables it for OOB install capabilities.

Step 4: Deploy

That’s really it.  Of course the harder part is your own application :-), but that’s for you to figure out, not me.  In the end you could then embed your application somewhere within a web page.  It will show your Installer if the user doesn’t have it, or remind them it’s already installed if they do.  Here’s a working example based on this pattern (Silverlight 3 required to run this application):

This is just a stub example app, no working functionality in the main application.  The purpose is just to show this pattern.  As you can see, when the application runs, it runs at the desired 800x600 application width that I want for my actual application, game, whatever.  All this while I minimize the impact visually to show the messages of the Installer and the Installed controls.

Summary

This is just a sample pattern that you may want to implement if the need (or desire) is there to force your applications to be run OOB.  Again, OOB in general should be understood before blindly going in and assuming 100% of what you have will run in this mode.  But if it will and this is something you want, you may consider using this pattern to make a smaller visual impact on the installer and providing the end user with a better user experience to get it installed.  Or at least it was an experiment for me.

Hope this helps!

| Comments

PLEASE READ UPDATE: While the API for checking for updates has changed, the update model *after* that method call to CheckAndDownloadUpdateAsync is the same as described below.  See this note for updates since this article to the Out-of-browser experiences.

Wednesday at MIX09 was a great day.  I was exhausted, but after the keynote I was so high on excitement it was great.  Throughout the day Twitter was buzzing (or is it fizzing?) about all the news around Silverlight 3.  In one conversation around the out-of-browser experiences with Silverlight, I caught a conversation on Twitter about it with questions about the update model.  I jumped in which resulted in this twittersation (sorry, couldn’t resist):

Twitter conversation

So, instead of trying to cram it in 140 characters, I’ll explain here how it works, and take the liberty of using just slightly more than 140 characters.

If you aren’t familiar with the Silverlight Out-of-browser experiences (lovingly referred to as a SLOOB – nod to @bryantlikes), you may want to read my previous post which is a guide to the new features in Silverlight 3 as well as view this video demonstrating the feature.

Detaching the application

The first time your users will view your application it will be in the web context in a page somewhere.  If your application is enabled for out-of-browser (OOB) experiences, then the user can detach the application via a user-initiated action (context menu or some function in your application that calls Detach()).  That process then immediately does a few things:

SLOOB Detach

This action takes the browser (for our example let’s say being served up at http://foo.com/mysloob.xap) and creates an OOB application.  What happens here is the following:

  • The application (XAP) is requested again via the browser
  • The XAP gets stored locally in a low trust location along with metadata which includes the origin URI of the XAP as well as metadata, most importantly for this discussion the ETag information of when it was downloaded (timestamp essentially)

The first OOB launch

Now we are all good.  If we close the application and launch it again from the desktop now we’d initiate a startup of the application:

SLOOB Launch 1

What happens in this instance is basically the application looks at it’s metadata for the origin URI of the XAP and makes a request there with.  It compares the HTTP response data (code and ETag) for comparison.  If the app wasn’t updated, then the app launches and all is well.  In fact inspecting the request it would look something like this for our app:

   1: GET /silverlight/oob/ClientBin/OutOfBrowser.xap HTTP/1.1
   2: If-Modified-Since: Thu, 19 Mar 2009 03:52:35 GMT
   3: User-Agent: Silverlight
   4: Host: timheuer.com
   5: X-P2P-PeerDist: Version=1.0
   6: Accept-Encoding: peerdist
   7:  
   8:  
   9: HTTP/1.1 304 Not Modified
  10: Last-Modified: Thu, 19 Mar 2009 03:52:15 GMT
  11: Accept-Ranges: bytes
  12: ETag: "f2e3a81746a8c91:445"
  13: X-Powered-By: ASP.NET
  14: Date: Thu, 19 Mar 2009 03:55:18 GMT

Notice the HTTP 304 Not Modified response.  No further information is sent and we can see that no content was even delivered.  The app hasn’t changed.  At the API level, Application.Current.ExecutionState is not triggered for a DetachedUpdatesAvailable state change.

Application updated, user launches from desktop

Now let’s update the application and upload to our server.  The next time the user launches the application, the same requests happen:

SLOOB Launch 2

Again, the requests are made sending the metadata information.  In this case though, there is an update.  What happens next is two fold.  Along with the response being sent with a new timestamp/ETag, the request also includes the bits of the updated application.  Looking at the request it would look like:

   1: GET /silverlight/oob/ClientBin/OutOfBrowser.xap HTTP/1.1
   2: If-Modified-Since: Thu, 19 Mar 2009 03:52:35 GMT
   3: User-Agent: Silverlight
   4: Host: timheuer.com
   5: X-P2P-PeerDist: Version=1.0
   6: Accept-Encoding: peerdist
   7:  
   8:  
   9: HTTP/1.1 200 OK
  10: Content-Length: 15557
  11: Content-Type: application/x-silverlight-app
  12: Last-Modified: Thu, 19 Mar 2009 03:56:29 GMT
  13: Accept-Ranges: bytes
  14: ETag: "ce39d0ae46a8c91:445"
  15: X-Powered-By: ASP.NET
  16: Date: Thu, 19 Mar 2009 03:56:45 GMT
  17: <data>

Where “<data>” in the snippet above would actually be the bytes (notice the Content-Length and Content-Type headers) of the updated XAP.  Two things happen now:

  • Application.Current.ExecutionState changes to a DetachedUpdatesAvailable state. 
  • The XAP in the local storage location is updated to the new bits.

Now right now there is no option to decline the updated bits.  If the app is updated, your users will get them.  This is something the team is considering understanding different scenarios.  Also, there is no way to force the user to shutdown the app or prevent them from continuing.  You should build your app in a manner that if an update occurs that perhaps you block the UI until the update is launched.  Re-launching the application (from the local machine) would startup again with the newly downloaded bits and the updated request would look again similar to the first:

   1: GET /silverlight/oob/ClientBin/OutOfBrowser.xap HTTP/1.1
   2: If-Modified-Since: Thu, 19 Mar 2009 03:56:49 GMT
   3: User-Agent: Silverlight
   4: Host: timheuer.com
   5: X-P2P-PeerDist: Version=1.0
   6: Accept-Encoding: peerdist
   7:  
   8:  
   9: HTTP/1.1 304 Not Modified
  10: Last-Modified: Thu, 19 Mar 2009 03:56:29 GMT
  11: Accept-Ranges: bytes
  12: ETag: "ce39d0ae46a8c91:445"
  13: X-Powered-By: ASP.NET
  14: Date: Thu, 19 Mar 2009 03:57:12 GMT

And that’s the basic mechanism of it.  When an application is detached, metadata is stored about that app.  Each launch it does a check against that metadata to see if an update is visible.  If the app is offline, that’s fine, it won’t block the app from launching.  This is very similar to the .NET Framework’s ClickOnce concept in deployment.

Contrast with Adobe AIR Update Framework

Kevin asked me to contrast that with how AIR does it because he felt that “in AIR, very straightforward.”  Now, as you can also see in the initial conversation, I admitted not knowing how AIR worked.  I’m basing the following (and I won’t go deep to get myself into “you’re an idiot” trouble) off of my research in the Adobe AIR developer tutorial on Using the Adobe AIR Update Framework

Now as far as I can tell, first the update framework is not a part of the core AIR runtime and [UPDATE] is avialable in the AIR 1.5 SDK (thanks David for the note) is a separate plugin/module/whatever-term-would-be-appropriate-for-the-runtime for AIR.  Mihai in the tutorial points to needing to download the update framework which apparently is a lab framework (I’m not sure if that means beta, supported or what – if someone knows please comment)?

Once the framework is included the next step is that the developer has to create an update manifest which is described as:

This is a small XML file that keeps information needed by the updater (for example, what version is available, from where to take it, and what info to display). This file sits on your server, and when the application starts up it loads this file to see if any updates are available.

Essentially a file sitting on some location that the app can retrieve (presumably a web server).  The developer then implements an event handler on the AIR WindowApplication.creationComplete event to check for an update.  This includes about 10-15 lines of code to tell the update framework where the manifest file is and to attach to some additional event handlers and code.  When the update is noticed and needed a prompt will occur and AIR has a default UI that would be shown:

Adobe AIR Update Dialog

I like the default UI, it’s informative.  Notice the Release Notes information.  This information comes from the manifest file.  I like that too…good idea.

What it appears to me though is that when the developer has an update the manifest needs to be manually updated or no update events would be triggered.  Perhaps that’s a good thing for granular control over optional events.  I couldn’t find information if you can modify whether “Download Later” was an option or not (thus forcing an update).

Summary

So there you have it.  In Silverlight 3 you have a framework to enable you to auto update your bits.  We’re still in beta so things may change and we know there are questions that you may have on our model (if you have those questions, please leave a comment here).  In AIR you have a framework that doesn’t appear to be a part of the core runtime but that provides the same methods for providing notification to your users.  If I’ve gotten my interpretation of the AIR tutorial wrong, please someone kindly correct me as it wouldn’t have been my intent to do so.

I hope this helps understand the model.  If it didn’t, then perhaps I should have just used 140 characters.


This work is licensed under a Creative Commons Attribution By license.