User-Defined Data Type Workaround

Team-Fly    

 
eMbedded Visual Basic: Windows CE and Pocket PC Mobile Applications
By Chris Tacke, Timothy Bassett
Table of Contents
Chapter 9.  Harnessing the Windows CE API


As I mentioned earlier, eVB is somewhat hindered even in the API calls it can make because it doesn't support callback functions or User-Defined Types (UDTs). While there really isn't anything to be done about the lack of callback support, you can work around the lack of UDT support.

Note

We will be doing a lot of bit manipulation throughout this section and therefore will be looking at most numbers in hexadecimal format. Numbers in hexadecimal are prefixed in VB and eVB with &H, so 255 in hexadecimal would be &HFF.

Bit manipulation can be confusing, especially if you've never worked at a binary level before, so I highly recommend researching decimal, hexadecimal, and binary numbers if you're not comfortable with it.

The source for this section is available from www.samspublishing.com. Although you can easily cut and paste the code from this section and it will work fine, understanding what the code is doing will allow you to take advantage of the techniques in other situations.


User-Defined Data Types

You first need to understand that simply put, a UDT is a single, contiguous block of memory that contains linear data.

For example, let's look at the RECT UDT, which will be used in the ClipCursor sample later. A RECT is the definition of a rectangle. If you look in the eMbedded Tools Help, a RECT is defined like this:

 typedef struct RECT {         LONG left;         LONG top;         LONG right;         LONG bottom; } RECT; 

Because a RECT can be used in only eMbedded Visual C++, the help file gives the C++ declaration. If it were written in Visual Basic 6, it would look like this:

 Public Type RECT         Left As Long         Top As Long         Right As Long         Bottom As Long End Type 

If you looked at this in memory, there would be 16 contiguous bytes (a Long is 32 bits, so it takes up 4) like this:

Bytes 14 Bytes 58 Bytes 912 Bytes 1316
Left Top Right Bottom

To make things even trickier, the bytes aren't in the order you would expect, because x86 type processors (386, 486, Pentium, Athlon, and so on), and therefore all Windows desktop operating systems, use what's known as Little Endian architecture . This means that the little, or least significant bits are stored first. For example, if you had the short (VB Integer) number &H9832, it would be stored in memory like this:

Byte 1 Byte 2
&H32 &H98

It works similarly with Longs, so &H12345678 would be stored in memory like this:

Byte 1 Byte 2 Byte 3 Byte 4
&H78 &H56 &H34 &H12

Notice I said that all Windows desktop operating systems use Little Endian architecture. This is simply because the processors used for desktop PCs, and servers for that matter, dictate this. It should also hold true for Windows CE devices, providing the processor uses Little Endian architecture.

Note

Macintosh architecture is Big Endian, so the large end is stored first, and this is why there are no Windows clones for Mac or MacOS for x86 machines.


Looking back at the RECT UDT, if you define a RECT with the following:

 Left = 100 Top = 50 Right = 1200 Bottom = 175 

It would look like this in memory:

Bytes 14 Bytes 58 Bytes 912 Bytes 1316  
Left Top Right Bottom  
Decimal 100 50 1200 1175
Hex &H00000064 &H00000032 &H000004B0 &H00000497
Little Endian &H64000000 &H32000000 &HB0040000 &H97040000

Strings

Now you know what a UDT looks like in memory, but how does it help you work around eVB's lack of UDT support? Well, the key is that a String is also a single, contiguous block of memory that contains linear data. What this means is that if you place the data from a UDT into a String, you can pass it as a parameter to an API and the receiver will handle it all the same. You simply need to figure out how to transfer the binary data into a binary string.

You need to be aware of one more thing. PocketPCsand, in fact, all Windows CE devicesuse Unicode for storing strings, which means that each character takes up two bytes, not just one. The reason behind this is that many languages of the world can't be fully represented in 256 characters, but 2 bytes allows for more than 65,000 characters .

