Arrays


Like UDTs, arrays must be passed by reference, and for the same reasons. The big difference is that Windows knows the size of a UDT used by an API function, but it doesn’t know how many elements an array contains. In fact, that’s usually the reason you pass arrays to Windows—so that you can give varying numbers of elements. This means, however, that the API function needs to ask for the length in a separate parameter.


This requirement highlights another difference between C and Basic. Basic always knows how many elements an array contains (just as it knows the size of a string). C doesn’t, forcing the C programmer to keep track. Windows, in the C tradition, has the same requirement. This isn’t true of COM Automation, however. From the interface, you might almost think that COM Automation was written in Basic. It wasn’t, but Visual Basic types had a strong influence on the design. COM Automation supports a type called the safe array that just happens to have exactly the same format as a Basic array. Or perhaps it’s the other way around. In any case, arrays passed to COM Automation functions are more intuitive than those passed to Windows API functions.


For example, the Polygon API function (discussed in Chapter 7) passes an array of POINT variables (which we must call POINTL variables, as noted on page 66) along with the number of points in the array. Polygon connects the dots. The function looks like this on the C side:

BOOL Polygon(
HDC hdc, // Handle of device context
CONST POINT * lpPoints, // Address of polygon’s vertices
int nCount // Count of polygon’s vertices
);

The Basic UDT and declaration look like this:

Type POINTL
x As Long
y As Long

End Type Declare Function Polygon Lib "GDI32" (ByVal hdc As Long, _
lpPoints As POINTL, ByVal nCount As Long) As Long

Notice that the lpPoints parameter is passed by reference. You might think this is because POINTL is a user-defined type and UDTs are always passed by reference. Not so. The reason lpPoints is passed by reference is that in C an array is actually the address of the first element. If you pass a variable by reference, you’re actually passing its address, which is just what C (and Windows) thinks an array is. Another way to see this is to think of a variable as an array with one element. That’s why the Polygon declaration looks exactly as it would if the function took one by-reference POINTL variable instead of an array of them.


To pass an array, you pass the first element. Here’s an example:

Dim ptPoly(1 To 5) As POINTL
For i = 1 To 5
‘ Calculate each point
§
Next
Call Polygon(hdc, ptPoly(1), 5)

This gives you lots of rope—enough to hang yourself. For example, you don’t have to pass the start of the array or all the elements in it. The call

Call Polygon(hdc, ptPoly(2), 3)

passes the second through fourth elements of the array.


If you look at arrays in legal terms, the contract isn’t worth the polish on the shoes of the lawyer who wrote it. Windows wants an array, but Basic can’t be sure that’s what you’re giving. Windows wants the number of elements in the array, but Basic isn’t going to count them. It’s up to you to tell the truth. If you claim that the array contains 10 elements when it really has 5, Windows will happily use those last 5 elements whether they exist or not. Your machine might head south in a hurry.


When you look up the syntax for declarations, you’ll see that it’s possible to put empty parentheses on a parameter to indicate that you’re passing an array. Don’t try it with API functions. This feature is used for calling COM DLLs that know about Basic-style safe arrays with an encoded size. If COM had a GDI library, the ComPolygon function might look like this:

Declare Function ComPolygon Lib “COMGDI” (ByVal hdc As Long, _
lpPoints() As POINTL) As Boolean
§
Call ComPolygon(hdc, ptPoly)

You’ll get errors if you try to pass an entire array to a C-style procedure that expects the first element of an array, or if you try to pass the first element of an array to a COM-style function that expects the entire array.