Eric's Blog

Day to day experience in .NET
Welcome to Blogs @ IRM Sign in | Join | Help
 Search

Disclaimer

The content of this site is my own personal opinion and does not in any way represent my employer, it's subsideries or affiliates. These postings are provided "AS IS" with no warranties, and confer no rights.

This Blog

Programmatically Update a ClickOnce Distributed Office Add-in

When using ClickOnce for distributing an Office Add-in the default configuration is to check for updates every 7 days. In an add-in I built I have situations where the add-in must be up to date (for example when changing the WCF service interface). In this post I will show how I solved it.

To check for existing updates I use the regularly ClickOnce API:s, for example a simple check could look like this:

return ApplicationDeployment.IsNetworkDeployed && ApplicationDeployment.CurrentDeployment.CheckForUpdate();

If there is a new version available the solution is to quit Outlook and start another small updater program which will start the installation and then restart the application (Outlook in my case). I have called the little program OutlookRestarter and is a simple Windows Application which is created as its own project in the solution. The compiled output from that project, I have added to the add-in project as a linked file and the important part is to set the file as Content in the Build Action so that it will be included in the ClickOnce installation.
To start the OutlookRestarter I use a simple Process.Start and the only “hard” part about it is to resolve the folder in which the files are deployed:

Uri updateLocation = ApplicationDeployment.CurrentDeployment.UpdateLocation;
                    
Assembly addinAssembly = Assembly.GetExecutingAssembly();
string cachePath = addinAssembly.CodeBase.Substring(0, addinAssembly.CodeBase.Length -
    System.IO.Path.GetFileName(addinAssembly.CodeBase).Length);
Uri restarterPath = new Uri(cachePath + "OutlookRestarter.exe");

Process.Start(restarterPath.AbsoluteUri, updateLocation.AbsoluteUri.Replace("File.vsto", "Setup.exe"));

Globals.ThisAddIn.Application.Quit();

I pass the Url to the installation as an argument to OutlookRestarter and the only notable thing about it is that I replace vsto, with the bootstrapper exe file, because I get ClickOnce errors if trying to go for the vsto file directly.

The OutlookRestarter application have three tasks; download the setup, run the setup and start Outlook again. I choose to download the file and run it manually to save the user from one click, which would be the effect if just doing a Process.Start on the Uri. For downloading the file I copied the DownloadFile method from http://www.codeguru.com/columns/dotnettips/article.php/c7005, with a small modification. Before creating the file I check if it already exists and in that case deletes it.

//Download setup
string tempFileName = Path.Combine(Path.GetTempPath(), "Setup.exe").ToString();
DownloadFile(args[0], tempFileName);

//Start and wait for setup to finish
System.Diagnostics.Process setupProcess = System.Diagnostics.Process.Start(tempFileName);
setupProcess.WaitForExit();

//Check to see if any existing Word Processes exist
System.Diagnostics.Process[] currentOutlookProcesses =
    System.Diagnostics.Process.GetProcessesByName("OUTLOOK");

//Empty Case gets ignored, lazy but effective enough
foreach (System.Diagnostics.Process p in currentOutlookProcesses) p.WaitForExit();

//Start Outlook
StartOutlook();

Before starting Outlook again I wait for setup to finish and also makes sure that Outlook is closed. To start Outlook I check the registry settings that is created by Office 2010 (14), but for Office 2007 (12) I make an assumption that it is installed under Program Files.

private static void StartOutlook()
{
    //Office 14 has installation path in the registry
    RegistryKey localMachine = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Office");
    object installationPath = null;
    string installationFolder = null;

    foreach (string s in localMachine.GetSubKeyNames())
    {
        installationPath = Registry.GetValue(string.Format(@"{0}\{1}\{2}", localMachine.ToString(), s, @"Outlook\InstallRoot"), "Path", null);
        if (installationPath != null) break;
    }

    if (installationPath != null)
        installationFolder = installationPath.ToString();

    //Guess that Office is installed under Program Files
    if (installationFolder == null || !File.Exists(Path.Combine(installationFolder, "Outlook.exe"))) //Office 12
    {
        installationFolder = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Microsoft Office\\Office12");
        if (!File.Exists(Path.Combine(installationFolder, "Outlook.exe")))
            installationFolder = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Microsoft Office\\Office12");
    }

    string filePath = Path.Combine(installationFolder, "Outlook.exe");
    if (File.Exists(filePath))
    {
        System.Diagnostics.Process.Start(filePath);
    }
}
Published den 25 juli 2010 13:00 by ericqu
Filed under: , ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Leave a Comment

(required) 
(optional)
(required) 
Submit
Powered by Community Server, by Telligent Systems