Crash Testing


There is no type safety when you call EnumWindows. You can pass anything you want in the first parameter as long as it’s a Long. This sounds dangerous, but it’s not quite as bad as you might expect. Let’s try some of the more obvious mistakes you might make and see whether we can bring the system to its knees. Save your data before continuing.


First, let’s try passing the address of a variable instead of a function:

f = EnumWindows(AddressOf c, c)

No way. Visual Basic gives a compiler error: Expected Sub, Function, or Property. You might think from its name that AddressOf would give you the address of anything, but actually it works only with procedures.


To continue our quest for a spectacular crash, we might try this:

f = EnumWindows(AddressOf Form_Load, c)

Form_Load doesn’t have any arguments, so it’s bound to crash. Nope. The compiler error is: Invalid use of AddressOf operator. This cryptic error message points out a problem I mentioned earlier. What it means is that you’re using AddressOf with a procedure in a form or class module. Visual Basic won’t let you do that. The EnumWndProc function must be located in a standard module (BAS file).


This unintuitive limitation has to with a big difference between standard modules and class modules that you ordinarily don’t have to worry about. A method in a form or class has an address, doesn’t it? Why can’t you pass that address to EnumWindow? Because a procedure in a standard module is just a procedure, but a method is a procedure associated with a specific instance of an object variable. Visual Basic would have to jump through some hoops to find the ob­ject and then find the address of a particular method. It doesn’t jump through those hoops, so you must jump through them instead. You must exchange global data between the form or class module where you’re using the callback and
the standard module where you had to put the callback function. This is why the test form has to Set a public copy of the ListBox object variable in the standard module.


Let’s continue our quest for the perfect crash. I’ll add an invalid function (one that doesn’t pass two Longs as expected) to the standard module:

Function CrashMe() As Long
CrashMe = 1
End Function

Try calling it like this:

f = EnumWindows(AddressOf CrashMe, c)

At this point, you’ll get undefined behavior. On my test machine, it runs with no error or warning. Who knows what it will do on your machine. But what about this one?

Function CrashMe(hWnd As Long) As Long
Debug.Print hWnd
CrashMe = 1
End Function

Here you have one parameter when you’re supposed to have two, and that one parameter is by reference when it’s supposed to be by value. You’ll get a good crash this time.


Here’s another way to crash. Visual Basic won’t let you use AddressOf to get the address of a variable, but it won’t prevent you from getting one with VarPtr:

f = EnumWindows(VarPtr(c), c)

It’s not difficult to make callbacks crash. I’ve done it not only on purpose in tests, but by accident in real programs. At least Visual Basic prevents some of the most obvious mistakes, but it will also prevent one thing (using a method) that doesn’t seem like a mistake.