Click-Once forced updates in VSTO: Some things we don't recommend using, that you might consider anyway.

Before I go any further in this post I'm going to put a huge disclaimer: 

Okay so disclaimer said,  I'm going to talk about is something you can do with a win-forms Click-Once application that you might try to do in VSTO (albeit with some large caveats).  The basic scenario is this:  You develop a customization that periodically checks for updates which contains a button that the user can "force" an update check with.  Click Once provides an API for this that works very well, and you can use this same API with VSTO though you travel from the realm of "it works and we've fully tested it" into the realm of "it might work but will very likely 'break' you anyway".

So lets' get to the point, in previous posts I've mentioned that you can reference System.Deployment and with a little work get access to the "CurrentDeployment" object.  I've used this in the past to demonstrate how to detect if your customization is running from local disk or from the Click-Once Cache.  We're going to use the same API, but now we're going to "force" an update check and install it.  The second method is going to use our friend the "VSTO Installer" executable that ships as part of the VSTO Runtime 3.0.  

Here's an example of a customized document in which I have create that demonstrates both methods:

Forced Updating Doc example ImageI've dropped some Controls onto this document to demonstrate 2 basic scenarios and additional options for the first scenario.

The basic flow is this, the user clicks the button and the 2 labels beneath it will give some information about the update/version.  The first label will display the current running version and the second will display the version after it the update has run.  As you can see in the example image the version is the same.  I show this now to demonstrate the next point I'm about to make.

When the customer updates the solution in this manner (either through the API or via VSTO Installer) the current running version will not change.  To fully update to the new version the running version of the customization would have to be shut down and the new version loaded up.  Currently neither Office or VSTO have support for doing this at the moment.  I imagine there are ways to do this but I'll leave that for a later time.

So let's talk a little more about the API itself.  I'm going to cover it first because I personally feel it's not the best solution for several reasons and then I'll move to what I feel is in a better direction. 

The first issue that comes up is a matter of trust.  In order to use the API to update the customization the developer needs to grant specific permissions to the code in the Click-Once Cache.  My understanding based on the feedback from the developer who provided me with the trust code that this is a case where the differences between the Click-Once Trust Model and the VSTO Trust Model differ.  What happens under the hood here is this:  ultimately in VSTO the VSTO Runtime is what determines the trust for the Addin.  When it creates the Add-in it evaluates everything against the VSTO trust model and (given the Add-in is actually trusted) executes the Add-in code under full trust.  Since the trust is evaluated and stored by the VSTO Runtime the Add-in must set the trust/permissions for the code in the Click-Once cache in order to execute the update, but it is able to do this since VSTO Add-ins always run under the "Full-Trust" Context. 

So here's the the trust code for enabling the API:

 //Create the appropriate Trust settings so the Application can do 
//Click-Once Related updating

ApplicationDeployment CurrentDep = ApplicationDeployment.CurrentDeployment;
String deploymentFullName = CurrentDep.UpdatedApplicationFullName;
ApplicationIdentity appID = new ApplicationIdentity(deploymentFullName);
PermissionSet everything = new PermissionSet(PermissionState.Unrestricted);

ApplicationTrust trust = new ApplicationTrust(appID);
trust.DefaultGrantSet = new PolicyStatement(everything);
trust.IsApplicationTrustedToRun = true;
trust.Persist = true;

ApplicationSecurityManager.UserApplicationTrusts.Add(trust);
  

The reason why I would not use the API for updating is that there are steps that the VSTO runtime must do as a result of the add-in existing within an Add-in Model versus a Stand-alone Executable Model.  Essentially the add-in model is a little more complex than a "standard" Click-Once scenario since not only does the execution code have to be moved onto the user's machine, but also there is a chunk of meta-data that needs to be communicated to the Host-Application for the Customization so that the host process can properly plug-in the customization.

What this turns out to mean is: when the update is done using the API, not every part involved in the customizatoin is correctly updated.  The behavior that I see when I tried this scenario is that the ApplicationDeployment object behaves strangely after the update occurred.  My initial investigation indicates that the solution information store is out of date after the update which then causes the customization to be running in an unexpected context. 

I won't show you the code for updating because I can't personally recommend it for your application.  What you might do though is use CurrentDeployment.CheckForDetailedUpdate() which will tell you if an update is available at least.

Anyway.

Let's move on to using VSTOInstaller...

In a previous post I talked about VSTOInstaller, but as a quick rehash, basically it's an thin executable that uses the same functionality in the runtime to update/install the customization.  What it means in this scenario is that we can use System.Diagnostic.Process to call it and force an update.  In the example code that I'm going to post I call it in 2 ways to demonstrate 2 alternative methods to consider.

In the first method you call VSTOInstaller directly (and silently).  The advantage in using this method is that the user does not see any UI.  The VSTOInstaller will return a negative value if the "update" failed.  Unfortunately the return value of 0 is the only "positive" value you will get back and it is not informative of whether or not an update occurred or if the current version is up to date.

Here's the Code for using VSTOInstaller Directly:

 label1.Text = "Current Version: " + 
    ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString();

//Call VSTOInstaller Explicitely in "Silent Mode"
string installerArgs = " /S /I \\\\GenericServer\\WordDocument2.vsto";
string installerPath = "C:\\Program Files\\Common Files\\microsoft shared\\VSTO\\9.0\\VSTOINSTALLER.exe";

System.Diagnostics.Process VstoInstallerProc = new System.Diagnostics.Process();
VstoInstallerProc.StartInfo.Arguments = installerArgs;
VstoInstallerProc.StartInfo.FileName = installerPath;
VstoInstallerProc.Start();

VstoInstallerProc.WaitForExit();
//Report Exit Code
MessageBox.Show("Exit Code: " + VstoInstallerProc.ExitCode.ToString());                  
//End "Silent Mode" Scenario  

If you want UI the easier method is to simply "shell out" the path to the .vsto file.  Doing so would invoke the mime handler which under the covers calls VSTOInstaller.  This method is less likely to fall prey to issues that come of running on future versions of the runtime(not that I'm saying we support you doing this exactly and I'm not going to go into specifics currently).  The UI will inform the user if the update actually happened or if the version was up-to-date but I don't know what would happen if the path is not available and there may be additional UI that occurs (I have seen specific cases in non-customization managed code where specific security settings on the OS would cause a blocking dialog to occur when the shell handler is invoked in this method).  So your mileage may still vary.

And here's the code for installing using the Shell Handler:

 //Call VSTOInstaller Via the Shell (will show UI) assumes the Mime handler works
string manifestPath = "\\\\GenericServer\\WordDocument2.vsto";

System.Diagnostics.Process ExplorerInstallProc = new System.Diagnostics.Process();
ExplorerInstallProc.StartInfo.Arguments = manifestPath;
ExplorerInstallProc.StartInfo.FileName = "explorer.exe";
ExplorerInstallProc.Start();

label2.Text = "\"NEW\" Current Version: " + 
    ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString();            

Here are some items to keep in mind if you are considering this at all:

  • VSTO Installer is less likely to put you in bad state then calling the API's directly.
  • Cached Data (in the Click-Once Data Cache) may be impacted (I would recommend thorough  testing/investigation).
  • You might consider shelling out a process that 'sleeps' until the Office app is closed and then causes the Update.
  • You might consider forcing a restart of the office application after the update occurred.
  • Warn your users that they may want to save their data before you start the update process.
  • Thoroughly Test anything before fully integrating it into any solutions.  Make sure you try it under extreme security situations (IE Protected mode on while running as a normal user).

If you do end up using any of this or have come up with a better solution, feel free to comment, I love open discussion about this stuff.

Thank You for reading.

Kris Makey