Bring Your Hatchet

Dealing with pointers in Basic is kind of like getting in a hatchet fight without a hatchet. If youíre going to hack into places where youíre not supposed to go, youíd better arm yourself appropriately. The weapons of the well-equipped Basic pointer hacker are these: CopyMemory, VarPtr, StrPtr, and ObjPtr.

Of these, CopyMemory is the weapon of choice when you want to chop through Basic type limitations. The sidebar on the following page tells the bizarre history of CopyMemory, which isnít really named CopyMemory. Here, weíre more interested in what it can do. For example, if youíve ever tried manipulating bit fields in Visual Basic, you know that Basicís lack of unsigned types and strong typing can make simple operations difficult. Hereís how you have to write a simple function that extracts the low Integer of a Long:

Function LoWord(ByVal dw As Long) As Integer
    If dw And &H8000& Then
        LoWord = dw Or &HFFFF0000
    Else
        LoWord = dw And &HFFFF&
    End If
End Function

Itís not particularly readable, but as weíll see in Chapter 5, it is efficient. We can make this code simpler with CopyMemory. Ignore Basicís picky requirements and simply blast the bits you want to wherever you want them:

Function LoWord(ByVal dw As Long) As Integer
    CopyMemory LoWord, dw, 2
End Function

To see exactly how this works, letís pretend for a moment that CopyMemory is written in a dialect of Basic that doesnít yet exist. It looks something like this:

Sub CopyMemory(anyDestination As Any, anySource As Any, _
               ByVal c As Long)

Of course thereís not really an Any type in Basic, and most of the time you wouldnít want a feature so dangerousóalthough itís nice to have it as an out in API functions. The first parameter of CopyMemory is a ByRef parameter (the address) of a variable of any type that youíll write some bytes to. The second parameter is the ByRef variable youíll copy from. The third parameter is the number of bytes to copy. If your finger slips and you type 20 instead of 2 in your CopyMemory call, nobody will complain until crash time.

What good is a LoWord function without a HiWord function? But this one gets a little more complicated.

CopyMemory: A Strange and Terrible Saga

Hereís the long, strange story of how the Win32 function for copying raw memory came to be called CopyMemory, even though thereís no such function in Visual Basic or in the Windows API.

It started when I first began searching for the Win32 equivalent of the Win16 hmemcpy function for use in Visual Basic version 4. No such thingónot even a note that the function might be obsolete. ButÖ

The closest I could come up with was the CopyMemory function, which has exactly the same arguments and is documented the same as the old hmemcpy. Unfortunately, despite what you might read in Win32 documentation, there is no such thing as CopyMemory. You can search all the 32-bit DLLs with the DumpBin utility, but you wonít find any DLL containing CopyMemory. ButÖ

If you search carefully through the Win32 C include files, youíll turn up the following in WINBASE.H:

#define CopyMemory RtlCopyMemory
#define MoveMemory RtlMoveMemory
#define ZeroMemory RtlZeroMemory

This C equivalent of an alias indicates that CopyMemory is another name for a function called RtlCopyMemory. Donít ask why; just check for RtlCopyMemory in KERNEL32.DLL. Again, nothing. More sleuthing in the Win32 include files reveals the reason. WINNT.H contains something like this:

#define RtlCopyMemory(dst, src, len) memcpy(dst, src, len)

In other words, RtlCopyMemory is an alias for the C memcpy function, but you canít use memcpy or any other C library function from Basic. The documenta-tion is simply lying when it claims that CopyMemory is a Windows function rather than a C function. If itís not exported from a DLL, you canít call it. ButÖ KERNEL32.DLL does contain an entry for RtlMoveMemory. If you check the Win32 documentation, youíll see that MoveMemory does the same thing as CopyMemory except that it handles overlapped memory in a different fashion. I canít imagine a situation in which a Basic programmer would be copying overlapped memory. No reason not to use MoveMemory instead. The name CopyMemory seemed more intelligible than hmemcpy or MoveMemory, so I used this alias for both 16-bit and 32-bit versions:

#If Win32 Then
Declare Sub CopyMemory Lib "KERNEL32" Alias "RtlMoveMemory" ( _
    lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)
#Else
Declare Sub CopyMemory Lib "KERNEL" Alias "hmemcpy" ( _
    lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)
#End If

The Windows API type library has an equivalent (or almost equivalent) CopyMemory function.

That explains why I used CopyMemory, but why does everybody else use it? Because I sent a copy of my sidebar to an internal alias at Microsoft, and someone who read it decided it would make a good Knowledge Base article. I agreed to let them use it if they mentioned it was an excerpt from my book. Good advertising, I thought. Ever since then Iíve read articles and heard speakers at the VBITS conference talking about CopyMemory as if it really existed. And none of them mention my book as the source. So donít be fooled by false advertising. If they talk about RtlMoveMemory, they figured it out on their own. If they talk about CopyMemory, they got it (perhaps without knowing) from me.

Function HiWord(ByVal dw As Long) As Integer
    CopyMemory HiWord, ByVal VarPtr(dw) + 2, 2
End Function

The destination and the size part work the same as in LoWord, but the source must be the upper two bytes of a Long, and thereís no way to specify part of a variable by reference. You have to calculate the address of that variable just like you would in assembly language. First you get the start of the variable with VarPtr, then you skip over two bytes to the middle of the variable, and finally you override CopyMemoryís normal ByRef parameter passing with ByVal.

If you remember VarPtr, youíre a real Basic old-timer. VarPtr was a function in QuickBasic, Basic Professional Development System, Macintosh Basic, GW-BASIC, and BASICA. You pass VarPtr a variable; it returns a pointer to that variable. Street Basic squared. Obviously, Visual Basic can have no trace of this abomination. Or can it? In previous versions of Visual Basic, VarPtr was a secret known only to the most hardcore of programmers (including readers of the first edition of this book). You had to figure out its secret DLL location and write a Declare statement for it. Well, now VarPtr has come halfway out of the closet. Itís part of Visual Basic, and you donít need a Declare statement to use it. But you wonít find any official documentation on it. If you call product support to complain about bugs in code that uses it, I expect that they will deny all knowledge.

Theyíll probably also deny knowledge of VarPtrís cousins, StrPtr and ObjPtr. VarPtr doesnít work on Strings because the pointer you would get back would be the pointer to the ANSI buffer that Visual Basic creates when calling API functions. StrPtr returns a pointer to the real Unicode string value. ObjPtr returns a pointer to an object. It makes me dizzy to even think about what hardcore programmers might think of to do with object pointers. I wonít talk about them in this chapter.