What this means is that you can't use standard character manipulation functions to handle binary strings. Left(strMyString, 1) would return 2 bytes, and you need to be able to manipulate one byte at a time to achieve the Little Endian format.

Fortunately, you have binary versions of the String methods Mid, Left, Right, and Asc, which are MidB, LeftB, RightB, and AscB, respectively, and they all work just like their non-binary counterparts.

Getting a Byte Value

To build a binary string in Little Endian format, the first thing you need to be able to do is to extract a single byte from your numeric data. If you consider the previous example and you have &H12345678, you need to extract each byte right to left and append them to a string left to right.

The first step is to isolate the byte you're after. This can be accomplished by simply doing a bitwise And with a number that has only the desired byte's bits turned on. This number is called a mask. In essence, this means if you want to extract &H34 from your number, you would perform the following:

 &H12345678 And &H00FF0000 

This results in &H00340000. The only exception is that the most significant bit, position 31 in our example, isn't used for holding a value in Windows, but is used instead to hold the sign. If the bit is on, the number is negative, if it's off, the number is positive. This means that if you want the first byte, &H12 in the example, you turn on all bits in that byte except bit 7 (which is bit 31 in our 4 byte number). This means you And it with &H7F000000.

You can then divide the resulting number by a number with all the bits of lower significance (those to the right of the desired byte) turned on. So in this example of getting the &H34, you would do the following:

 &H00340000 / &H0000FFFF 

This results in &H00000034 or simply &H34.

So you need a function that performs this. You can make a single function that works both for Integers or Longs, and it looks like the code in Listing 9.11.

Listing 9.11 Extracting the Value at a Specific Byte Position in a Binary String
 Public Function GetByteValue(Number As Variant, BytePos As Integer) As Long     Dim mask As Long     On Error Resume Next     ' cannot check byte positions other than 0 to 3     If BytePos > 3 Or BytePos < 0 Then Exit Function     If BytePos < 3 Then         ' build a mask of all bits on for the desired byte         mask = &HFF * (2 ^ (8 * BytePos))     Else         ' the last bit is reserved for sign (+/-)         mask = &H7F * (2 ^ (8 * BytePos))     End If     ' turn off all bits but the byte we're after     GetByteValue = Number And mask     ' move that byte to the end of the number     GetByteValue = GetByteValue / (2 ^ (8 * BytePos)) End Function 

Now to get our &H34 value out of &H12345678, you could call GetByteValue with a BytePos of 2 (you wrote the function zero-based , and you count from the right) like this:

 MsgBox(GetByteValue(&H12345678, 2)) 'remember we've number 0 to 3 

And you would get a MessageBox with 52, the decimal equivalent of &H34, as its text.

Building a Binary String

Now that you have a function to pull the byte value at a specific position, you need another function that takes the Long or Integer, steps through its bytes, and builds a binary string.

If you could strongly type the variable, you could just check the length in the function, but because all eVB variables are really Variants trying to get the correct length of the variable is unreliable. Instead you'll need to tell the function how long the input number really is.

To make things a little more readable in the calling code, you can declare constants for the only two values you'll ever use, 2 for Integers and 4 for Longs, like this:

 Public Const CE_INTEGER = 2 Public Const CE_LONG = 4 

The function itself is quite short. The major work is done in a For...Next loop that just iterates through the bytes of the passed in number and appends them to the binary string.

One thing you must do before you start building the string is to handle negative numbers. Negative numbers are easily recognizable because, as I mentioned before, the most significant byte is turned on. In fact, a negative number is stored in memory as the binary complement of the number's absolute value, plus 1. A binary complement is achieved by reversing the values of all the bits in a number, turning off those that are on and vice versa. This is done easily by Xoring the number with a mask number with all bits on.

Look at the number 10 as an example. First, you need the absolute value, which is 10 (&H0A, or 00001010 in binary). We get the complement by Xoring with &HFF to get 11110101 in binary and then add 1 to get 11110110 or &HF6.

