ActivePatch Developer's Guide - Package Control

The ActivePatch Package control is used to build and apply patch packages. The Package control properties and methods related to the creation and application of patch package are divided into three general groups: those which create or update the package, those which apply or reverse the application of a package, and those which return or modify information about the package itself. While patch packages are designed specifically for the creation and application of patches to a collection of files, it can be helpful to think of them in terms of a specialized archive, similar in concept to Zip files. In fact, the ActivePatch Package control could be used to create simple, compressed archives if desired. It's important to note however, that a patch package is not a Zip file, and utilities or libraries designed to work with them will not function with ActivePatch packages.

Create Method

The Create method creates a new package file, or overwrites an existing file, given the original version and updated versions of the files to be included in the package. When creating the package, the collection of original and updated files may be specified either by a file name which includes wildcards or by a directory name. If a directory is specified, all of the files in that directory are analyzed. There are numerous options which control how the files are stored in the package, as well as how they are to be applied on the target system. The two key sets of options are the package options, which are used to specify attributes of the package itself and specified by setting the Options property. The other options are the update options, which are used to control how the package is created and are specified as an optional argument to the Create method. There are three key package options: the package should support multiple versions of files, when the package is applied it should search for missing files, and/or it should ignore those files which it cannot find.

Normally, when a package is created, only one version of an updated file is stored in the package. However, there may be cases where multiple versions of a file should be included. One example is when there may be multiple versions of an original file and you wish to create a single package which can update any of them. Consider an application which has been distributed to end-users and has been updated three times. There are now versions 1.0, 1.1, 1.2 and 1.3 which have been released over a period of time and you wish to create an update to take them to version 2.0. Rather than requiring that the users update individually to each subsequent version (from 1.0 to 1.1, then from 1.1 to 1.2 and so on), a single package can be created which consists of updates from version 1.0 to 2.0, 1.1 to 2.0 and so on. This eliminates the need for the customer to download multiple because, when the package is applied, ActivePatch will determine which version of the updated file should be applied. This determination is made based on three criteria: the name of the file, the checksum of the original file on the target system, and the operating system that the package is being applied on.

Of course, the trade-off for the convenience of distributing a single patch package is that you have a much larger package file, since you are including multiple versions of the same file.

Another example of when multiple version support is useful is if the update must target different operating system platforms. Using the above example, let's say that the software is written for Windows 95/98 and Windows NT/2000/XP, however, the executables are different for the Windows NT version of the package. In this case, ActivePatch can be instructed to install only those files specific to the current platform that the package is being applied on. In other words, if you have a Windows 95/98 version of PROGRAM.EXE and a Windows NT version of PROGRAM.EXE which are different, both updated versions will be stored in the package. If the package is then applied on a Windows 95/98 system, the correct version of the file will be updated. Again, this allows you to create a single patch package which targets multiple operating systems at the expense of a larger package file.

The update options for the Create method determine how the function analyzes and stores the updated files in the package. There are three important update options: the option to recursively scan all subdirectories for modified files; the option to always replace files, regardless of their current version (effectively making the package an archive); and the option to create partial updates. A partial update is one where files may be added or modified but should never be removed from the target system.

In addition to the various options and the original and updated collections of files, one more important piece of information must be specified when creating or updating a package, and that is the default installation path for the files being stored in the package. By default, when the package is created, the files in the package are stored using relative files names. Consider a product that is being patched from version 1.0 to version 2.0, and which has the following directory structure:

In this case, when the patch package is created, and ActivePatch is told to recursively analyze the differences between the files in version 1.0 and version 2.0, the files would be stored with names relative to those directories. For example, if a file named Widget.exe was found in the Programs subdirectory, it would be stored in the package as Programs\Widget.exe. Because the file is only stored with a relative path, there must be a way to determine where the file should be updated on a target system. To address this issue, there are four general approaches that may be taken when creating the patch package:

  • Default Directory
    The updating application (the program which is applying the patch package) is responsible for determining the correct installation path. In this case, no installation directory is specified when the package is created and must be specified when the package is being applied on the target system. If no installation directory is specified when the package is applied, then a special set of rules are used by ActivePatch in an attempt to determine what the correct installation path is. In brief, these rules are as follows:
    • If a product key has been specified for the package using the SetPackageProperties function, then search the registry under that key for a value which contains the installation path.
    • If a product name and version has been specified for the package using the SetPackageProperties function, then search the registry under that product name for a value which contains the installation path.
    • If a product name has been specified for the package, and no registry information has been found, search under the Program Files directory looking for a subdirectory with the same name as the product.
  • Absolute Directory
    An absolute directory path is specified when the package is created. This choice is appropriate for those situations where the installation directory is known to be identical on all systems that will apply the patch package. In general, this would not be an appropriate method for specifying the installation directory where the user has the ability to specify an alternate installation directory at the time the original version of the files were installed on their computer system.
  • Environment Variables
    An environment variable may be specified as part of an installation path by enclosing the variable name in percent symbols. For example, "%TEMP%" would refer to the directory where temporary files are stored for the current user. This method should only be used if it is known that the user's environment will have the appropriate variables defined at the time the package is applied.
  • Folder Macros
    A macro is specified as the installation directory which is resolved at the time that the package is installed. Macros provide the greatest flexibility and it is recommended that they are used whenever possible. When specified, folder macros should be enclosed in brackets such as [WINSYSDIR]. Additional subdirectory paths may be specified after the folder macro if needed and they may be combined with environment variables. For example, the installation path "[WINDIR]\Media" would be valid. For a list of folder macros supported by ActivePatch, refer to Appendix A.

