User-Defined Types


In Basic, you pass UDTs (structures in C) to Windows API functions by reference for three possible reasons. The first is the same reason you pass integers by reference—so that you can get something back. The second is that most UDT variables are too large to pass efficiently by value. The third and deciding reason is that Basic won’t let you pass them by value even if you want to. You’ll see an example in Chapter 6 (“Window position and size,” page 312) in which the first two reasons don’t apply and you have to jump through hoops to get around the third.


The window placement functions illustrate UDT parameter passing. The GetWindowPlacement function returns window position values through a WINDOWPLACEMENT type, and the SetWindowPlacement function saves values in the same structure. As with most examples in this chapter, it’s not important what these functions do; we’re concerned only with the syntax and what is passed at the lowest level.


The C documentation for the placement functions looks like this:

BOOL GetWindowPlacement(
HWND hWnd, // Handle of window
WINDOWPLACEMENT * lpwndpl // Address for position data
);

BOOL SetWindowPlacement(
HWND hWnd, // Handle of window
CONST WINDOWPLACEMENT * lpwndpl // Address for position data
);

The only difference between the two functions, other than the name, is that SetWindowPlacement has CONST in the WINDOWPLACEMENT type. I’ll explain that difference shortly.


The Basic user-defined type looks like this:

Type WINDOWPLACEMENT
length As Long
Flags As Long
showCmd As Long
ptMinPosition As POINTL
ptMaxPosition As POINTL
rcNormalPosition As RECT
End Type

The Basic Declare statements look like this:

Declare Function SetWindowPlacement Lib "USER32" ( _
ByVal hWnd As Long, lpwndpl As WINDOWPLACEMENT) As Long
Declare Function GetWindowPlacement Lib "USER32" ( _
ByVal hWnd As Long, lpwndpl As WINDOWPLACEMENT) As Long

In the last edition of this book, I made the mistake of changing the names of Windows API structures. I renamed WINDOW­PLACEMENT to TWindowPlacement. Of course, none of the software parties to API transactions care what you name a structure as long as it’s the right size. But the human parties find it confusing if you change the names of standard operating system features. It’s not really a good idea, even if the official names are great big, ugly, all-uppercase names that conflict with your naming conventions. People don’t want better standards, they want standard standards. I apologize for any inconvenience caused by my original misnaming or my belated correction. There is still one big exception to the rule. Unfortunately, the very common POINT structure conflicts with the rare Visual Basic Point method, so I had to use the equivalent POINTL structure for all cases where API functions take POINT structures.


You simply pass GetWindowPlacement an empty WINDOWPLACEMENT variable and read the result out of the variable afterward:

Dim wp As WINDOWPLACEMENT
‘ First set type length for Windows
wp.length = Len(wp)
‘ Get coordinates and other data about the window
f = GetWindowPlacement(hWnd, wp)
‘ Read and use the data
sValue = wp.showCmd & “,"
sValue = sValue & wp.Flags & “,”

Notice how the length field is set to the length of the structure before the call. This is called planning ahead. If Microsoft designs a new version of Windows that has more fields in the WINDOWPLACEMENT type, your old code won’t necessarily be broken. Windows will be able to tell from the length field whether you have the new and improved structure or the old standby. You’ll see length fields in a few of the original Windows structures—and a lot more of them in new Win32 structures.

While writing this book, I forgot to set the length field and wasted several hours in fruitless debugging so that you won’t have to. Don’t let my suffering be in vain.


The SetWindowPlacement function looks almost the same, but it works in the opposite way. You put values into the variable first and then call the function to pass them to the system:

‘ Remember to set length
wp.length = Len(wp)
‘ Send all your settings to the system
f = SetWindowPlacement(hWnd, wp)

If you look back at the declarations, you can see that the WINDOWPLACEMENT variable is passed by reference in both the Set and Get functions. In the Get function, it must be passed by reference so that Windows can fill in the new value. In the Set function, it’s passed by reference only to avoid wasting stack space. That’s where the CONST keyword in the C parameter definition comes in.


C allows you to specify that a variable is being passed by reference for convenience, not for modification. When you pass the address of a variable, you give the receiver the right to modify it. The CONST keyword revokes that right. It would be nice if this protection were written into the Basic side of the API contract. Just imagine being able to put a ReadOnly attribute on Visual Basic by-reference parameters so that an error is generated if the callee changes the value. Fortunately, it’s unnecessary for the Basic-to-Windows interface because Windows enforces the CONST attribute. You can rest assured that if the documentation for an API parameter says that it’s CONST, anything passed to it will come back untouched.