"10" in hex &H0A
"10" in binary 0000 1010
Mask with &HFF 1111 1111
Xor result 1111 0101
Add 1 1111 0110
Back to hex &HF6

To make things a bit more complex, if you try Xoring the most significant bit of a Long, you get an overflow error, so you have to Xor it as you build the string so that you're Xoring only a single byte.

The resulting function, ToBinaryString, looks like Listing 9.12.

Listing 9.12 Iterating Through the Bytes of an Integer or Long and Returning a Binary String Equivalent
 Public Function ToBinaryString(Number As Variant, Bytes As Integer) As String     Dim i As Integer     Dim bIsNegative As Boolean     ' cannot check byte positions other than 0 to 3     If Bytes > 4 Or Bytes < 1 Then Exit Function     ' If the number is negative, we need to handle it last, _     '  so we'll set a flag     If Number < 0 Then         bIsNegative = True         ' get the absolute value         Number = Number * -1         ' get the binary complement (except the most sign. bit)         Number = Number Xor ((2 ^ (8 * Bytes - 1)) - 1)         ' add one         Number = Number + 1     End If     ' start at the least significant bit (0) and work backwards     For i = 0 To Bytes - 1         If i = Bytes - 1 And bIsNegative Then             ' If the number is negative we must turn on the most             '  significant bit and then and append it to the string             ToBinaryString = ToBinaryString _                     & (ChrB(GetByteValue(Number, i) + &H80))         Else             ' just append the byte to our string             ToBinaryString = ToBinaryString & ChrB(GetByteValue(Number, i))         End If     Next i End Function 

Converting from a Binary String

Now that you can generate a binary string from a number, you can call any API that has UDTs that contain Integers or Longs as well as Strings, but if you need to retrieve a value set by an API in a UDT, you will need ToBinaryString's complementary function.

Here, you can reliably check the length of the variable because it has been strongly typed by the C++ API, so you only need to pass the function the binary string.

Again, this is just reverse engineering the ToBinaryString function. We step through the string, pulling each character (which is actually a byte), determining its numeric value, and adding it to the resulting number. Remember, the rightmost character contains the most significant, or sign, bit, so you have to check for it and if it's on, you have to Xor each byte with &HFF, add one to get the absolute value of the actual number, and then multiply by 1 to make it negative.

Listing 9.13 shows my implementation of FromBinaryString.

Listing 9.13 Iterating Through a Binary String's Bytes and Converting It Back to a Usable Number Format
 Public Function FromBinaryString(BString As String) As Variant     Dim i As Integer     Dim bIsNegative As Boolean     bIsNegative = False     ' start at the end of the string and work backwards     For i = LenB(BString) - 1 To 0 Step -1         If i = LenB(BString) - 1 And (AscB(MidB(BString, i + 1, 2)) _                   And &H80) Then             ' check the signigicant bit             ' If it's negative, set a flag             bIsNegative = True         End If         If bIsNegative = True Then             ' extract the binary complement of the byte             FromBinaryString = FromBinaryString + _                 ((AscB(MidB(BString, i + 1, 2)) Xor &HFF) * (2 ^ (8 * i)))         Else             ' extract the byte             FromBinaryString = FromBinaryString + _                 AscB(MidB(BString, i + 1, 2)) * (2 ^ (8 * i))         End If     Next i     ' if it is supposed to be negative, make it so     If bIsNegative Then         ' Subtract one         FromBinaryString = FromBinaryString + 1         ' make it negative         FromBinaryString = FromBinaryString * -1     End If End Function 

Team-Fly    
Top
 


eMbedded Visual BasicR. WindowsR CE and Pocket PC Mobile Applications
eMbedded Visual BasicR. WindowsR CE and Pocket PC Mobile Applications
ISBN: N/A
EAN: N/A
Year: 2001
Pages: 108

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net