| ActivePatch Developer's Guide - Library Overview | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
ActivePatch can be used with a wide variety of programming languages and software development tools for Windows. The majority of the library functions use simple data types such as 16-bit and 32-bit integers, and null-byte terminated strings. To determine if your development language is capable of using the ActivePatch DLL, it should support all of the following features:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Data Type Representations | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Because various languages handle data types in different ways, the ActivePatch library has been designed to primarily use basic data types such as integers and strings. The following is a list of numeric data types that are used, along with their C and Visual Basic equivalents.
One problem that is frequently encountered when converting function definitions from C or C++ to other languages is the size of the integer data type. For example, default integer size for Visual Basic is 16-bits, even on 32-bit platforms. Also, some languages do not support unsigned integer types. In this case, as with Visual Basic, the signed type should be used instead. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Handles | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Throughout the documentation for the ActivePatch functions, you will see references to "handles". Simply put, a handle is an unsigned integer value that is used to represent some object in memory. The actual value of the handle is not important and may refer to the memory address of a specific object, or it may be an index into a table of objects. When a handle is created, its value is unique for the life of each object created by the process. It is important that an application never make assumptions about the specific value of a handle. Handle values may be reused for objects that have been destroyed, and there is no guarantee that handle values are assigned in any particular order. In ActivePatch, handles are used to refer to patch packages. The package handle, defined as an unsigned integer type called HPACKAGE, is returned by those functions which create a new patch package, or open an existing package. During the time that the package file is open, the package handle is used to refer to this file. When the application closes the package, the handle is destroyed. The value of an ActivePatch handle varies between processes. The only constant is the value represented by INVALID_PACKAGE_HANDLE, which indicates that a given handle does not represent an open package file. It is important to note that the handles used by functions in the ActivePatch library are not Windows handles. In other words, you cannot use functions like DuplicateHandle and CloseHandle with package handles. More importantly, they are not file handles and cannot be used with any of the file-related Windows API functions. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Boolean Data | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Boolean parameters present a special problem for two reasons. Firstly, the data types used to represent boolean values frequently vary between languages. Secondly, different languages represent the values true and false differently. Boolean parameters (declared as BOOL in the function prototypes) should always be passed as 32-bit signed integers. If you are passing a boolean parameter to a function, then false should be represented by a value of zero and true as a non-zero value (typically a value of one). When writing code that checks a boolean flag, or tests a boolean return value from a function, it is recommended that you test against a value of zero. For example, consider the following code in Visual Basic: Dim bResult As Long
Dim pfInfo As PATCH_FILE_INFORMATION
bResult = GetPatchFileInformation(strPatchFile, strPassword, _
PATCH_INFO_REFERENCE_FILE, _
pfInfo)
If bResult = True Then
' The patch file and password is valid
Else
' The patch file or password is not valid, check
' the value of Err.LastDLLError for the error code
End If
In this example, even if the function GetPatchFileInformation was successful, the program would always believe that it failed because of the explicit test against the value True. This is because the function returns a value of 1 to indicate success, but Visual Basic defines True as 1. Instead, the code should be written as: Dim bResult As Long
Dim pfInfo As PATCH_FILE_INFORMATION
bResult = GetPatchFileInformation(strPatchFile, strPassword, _
PATCH_INFO_REFERENCE_FILE, _
pfInfo)
If bResult <> 0 Then
' The patch file and password is valid
Else
' The patch file or password is not valid, check
' the value of Err.LastDLLError for the error code
End If
In summary, the rule of thumb for dealing with boolean parameters is that they should always be 32-bit integer values, and you should always compare the boolean against a value of zero, never against a predefined constant. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| String Data | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
String parameters can also present a problem when calling functions from languages other than C and C++. Different languages tend to have different internal representations of how string data is stored. The convention used by the ActivePatch library is that a string is an array of characters, terminated by a null-byte (a character with an ASCII value of zero). The length of the string is not stored in the string data itself, and by definition, a string cannot contain embedded nulls. To use functions which require string parameters, the language must be capable of converting between its native string data type and the null-terminated character array expected by the ActivePatch functions. For example, Object Pascal provides the StrPCopy function. Note that Visual Basic provides implicit conversion between its native string type and null-terminated strings when a string is passed by value (ByVal) instead of by reference. Another important consideration when passing string parameters is how the string is being used by the function. In most cases, the string is provided as input to the function (for example, as the hostname or address of a server to establish a connection with). However, in some cases the string is passed to the function as an output buffer, which the function copies data into. For example, the GetPackageProperty function stores the value of a package property into a string parameter. A Visual Basic programmer may write code that looks like this: Dim strProductName As String
Dim nLength As Long
nLength = GetPackageProperty(hPackage, _
PACKAGE_PROPERTY_PRODUCT_NAME, _
strProductName, _
255)
Although this code looks correct, it will invariably result in a general protection fault or some other unpredictable error. The problem is that although the strProductName variable has been defined, no memory has been allocated for it. There are two ways that this can be done in Visual Basic. One is to declare the string as fixed-length, such as: Dim strProductName As String * 256
Dim nLength As Long
nLength = GetPackageProperty(hPackage, _
PACKAGE_PROPERTY_PRODUCT_NAME, _
strProductName, _
255)
The other is to dynamically allocate memory for the string using the String function, such as: Dim strProductName As String
Dim nLength As Long
strProductName = String(256, 0)
nLength = GetPackageProperty(hPackage, _
PACKAGE_PROPERTY_PRODUCT_NAME, _
strProductName, _
255)
One final consideration is that string data that is returned by the function will be null-terminated. For those languages that do not terminate strings with null-bytes, it may be necessary to remove the trailing null-byte. The complete example would be written in Visual Basic as: Dim strProductName As String
Dim nLength As Long
strProductName = String(256, 0)
nLength = GetPackageProperty(hPackage, _
PACKAGE_PROPERTY_PRODUCT_NAME, _
strProductName, _
255)
' Trim string up to the terminating null-byte
strProductName = Left(strProductName, _
InStr(strProductName, Chr(0)) 1)
If you are unsure of how your language handles null-terminated strings, we recommend that you review the languages technical reference for information on how to call native Windows API functions. Since the Windows API also uses null-terminated strings, that same information can be used to determine how to call functions in the ActivePatch library. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Unicode Support | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Unicode is a multi-language character set designed to encompass virtually all of the characters used with computers today. Unicode characters are represented by a 16-bit value, and differs from other character sets in two important ways. First, unlike the traditional single-byte (ANSI) character sets, Unicode is capable of representing significantly more characters in a variety of languages. Second, unlike multi-byte character sets (where some characters may be one byte in length, while others may be two bytes), the characters are fixed-width, which makes them easier to work with. Windows support for Unicode varies according to the platform. There is no Unicode support for 16-bit versions of Windows, or for 16-bit applications running on 32-bit platforms. On 32-bit platforms, Unicode is fully supported under Windows NT. However, support is extremely limited under Windows 95 and Windows 98. The ActivePatch library supports both the single-byte and Unicode character sets under Windows NT/2000/XP, and Windows 95, 98 and Windows ME. This is done by having two versions of each function that either expects a string as an argument (including those functions which pass structures that contain strings) or returns the address of a string. The version of the function that expects a single-byte character set has a suffix of "A" (ANSI), while the function which expects the Unicode character set has a suffix of "W" (wide). Note that no suffix is used with those functions which only expect numeric parameters and return numeric values. For example, consider the GetPackageProperty function mentioned in the previous section. If you looked at the list of exported functions in the library, you would see two functions exported, GetPackagePropertyA and GetPackagePropertyW. In C and C++, which function is called actually depends on how the application is being built. That is, if your application is built to use Unicode (in other words, the UNICODE macro is defined and you are linking with Unicode versions of the standard libraries), then the GetPackagePropertyW function will be used instead of the GetPackagePropertyA. In other languages, you may have to explicitly declare which version of the function you wish to use. In Visual Basic, for example, the Alias keyword must be used with the function declaration to specify the correct name. When developing software for the 32-bit Windows platforms, whether or not to use Unicode is an important consideration. If you are developing an application specifically for Windows NT/2000/XP, then Unicode support is an option. However, if your application must run on Windows 95/98 in addition to NT, then it is recommended that you use the ANSI character set. Another alternative is to build two versions of the software, one that uses Unicode and is specific to Windows NT, and another that uses ANSI. One final consideration when using Unicode is that, regardless of the character set, some of the ActivePatch functions use byte arrays, not character strings. This can create problems when reading and writing Unicode string data. For example, consider the StorePackageResource function which is used to store application specific metadata in a patch package. Because character strings and byte arrays are essentially identical when using the ANSI character set, a C/C++ programmer may try to write code such as this: LPTSTR lpszData;
BOOL bResult;
int cchData;
lpszData = _T("This is a test, this is only a test");
cchData = lstrlen(lpszData);
bResult = StorePackageResource(hPackage, uID,
PACKAGE_RESOURCE_STRING,
lpszData, cchData);
This would work as expected until the programmer tries to compile for the Unicode character set. The problem is that the Unicode string is no longer an array of bytes, but is now an array of 16-bit (short) integers. The string must be converted from Unicode to a byte array before passing it to the StorePackageResource function. To do this, the WideCharToMultiByte function can be used as follows: LPTSTR lpszData;
LPBYTE lpBuffer;
BOOL bResult;
int cchData;
lpszData = _T("This is a test, this is only a test");
cchData = lstrlen(lpszData);
#ifdef UNICODE
lpBuffer = (LPBYTE)_alloca((cchData + 1) * 2);
if (!lpBuffer) {
// Unable to allocate memory
return;
}
WideCharToMultiByte(CP_ACP, 0,
(LPCWSTR)lpszData, cchData,
(LPSTR)lpBuffer, ((cchData + 1) * 2),
NULL, NULL);
#else
lpBuffer = (LPBYTE)lpszData;
#endif
bResult = StorePackageResource(hPackage, uID,
PACKAGE_RESOURCE_STRING,
lpBuffer, cchData);
Note that the type of characters that are being converted may also present a problem to the developer. In this example, the string is easily converted because it is composed only of characters that are part of the basic ASCII character set. However, when converting a string that contains international characters (such as accented vowels) the conversion may result in unprintable characters. For additional information, check your programming language's technical reference for issues with regards to localization and the use of Unicode. |
||||||||||||||||||||||||||||||||||||||||||||||||||||