ActivePatch Developer's Guide - Creating Patch Packages

Where a patch file is designed to update a single file, a patch package is a collection of updates which are applied on a target system. One important aspect of creating a patch package is that the patch is being created for distribution to a target system, so the directory structure and files must match how they are installed on the target system, not the development system. The simplest way to do this is to simply install the original version of the product or files on the system as though you were the end user. Then, install the updated version of that product in a different directory, again as if you were the end user. These two directories can then be used as the reference and update directories on which the patch package is generated. For the examples used with the package functions, we'll use two groups of files which represent two different versions of a product. The directory version1 will contain subdirectories and files in the original version, and the directory version2 will contain the updated files.

To create the patch package, we'll use the CreatePackage function as follows:

// Create a package file based on the differences between the files
// in two directories

HPACKAGE hPackage;
LPCTSTR lpszPackageFile = _T("update.pkg");       // package file
LPCTSTR lpszPassword = _T("secret");              // package password
LPCTSTR lpszOldDir = _T("\\product\\version1");   // original version
LPCTSTR lpszNewDir = _T("\\product\\version2");   // modified version
DWORD dwOptions;
DWORD dwFlags;
DWORD dwPlatforms;

// The default options used when applying the package
dwOptions = PACKAGE_OPTION_IGNORE_MISSING |
            PACKAGE_OPTION_IGNORE_FILETIME |
            PACKAGE_OPTION_OVERWRITE;

// The options used when creating the package
dwFlags = PACKAGE_UPDATE_RECURSIVE;

// The platforms the package may be applied on
dwPlatforms = PACKAGE_FILE_PLATFORM_DEFAULT;

hPackage = CreatePackage(lpszPackageFile,    // package file name
                         dwOptions,          // package options
                         dwFlags,            // package creation flags
                         dwPlatforms,        // package platforms
                         lpszPassword,       // package password
                         lpszOldDir,         // original files
                         lpszNewDir,         // modified files
                         NULL,               // install directory
                         NULL,               // exclude mask
                         NULL,               // callback function
                         0);                 // callback parameter

In this example, the first thing that we do is assign the default package options to the dwOptions variable. If no options are explicitly specified when the package is being applied, these are the options that will be used. In this case, three options are specified. The first option is PACKAGE_OPTION_IGNORE_MISSING which is useful when the target system may not contain all of the files in the package. For example, most installations will allow you to specify whether or not you want a minimal install, a complete install or a custom install in which the end-user may select certain components which are to be installed. Because there is no way for you to know ahead of time which type of installation a user has selected, you simply include all files in the package. This option then tells ActivePatch to not return an error if the file doesn't exist because the user may not have chosen to install it. The second option is PACKAGE_OPTION_IGNORE_FILETIME and serves the same function as in the patch example. The third option is PACAKGE_OPTION_OVERWRITE which specifies that ActivePatch should always make sure that updated files are overwritten, even if the file is new and not expected to exist on the target system, or if the file has the read-only file attribute set.

The dwFlags variable is then assigned the options to be used during the creation of the package. These options are the same as used with the UpdatePackage function. In this case, we specify a single option, PACKAGE_UPDATE_RECURSIVE which tells ActivePatch to recursively search any subdirectories in the reference and update directories. For a complete list of the flags that can be specified, consult the technical reference.

The dwPlatforms variable is set to identify the target platforms on which the package may be installed. This is used to control the application of a package so that the updates can only be applied on the appropriate platform. The value used in this case is PACKAGE_FILE_PLATFORM_DEFAULT, which specifies that the package may be applied on any system.

The remaining parameters which are passed to CreatePackage are similar to those used with the CreatePatchFile function, with two notable exceptions. The eighth parameter specifies the name of the default installation directory, and the ninth parameter specifies a wildcard mask used to exclude certain files from the package. In most cases when creating a package, the installation directory will simply be passed as a NULL pointer or empty string. This indicates that the package files will be updated in the current working directory on the target system, or in an installation directory specified at the time that the package is applied. It is not recommended that you specify an installation path unless you are certain that all users who apply the package have the files installed in the same location. If you wish to exclude certain files from the package, this can be done by passing a null-terminated string which specifies a wildcard that matches the files to exclude. Multiple wildcards may be specified by separating them with a semicolon. For example, to exclude all files with a .tmp and .bak extension, the exclude mask string would be "*.tmp;*.bak"

If the CreatePackage function succeeds, it will return a handle to the package which may be used in subsequent calls to the ActivePatch package API. Make sure that you call the ClosePackage function with this handle before your application terminates. If the function fails, it will return the value INVALID_PACKAGE_HANDLE and the GetLastError function should be called to determine the cause of the error.