In a previous article, I discussed the Windows messaging system and explained how VB interprets numeric window messages as a set of events and properties for which programmers can write code. I also demonstrated how a technique of listening in on a VB application's message queue (known as subclassing) can provide functionality not found in VB's stock GUI controls. In this article, I'll build on that information by taking the lid off the other side of the messaging box and demonstrate how to add new behavior to your VB applications by sending messages to VB controls and forms.
I'll assume that you have a basic understanding of Windows' messaging system and how applications receive event messages from the operating system. I'd recommend that you check out the original article if you are fuzzy on these concepts.
A message in a bottle
We already know that VB's event system is not exhaustive and doesn't include the full set of standard Windows event messages. While many of the messages that VB ignores are of questionable utility to the average programmer, some of them do provide useful functionality.
For example, take the auto-complete feature found in Internet Explorer's address combo box (Figure A). IE takes the first few characters of input and displays the closest match from a list of recently visited Web site URLs.
|Internet Explorer's auto-complete behavior|
Implementing this functionality in Visual Basic would require quite a bit of code. However, by sending a combo box one of the window messages that VB ordinarily ignores using one of the Windows API's messaging functions, the same behavior can be achieved with the relatively small amount of code shown in Listing A.
The Windows API includes several functions that are useful for sending messages to a window. The most basic of these, SendMessage, sends a message by calling a window's window procedure directly. The VB declaration for SendMessage is:
Private Declare Function SendMessageLong Lib "user32" _
Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As _
Long, ByVal wParam As Long, ByVal lParam As Any) As Long
SendMessage accepts these parameters:
- · hWnd—This is the window handle of the window to which the message should be sent. Remember that in a graphical operating system, controls are considered to be windows, so to send a message to a control on a form, this parameter would be the control's hWnd and not that of the containing form!
- · wMsg—This is the actual numeric message.
- · wParam and lParam—These provide additional information that's dependent on the message being sent.
Because SendMessage calls the target window's window procedure directly, a call to it will not return until the window procedure exits—behavior that might not be ideal for all applications. There are variations on SendMessage that do not suffer from this limitation, including SendMessageCallback, SendMessageTimeout, and PostMessage.
Digging into the code
In my example, I placed a call to SendMessage in the combo box's KeyPress event, passing it the CB_FINDSTRING message (&H14C). In this instance, the wParam and lParam arguments represent the index of the combo's data list at which to begin the search and the string (actually a pointer to a null-terminated string) to search for.SendMessage then passes these parameters to the combo box's window procedure, waits for it to exit, and returns the index of the first occurrence of the lParam string in the combo's data or –1 if the string wasn't found. After doing a little math to set the combo's selected text, we have Figure B, an auto-complete combo box.
|A nice imitation of IE's auto-complete feature in 15 lines of code!|
More nifty tricks courtesy of SendMessage
There's more at the end of the SendMessage rainbow. For instance, did you know that all windows that edit controls—which is basically every control that incorporates text box-like editing behavior—have a built-in undo feature? The example in Listing B illustrates how to tap into this feature using the EM_CANUNDO and EM_UNDO messages (&HC7 and &HC6, respectively).
Other window messages can be used to further extend the behavior of VB's intrinsic controls. Here are some of the other useful messages:
- · EM_GETFIRSTVISIBLELINE (&HCE) returns the index of the topmost visible line in a multiline text box.
- · The WM_xBUTTONDOWN and WM_xBUTTONUP messages can be used to simulate mouse-clicks on a control, where x can be L, R, or M for the left, right, or middle buttons.
- · LB_SELECTSTRING (&H18C) searches a list box's data list for a string and selects it in the list, if found.
- · Sending the CB_SHOWDROPDOWN (&H14F) message to a combo box causes it to display its drop-down list.
In each case, the meaning of the lParam and wParam parameters and the return value of SendMessage are different, so be sure to check out MSDN's documentation.
All the window messages we've talked about up until now use standardized, constant codes defined by Windows. But it's also possible to define and use custom messages that only your applications understand. In the final article in this series, I'll show you how custom messaging is done and explain the technique's usefulness in cross-process communication.