Dear John, How Do I... Simulate Unsigned Integers?

Unfortunately, Visual Basic does not support unsigned 16-bit integers, a data type often encountered in API calls. But don't despair—there are ways to compensate for this lack.

Visual Basic's integer data types come in three distinct flavors: Long, Integer, and Byte. Long variables are 32-bit signed values in the range -2,147,483,648 through +2,147,483,647. The most frequently used of these three data types is Integer, which stores 16-bit signed integer values in the range -32,768 through +32,767. Byte variables hold unsigned 8-bit numeric values in the range 0 through 255. Notice that the only unsigned integer data type is Byte.

Unsigned 16-bit integers are useful in many API function calls. You can go ahead and pass signed integer variables to and from these functions instead, but you must develop a mechanism to deal with negative values when they show up. There are several approaches to simulating unsigned 16-bit integers, and I'll cover two of the best.

NOTE

If you are compiling your program using the native code compiler, be aware that you can turn off integer overflow detection. This allows incorrect integer computation in some cases, so you should carefully consider your application before turning off this option. On the positive side, turning off integer overflow detection allows faster integer math calculations and, for certain types of calculations, effectively allows unsigned integer results.

Transferring to and from Long Variables

In some cases, you can manipulate unsigned integers in the range 0 through 65,535 by storing them in Long integers. When it comes time to assign the 16bit values to a signed Integer variable that will simulate an unsigned 16-bit variable, use the following calculation:

intShort = (lngLong And &H7FFF&) - (lngLong And &H8000&)

Here intShort is a signed Integer variable to be passed to the API, and lngLong is the Long variable containing the stored value to be passed. The calculation uses the logical And operator and hexadecimal-based bit masks to execute bitwise operations to convert the 32-bit value to a 16-bit unsigned value.

To store the value of a signed integer that has been used to simulate an unsigned 16-bit integer as a Long integer (the inverse of the calculation just shown), use this calculation:

lngLong = intShort And &HFFFF&

Be aware that the 16-bit unsigned value (intShort), although stored in a 16-bit signed Integer variable, might be interpreted as a negative value if you print or calculate with it. Use these two calculations to convert the values just before and just after calling API functions that expect a 16-bit unsigned integer. Work with the Long integer version (lngLong) of the unsigned integers in your code, and pass the Integer version (intShort) of the value to API functions.

Packing Unsigned Byte Values Using Data Structures

Visual Basic doesn't have a Union construct as C does, but you can simulate the construct's functionality by copying bytes between UDT structures using the LSet statement. This makes it easy to pack and unpack unsigned Byte values into and out of a signed integer. The following code fragments demonstrate this technique:

Option Explicit

Private Type UnsignedIntType
    lo As Byte
    hi As Byte
End Type

Private Type SignedIntType
    n As Integer
End Type

These two UDT structures define storage for 2 bytes each. Although the memory allocation is not overlapping, as would be true in a C union, the binary contents can be shuffled between variables of these types, as the next block of code demonstrates:

Private Sub Form_Click()
    `Create variables of user-defined types
    Dim intU As UnsignedIntType
    Dim intS As SignedIntType
    `Assign high and low bytes to create integer
    intU.hi = 231
    intU.lo = 123
    `Copy binary data into the other structure
    LSet intS = intU
    Print intS.n, intU.hi; intU.lo
    `Assign integer and extract high and low bytes
    intS.n = intS.n - 1  `Decrement integer for new value
    `Copy back into the other data structure
    LSet intU = intS
    Print intS.n, intU.hi; intU.lo
End Sub

If you put these two code fragments in a form and click on it, the signed integer value (-6277) and the two byte values (231 and 123) on which it is based are printed on the form. After conducting an operation on the integer—in this case, subtracting 1—the result of the reverse operation is also printed: the new signed integer value (- 6278) and the high and low bytes (231 and 122) of the integer. This is accomplished by first declaring two variables, intU and intS, using the declared UDT structure definitions. The intU.hi and intU.lo bytes are used to assign values to the high and low bytes of the intU variable. The binary contents of intU are then copied into intS using LSet, and the resulting signed integer is printed out. Finally, the integer value in intS is decremented to provide a value different from the original, and intS is copied back into intU to show how a signed integer can be split into two unsigned byte values.

By adding text boxes and labels to your form, it's easy to transform the code fragments into a working integer-byte calculator, as shown in Figure 3-1.

Figure 3-1. A signed integer comprising two unsigned bytes.

Note that LSet can be used to copy any binary contents of one UDT structure to another. This provides a simple way to treat the binary contents of memory as different types of data.

WARNING

In Microsoft Windows 95 and Microsoft Windows NT, the elements in your UDT structures might not line up in memory exactly as you'd expect. This is because each element is aligned on a 4-byte boundary, with extra padding bytes inserted to accomplish the alignment. When using LSet to move data from one UDT structure to another, experiment to verify that the bytes end up where you want them to go. Be careful!

SEE ALSO