ActivePatch Developer's Guide - Package Functions

The ActivePatch functions 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 functions 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.

To create an ActivePatch package, there are two functions that may be used, depending on the needs of the application: OpenPackage or CreatePackage.

OpenPackage

The OpenPackage function is used to open an existing package file, or create a new package file. When used to create a new package, the resulting package file is empty. The OpenPackage function returns a handle to the package which may be used with subsequent functions that are used to modify or examine the contents of the package. To close the package and release the handle, the ClosePackage function should be called.

CreatePackage

The CreatePackage function 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 the update options, which are used to control how the package is created. There are three key package options: whether the package should support multiple versions of files, whether when the package is applied it should search for missing files, and/or whether 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 patches 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 CreatePackage function 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. 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 file 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 that 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.

Once a package has been created, there are a number of functions which can be used to modify the contents of the package. These functions are UpdatePackage, StorePackageFile and DeletePackageFile.

UpdatePackage

The UpdatePackage function is similar to the CreatePackage function with the exception that instead of creating a new package, the package is updated with the specified files.

UpdatePackage 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 which are installed in the Windows system directory. In this case, you could use the UpdatePackage function 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 UpdatePackage would be used is when the package has been created to support multiple versions of a file. In this case, UpdatePackage would be called, separately specifying each collection of files. For example:

First the package could be created using the CreatePackage function, 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. Using the handle returned by CreatePackage, the UpdatePackage function 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 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 UpdatePackage function would simply replace the files already in the package.

StorePackageFile

The StorePackageFile function is used to store an individual file in a package rather than a collection of files. As with creating an individual patch file, a reference file and an updated version of the file is provided along with a set of options. To store only the differences between two files, both a reference and updated version of a file must be specified. However, it is possible to store the entire contents of a file by omitting the reference file.

This tells StorePackageFile that a previous version of the file does not exist and the complete file should be stored in the package. This is similar in concept to adding a file to a Zip archive. Another possibility is to specify a file as a reference file but omit the updated file name. This tells StorePackageFile that the file exists in the original version but does not exist in the updated version. In this case, information about the file is stored in the package but not the actual file data. When the package is applied, the file is removed from the target system.

One additional feature of using StorePackageFile is the ability to specify an alternate file name for the file on the target system. In the previous examples, the name of the files stored in the package are based on the file names as found in the specified directories. However, there may be cases where two versions of the same file have different names. For example, one file may be named Widgets10.exe and the later version may be named as Widgets20.exe. In this case, StorePackageFile can be used to store the differences between the two files, but when it is applied on the target system, the file name Widgets.exe would be used.

DeletePackageFile

The DeletePackageFile function is used to remove a specified file from the package. Note that this does not cause the file to be removed from the target system (the StorePackageFile function provides this functionality), but rather the file is removed from the package itself and as a result, is not updated on the target system. If the package has been created with support for multiple file versions, it is important that the file platform, checksum, and name be provided. Simply providing the file name is not sufficient since there may be multiple files with the same name in the package. It is also important to remember that the complete file path must be specified. In other words, if the file is stored in the package as "Programs\Widgets.exe", that is the name that must be specified. Simply specifying "Widgets.exe" would result in an error.

To display information about the package to a user, or to be able to determine what files are currently stored in the package, there are several functions available. These are FindFirstPackageFile, FindNextPackageFile, GetFirstPackageFile, GetNextPackageFile and GetPackageFileInformation.

FindFirstPackageFile, FindNextPackageFile

The FindFirstPackageFile and FindNextPackageFile functions are used to search the package for a specific file name, optionally targeted for a specific operating system platform. Wildcards may be used, and the functions can be used to recursively search through subdirectories in the package. The functions return information in a PACKAGE_FILE_INFORMATION structure, which provides details about the individual file, such as its size, compression ratio, attributes, checksum, and target operating system platform.

GetFirstPackageFile, GetNextPackageFile

The GetFirstPackageFile and GetNextPackageFile functions are used to return information about each file in the package. The functions return information in a PACKAGE_FILE_INFORMATION structure, which provides details about the individual file, such as its size, compression ratio, attributes, checksum and target operating system platform.

GetPackageFileInformation

The GetPackageFileInformation function returns information about a specific file in the package. As with the functions used to enumerate the contents of the package, the function returns information about the file in a PACKAGE_FILE_INFORMATION structure.

Once a package file has been created, it can be deployed and applied on the target system. There are two functions which may be used, ApplyPackage and UndoPackageApply.

ApplyPackage

This function 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 ApplyPackage function 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 match 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 ApplyPackage function is called.

UndoPackageApply

The UndoPackageApply function 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.