Sunday, November 15, 2009

Restart WPF application that is ClickOnce deployed

What do I have:
Tiny WPF application deployed via ClickOnce that is up and running all the time and like start menu it contains shortcuts to applications that will be used by users + work related statistics. Application is deployed to remote IIS and is available offline.


What do I need:
When new version is deployed application must upload latest version and restart itself. Kind of self updating application. For that it is checking every x minutes for a new deployment.


Problem:
  • Unfortunately there is no appropriate method in Application class for restarting WPF application like there is one in Windows Forms. Referencing Windows.Forms just because of that does not sound like a proper solution. If Restart method was pulled out from WPF then there should be a reason for that.
  • Starting a new process with application deployment path and shutting down current does not sound like a good idea as a new browser window will be opened. I don’t want users to be confused by any new windows that will appear without ‘any reason’.
  • Starting a new process like Process.Start(Assembly.GetEntryAssembly().Location); is good for just restarting but in case of ClickOnce deployed application it will start the same version but not new one.
  • Another point to make application self updating is that since it’s up and running all the time it needs to utilize latest critical updates that maybe time critical.



Solution:
What I thought of was to create a simple “restarter” app that will:
  • Be started after new version is downloaded.
  • Close all running applications by closing all processes by app name.
  • Contain a transparent borderless main window with a web browser control on it that will navigate to app deployment uri. It will ensure that no new browser windows will appear, no ClickOnce “Update Available” dialog will appear and latest version will start.
  • When navigated (new app started) it will close itself.
  • Encapsulate all restart logic + infrastructure (invisible web browser control).

Main Application will contains a timer that will check for new version, update current deployment if new one is available, start ”restarter” and finally shutdown itself.


Main code blocks:
Main app:
///
/// New version check routine.
/// If new version is avaialble start restarte
///
private void CheckForNewVersion()
{
  ApplicationDeployment deployment = ApplicationDeployment.CurrentDeployment;
  // check for new version
  if( deployment.CheckForUpdate() )
  {
    // start restarter process
    Process.Start( "Restarter.exe" );
  }
}


Restarter is pretty simple:
XAML:
<Window x:Class="Restarter.RestartWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        Title="Restarter" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        d:DesignHeight="500" d:DesignWidth="500" SizeToContent="WidthAndHeight" Width="1" Height="1"
        Loaded="Window_Loaded" WindowStyle="None" ShowInTaskbar="False" Opacity="0" Top="-10" Left="-10">
    <Grid>
        <WebBrowser Name="ctrlBrowser" Navigated="ctrlBrowser_Navigated" Width="10" Height="10"/>
    Grid>
Window>

Code behind file has only two event handlers:
private void Window_Loaded( object sender, RoutedEventArgs e )
{
  // shutdown all running applications
  foreach( Process process in Process.GetProcessesByName( "WPF.Restart" ) )
  {
    process.Kill();
  }

  // open online application uri -> it will update app to latest version
  ctrlBrowser.Source = new Uri( "" /* Insert your deployment uri*/ );
}
private void ctrlBrowser_Navigated( object sender, NavigationEventArgs e )
{
  Application.Current.Shutdown();
}

You can download solution (.Net Framework 4 Beta 2) here or get it from file widget lower on the right panel.


Hope this helps someone.
Regards,

Oleh

No comments: