The AppLife Update Blog
A complete solution for maintaining deployed software on the Windows platform
Has your product prerequisites ever changed between versions and therefore required you to deploy a new Windows Installer package before your new product version can function? Have you ever needed to deploy a companion application along with your previously installed and AppLife Update maintained application? With AppLife Update, these challenges can be overcome by executing a Windows Installer package as part of your application update.
Packaging and Executing an MSI in an AppLife Update
It’s very easy to include a Windows Installer package with your application update and then execute that Msi on all of your remote systems. To accomplish this, all that needs to be done is to include the Msi file with your update by using an Add & Replace files action, and then run the Msi using the Execute Msi action.
The Update Package Directory is the temporary directory that the update engine creates and removes for the currently executing update and is easily accessed by other actions. If you want your Msi file to remain on the remote system after the update completes you would choose a different folder, such as a subdirectory of your application directory.
After this update action executes, the Windows Installer package will be present on the remote system. To execute the Msi we’ll add an Execute Msi action. The Execute Msi action lets your define a <path> variable and then use that variable within an msiexec.exe command line. When defining the path variable, you can choose any of the available local paths, including the Update Package Directory path that we placed the Msi file in with the previous update action. To complete the path to the Msi file, any subdirectories as well as the Msi file name can be added to the subdirectory property. With the <path> variable defined, the msiexec command line can be constructed. To install the Msi, the /i command line option can be used. A command line of /i “<path>” is equivalent to the default Msi file behavior.
When this action executes, the Msi is executed on the deployed client.
Conditionally Update Based on Local Information
In the case of prerequisites, it might be necessary to first verify that the prerequisites are not already present before executing the installer package. There are a number of methods that can be employed for this purpose. With the Windows Installer product code, we can look up information about the product. For example, if I wanted to first verify that AppLife Update was installed before taking action on the client, I could look up the AppLife Update product code in the uninstall registry key. Using a Read Registry Key action, we can look up the uninstall string and even use it to uninstall. If the string is not present, we can use the information to infer that the product is not installed.
Using a Read Registry Value action, we can set a Shared Property to the value of the uninstall string, and default the value if the string cannot be read.
If we wanted to make the execution of the Msi action conditional on AppLife Update being installed (or not installed) we can add a conditional statement to the update action.
Windows Installers can be configured to first uninstall the application, but this is not usually the default behavior when using Windows Installer creation tools. If you would like your update to first uninstall, we can use the uninstall string we just read from the registry using a Command Line Action. Alternatively, we can also use the Msi Action directly.
Silent Updating when Using the Elevated Permissions Service
When your application is using the AppLife Update Windows Service for permissions elevation, it’s important to recognize that the Windows Installer will be executing under the Local System account user, and won’t be able to interact directly with the logged on user. For some installers, this could impact the way they operate. Running under the service, the installer must be able to run silently (using /q). In the event that the installer must interact with a user, the elevation service cannot be used for that update. When publishing the update, select the option to override the controller elevation settings and use the UAC elevation option instead. This will require an administrator to apply the update, but will allow the Msi to interact with them as they do so.
Disabling the Elevation Service
Troubleshooting Updates that Deploy an Msi
The AppLife Update engine logs activity to an event log as the update executes. However, when an Execute Msi action is executed, the log will only include information indicating the exact command line used and the msiexec return value. In order to investigate what actions the Msi performs, it is necessary to use the msiexec command line option to log the Msi operation. This option can take an explicit path. To write a log file to the install directory, you can use a Set Shared Property action to define a Share Property with the application directory path.
And use it to define where to write the log file to.
In the course of maintaining an application, it is sometimes necessary to install new prerequisites or companion installers. Using AppLife Update, automating these actions and ensuring an effortless transition for your end-users is very easy. The update actions available within AppLife Update lets you package and deploy new Windows Installer packages, conditionally execute update actions, uninstall existing packages if necessary, and troubleshoot the update process before widespread distribution.
Most application files reside in the installation directory, referred to within AppLife Update as the Application Directory. The AppLife Update execution engine determines the Application Directory from the physical location of the executable that launches the update. When any of the file related update actions are added to an update action list, the default directory that is targeted is the Application Directory.
This is most often the location where files need to be added or replaced during an application update. Accessing paths relative to the Application Directory can be accomplished using the subdirectories property.
And navigating up the directory tree…
In addition to the Application Directory, you can target any of the well-known named folder locations as well, such as the current user’s profile directories.
You’ll notice in the list of directories, there is an Explicit Path option. This option lets you specify the entire path to use for the file action. At first glance, this feature doesn’t look all that useful, but when combined with Shared Properties, it becomes a very nice feature.
Expanding Shared Properties in Place
Shared Properties can be used in many action properties. Most of the properties of built-in actions have the ability to expand a Shared Property in place. These properties are adorned with a blue icon indicating their support of this feature. A Shared Property is inserted using $$ delimiters, and during update execution, the value of the designated Shared Property is inserted in-place of the delimiter.
So for file actions, we can use a Shared Property to target an explicit folder, which opens up lots of possibilities because Shared Property values can be passed in from the host application, set by other update actions that can read from a database, the registry, xml config files, or custom actions running your own code.
Say your application installs templates for Microsoft Office and you need to maintain those templates during an application update. There is a default location for Office templates however this location is user configurable, so it could be different on every system. The default template location is located in the current user’s roaming application directory.
If the user has specified a custom template location, we can find this path in the registry.
HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\Common\General – UserTemplates
One caveat is that if the user has not specified a custom location, this registry key is not present. So in order to target the correct folder, we’ll need to read the user configured location from the registry, and then use that location with an Add & Replace files action. If the registry value is not present, we’ll also set the default value of this shared property to the correct default location by expanding another Shared Property.
Action 1 – Add a Set Shared Property action and set a Shared Property to the current user’s local roaming AppData folder path. We’ll use this to set the default value in the next action.
Action 2 – Add a Read Registry Value action to get the User Configured template directory from the registry and assign the value to a new Shared Property. If the value doesn’t exist, we’ll set the default value with the path identified in action 1.
Action 3 – Add an Add & Replace Files action and use an explicit path to target the correct templates folder.
Using Shared Properties and explicit paths, we can easily access and maintain files and assemblies in any local directory that our application uses.
In order to maintain an installed software application on different hardware and operating environments, it is sometimes necessary to perform update actions based on the conditions at the local site or system. An example of this is when your application uses both 64 bit and 32 bit assemblies. Your installer places the appropriate assembly during initial installation, but in order to maintain that installation during updates we might need to take different action based on the local architecture. Another example might be maintaining different configurations, such as a Dev, QA, or Production installations. We might want to place debug symbols on a dev update, but not in production. Or we might set specific configuration settings differently, based on the environment. With AppLife Update, we can use information from the local installation to conditionally control the actions taken during the application update. We accomplish this by defining and setting Shared Properties based on the local system information and then using those Shared Properties to conditionally execute update actions.
The AppLife Update engine execution context hosts a collection of name/value pairs, and this collection is named the Shared Properties collection. Shared Properties can be accessed by any Update Action in the update action list, hence the name Shared Properties.
Shared Properties can be defined and set in a many different ways. The initial collection of shared properties can be passed into the update context from the host application. The ApplyUpdate method of the Update Controller has an overload that takes an IDictionary object of string values. The contents of this object are used to populate the collection of Shared Properties.
1: Dictionary<string, string> sharedProperties = new Dictionary<string, string>(); 2: sharedProperties.Add("MyData1", mMyData1); 3: this.updateController1.ApplyUpdate(ApplyUpdateOptions.None, sharedProperties);
If you are using the built-in user experience controls, your code doesn’t call the ApplyUpdatemethod directly. All of the user experience controls have a property called InitialSharedProperties that can be used to add values to prior to applying an update.
1: updateDisplay1.InitialSharedProperties.Add("MyData1", mMyData1);
Many of the built-in update actions that read information can set shared properties. The Database actions can read query results into a shared property. The Read Registry Value action, Read Xml Node action, and the Set Shared Property action can all set shared property values. Custom actions, most easily created using the C#/VB.Net dynamic code action, can access the Shared Properties collection directly through the context parameter.
It’s worth noting that while most of the actions read and write string values to the Shared Properties collection, custom actions can set and access any object data.
Conditional Updating Based on Shared Properties
Say we want to replace 64 bit or 32 bit assemblies, depending on the architecture of the system being updated. To accomplish this, we’ll add two Add & Replace files actions to our update action list, and then conditionally execute each action based on the value of a Shared Property. We can define the Shared Property with a Set Shared Property action. Processor type is one of the values that are available to set using this action. We’ll name the Shared Property ProcessorType.
After the action executes, the Shared Property named ProcessorType will contain a value of either 32 or 64, based on the current processor architecture. We can now use this value in the Conditional Statement of the two file actions.
This action will only be executed if the value of the ProcessorType Shared Property is “64”. The conditional for the 32 bit assemblies is obviously “32”. By using a Shared Property and conditional statements, we can use local system information to control the execution of the update.
Example Project Source Code
When your software operates in an unreliable networking environment, it can be very challenging to automatically maintain your software installation. These environments are quite common. Police cruisers and ambulances use laptops to run software in the field, relying on cellular technology to distribute updates. Traveling salesmen use proprietary laptop business software and are constantly going in and out of network coverage. Medical staff use their mobile pc’s in large medical facilities, where dropping connections in elevators and dead Wi-Fi spots is common place. In scenarios like these, professionals are using software that must be maintained. In today’s professional environments, software maintenance is expected to “just happen”, without interrupting the work of the professionals using the software. Software publishers who create software that operate in environments like these all face the same challenge of maintaining their software without requiring support staff to take possession of the hardware. The AppLife Update Solution provides the answer for many software publishers whose applications operate in environments like this.
With the AppLife Update solution, software publishers create and publish self-contained update packages that migrate an installation from one version to another. These update packages are retrieved by the client software when network connectivity is present. Once the update package is retrieved, network connectivity is no longer required and the update can be applied, performing the work necessary to maintain the deployed installation. As the update package is downloaded, the partial download is cached locally so that any interruption in network connectivity will not require restarting from the beginning.
The flexibility of the AppLife Update solution provides the ability to change the behavior based on known circumstances. When it is known that the software being maintained is going to operate in an unreliable networking environment, it is possible to handle network connectivity errors differently than normal. Normally a network connectivity error would be unexpected and the user would be notified of the situation. Instead, the application can postpone the update retrieval operation for a short period of time and try again. And try again. And try again, until the update retrieval succeeds and can be applied.
In the example project accompanying this post, the updating functionality is completely automated. The implemented updating process can be described as:
When the application launches, a background update check is performed. If an update(s) is available, the update packages are retrieved in the background and without user interaction. If network connectivity is lost, the retrieval is postponed and continually retried in the background without user interaction until the update is successfully retrieved. Once retrieved the user is prompted to either apply the update now, or when the application is closed. If the user elects to apply the update, the update is applied and the application restarted. If the update is deferred until the application exits, the update is applied as the application exits and the application is not restarted.
The example project contains an Updater class that controls the update check and retrieval process. Inspecting the Updater class, you’ll see that the class maintains an AppLife Update Controller and uses the API methods and events of the update controller to pause and retry as network errors occur. When an update package is successfully downloaded, an event is raised, alerting the primary application that an update is ready to be applied. The implementation code in the primary application is very simple. An Updater class is instantiated and the Update Controller properties are configured. Once configured, the GetUpdateCompleted and GetUpdateProgressChanged events are hooked before calling the GetUpdateAsync method. That’s it. Any updates will be retrieved as the network allows. When the completed event fires, the Updater status is checked and the user is prompted to apply the update now or when the application exits.
The Updater class is the heart of the implementation. It has a very simple interface:
The Updater class wraps an AppLife Update Controller and exposes a single method that implementing code will call to perform the entire update process.
We want to take a look at the GetUpdateAsyncmethod. This method calls the Update Controller’s CheckForUpdateAsync method and then returns. The update process continues in the CheckForUpdateAsyncCompleted event handler. If an update is present, the DownloadUpdateAsync method is called. When the download completes, the GetUpdateCompleted event is raised. When these asynchronous methods are called, any network errors will cause the completed event to fire, and the event argument’s Error property will indicate the cause of the error. Based on the error information, we can take appropriate action. In this example, we are considering any WebException or IOException to indicate a network error. Your implementation can improve on this, based on environment specifics. At any rate, when a network error is detected, the updater class goes into a wait mode and try’s the check/download again after a period of time. Eventually, the update download is completed, and the GetUpdateCompleted event is raised, triggering the client application to prompt the user.
Of course, the user experience behavior is completely customizable with just basic coding. The AppLife Update Controller performs the heart of the technical challenges involved in this implementation, and the Update Engine is what lets you package up any work that is necessary to maintain and migrate your deployed installations. So I invite you to take a look at the example and evaluate it for suitability in your own environment. Feel free to contact our technical support team with any questions.
Kinetic Jump has a new home! We’ve moved down the road about 5 miles to a new office building with more space and a better landlord (us!). Moving day was Friday June 28th, and by Monday July 2nd we were back up and running! Hats off to the guys from Two Men and a Truck and all the people who helped out in this endeavor. Aside from a dead power supply on a server, all of our equipment came back to life without issues. Unfortunately our phone numbers couldn’t make the move with us, so please take note of the new phone number.
Kinetic Jump Software
8746 Egan Drive
Savage, MN 55378