Here is an example of how the Create method could be used to create a patch package:

Package1.FileName = "Widgets.pak"
Package1.Password = ""
Package1.Options = apDefaultOptions

On Error Resume Next: Err.Clear
Package1.Create "C:\Project\Widgets\Version1", _
                "C:\Project\Widgets\Version2", , , _
                apRecursiveUpdate + apPartialUpdate

If Err.Number Then
    MsgBox Err.Description
    Exit Sub
End If

Once a package has been created, the Update method can be used to add additional files to the package or replace existing files.

Update Method

The Update method is similar to the Create method with the exception that instead of creating a new package, the package is updated with the specified files. The Update method may be used to refresh the contents of a package, replacing the current set of files with an updated set of changes. It may also be used to add multiple sets of files to the package. For example, consider the situation where you have an application that is being updated which has some files installed in a private directory and other shared components installed in the Windows system directory. In this case, you could use the Update method to update the files relative to the private directory and then add the files relative to the system directory, using different default installation paths.

Another situation where the Update method would be used is when the package has been created to support multiple versions of a file. In this case, the Update method would be called to separately specify each collection of files. For example:

First the package could be created using the Create method, specifying Version1.0 as the reference directory and Version2.0 as the updated directory. Any changes in the files between these two directories would then be stored in the patch package. The Update method is then called, specifying Version1.1 as the reference directory and Version2.0 as the updated directory. Any changes between those versions would also be stored in the patch package.

When the package is applied on the target system, ActivePatch would determine which versions of the files are currently installed and would update them appropriately, depending on whether they are version 1.0 or version 1.1. However, keep in mind that the package would have to be created with the option to support multiple file versions; otherwise the Update method would simply replace the files already in the package.

Here is an example of how the Update method could be used to update a patch package:

Package1.FileName = "Widgets.pak"
Package1.Password = ""
Package1.Options = apMultipleVersions

On Error GoTo Failed
Package1.Create "C:\Project\Widgets\Version1.0", _
                "C:\Project\Widgets\Version2.0", , , _
                apRecursiveUpdate

Package1.Update "C:\Project\Widgets\Version1.1", _
                "C:\Project\Widgets\Version2.0", , , _
                apRecursiveUpdate

Exit Sub

Failed:
MsgBox Err.Description

Apply Method

The Apply method applies the specified patch package on the current system, updating the files to their current versions. There are two general options which may be used when applying a package: the option to automatically find missing files and the option to ignore missing files. As the package is applied, if an expected file is not found, ActivePatch may be instructed to search for the file using the standard Windows pathing rules. It will search the current directory, the default installation directory, the Windows directory, and those directories specified in the PATH environment variable. If the file cannot be found, an error is returned unless the option to ignore missing files has been specified.

During the patch application process, the Apply method has two distinct phases. The first stage is the validation phase, where the function finds each file in the package that is targeted for the specified platform and verifies that the original version of the file on the local system matches the files that were used to generate the package. For example, if version 1.0 of the Widgets.exe program is 147,932 bytes in length and has a checksum of 98A43F2h, ActivePatch makes sure that the Widgets.exe program on the local system is the same length and has the same checksum. If the local version of Widgets.exe is different, then something has changed this file and it is not considered safe to update. Note that this behavior can be overridden by specifying that the file should always be replaced when it is added to the package. If any file fails the validation phase, the patch application process stops, an error is returned, and no files are modified.

Once ActivePatch has determined that all files in the package can be safely updated, it begins the process of modifying, replacing, or removing the original versions of the files as needed. During this process, the original files are left intact so that if a failure occurs, the changes made to the system are automatically reversed out. If a file that is to be updated is locked by another process, a temporary version of the updated file is created and the locked file is replaced when the system is restarted.

Once the patch application process has finished, the data and instructions to reverse the patch are stored in a file specified when the Apply method is called.

Undo Method

The Undo method reverses the application of a package using the reverse patch (undo) file created during the application process. Multiple reverse patch files may be created, however it is important to keep in mind that multiple package applications must be undone in the reverse order that they were applied. For example, consider a package which was applied that updated a product from version 1.0 to 1.1, and then another package that was applied to update version 1.1 to version 1.2. To reverse the updates and return the user back to version 1.0, first the update to 1.2 must be reversed back to 1.1, and then the update from version 1.1 can be reversed back to 1.0.