ActivePatch Developer's Guide - Creating Patch Files

The next step is to create the patch file based on the differences between the two versions of the file. You would call the CreatePatchFile function as follows:

// Create a patch file based on the differences between two
// versions of a database

LPCTSTR lpszPatchFile = _T("house.pat");    // patch file
LPCTSTR lpszPassword = _T("secret");        // patch file password
LPCTSTR lpszOldFile = _T("house1.mdb");     // original version of database
LPCTSTR lpszNewFile = _T("house2.mdb");     // modified version of database
BOOL bResult;

bResult = CreatePatchFile(lpszPatchFile,          // patch file name
                          PATCH_LEVEL_DEFAULT,    // patch level
                          0,                      // patch options
                          0,                      // patch expiration
                          0,                      // reserved
                          lpszPassword,           // patch password
                          lpszOldFile,            // original file
                          lpszNewFile,            // modified file
                          NULL,                   // callback function
                          0);                     // callback parameter

The first parameter to the CreatePatchFile function is the name of the patch file to create. This may be any legal file name, with any extension that you choose. In these examples, we will use the extension ".pat" to refer to individual patch files, and the extension ".pkg" to refer to patch packages. However, there are no requirements that you use these extensions in your own code, and ActivePatch does not make any assumptions about the contents of a file based on its extension.

The next parameter is the patch level that is to be used when creating the patch. This is a value between one and nine, with a value of zero specifying the default level. In most cases, it is recommended that you use PATCH_LEVEL_DEFAULT as the patch level, since this provides the optimum combination of patch generation speed and size reduction.

The next parameter is the patch options, which are detailed in the technical reference. A value of zero specifies the default options for the patch file.

The next parameter is the expiration period for the patch. A value of zero specifies that the patch never expires. If you wish to limit the number of days that the patch file will remain valid, you can specify that value here.

The next parameter is the password used to secure the patch file. This prevents the patch from being applied unless the same password is provided to the ApplyPatchFile function. If you do not want to require a password, then a NULL pointer or an empty string can be specified.

The next two parameters are the names of the reference (original) version of the file, and the updated version of that file.

The last two parameters are used to specify a function which is called during the patch creation process and an optional parameter to pass to that callback function. Because we are not using a callback function, a NULL pointer is passed as the function address and a value of 0 is passed as the callback parameter.

The CreatePatchFile function will return either a value of zero, which indicates failure, or a non-zero value which indicates that the patch file has been successfully created. If the function fails, the GetLastError function should be called immediately afterwards to determine the cause of the failure. To obtain a string which describes the error, you need to call the FormatMessage function. This function is part of the standard Windows API, and can be used like this:

if (bResult == FALSE)
{
    // Unable to create the patch file; to obtain a description of
    // the error, use the Windows API functions GetLastError and
    // FormatMessage

    HMODULE hModule;
    DWORD dwError;
    DWORD dwLength;
    LPVOID lpMessage;
    TCHAR szError[128];

    dwError = GetLastError();
    hModule = GetModuleHandle(_T("APATCH.DLL"));
    dwLength = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                             FORMAT_MESSAGE_FROM_HMODULE |
                             FORMAT_MESSAGE_FROM_SYSTEM |
                             FORMAT_MESSAGE_IGNORE_INSERTS,
                             hModule,
                             dwError,
                             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                             (LPTSTR)&lpMessage,
                             0,
                             NULL);

    if (dwLength > 0)
    {
        lstrcpyn(szError, (LPTSTR)lpMessage, cchError);
        LocalFree(lpMessage);
    }
    else
    {
        TCHAR szUndefined[128]; 
        wsprintf(szUndefined, _T("Undefined library error (0x%08lx)"),
                           dwError);
        lstrcpyn(szError, szUndefined, cchError);
    }

    MessageBox(NULL, szError, NULL, MB_OK|MB_ICONEXCLAMATION);
    return;
}

Of course, if there are a number of different functions in your application in which you would like to display error messages, this code could be written as a separate function. As a side-note, code similar to this can also be used to display error messages for all standard Windows API functions which set the last error code on failure.

If the CreatePatchFile function succeeds, it will return a non-zero value and, using this example, the file house.pat has been created. The resulting patch file is approximately 50 Kb in size and 90% smaller than a compressed version of the same database.