Extending Visual Basic with the API
Until recently, Windows programming has been a difficult and dangerous task. If you didn't handle all of the many details exactly correctly, your program would crash. Sometimes it would crash the development environment, and sometimes the entire operating system.
Visual Basic changed all that. By hiding most of the trickiest details, Visual Basic makes Windows programming easy. It allows programmers to achieve new levels of productivity.
That increased productivity comes at the price, however. Visual Basic hides many confusing but powerful tools available to Windows programs. Fortunately you can regain the use of many of those tools using the Windows Programming Interface (API). These functions provide access to functions at a lower level than is normally available to Visual Basic programs.
This article explains the fundamentals of using the API in Visual Basic. In the process, it describes a couple of API functions that you may find useful in your programs.
Getting StartedThere are thee main steps to using an API function:
Many Web sites contains API examples. The VB Helper Web site (www.vb-helper.com) contains more than a hundred. There are also a couple of useful books about the API. Dan Appleman's "Visual Basic 5.0 Programmer's Guide to the WIN32 API" contains more than 1500 pages of information about API functions. Finally, Microsoft's Knowledge Base contains lots of articles explaining how to use API functions to perform tasks that are impossible using Visual Basic alone.
Once you have decided what function to use, you need to declare it so your Visual Basic program understands how to use it. The declaration tells Visual Basic what DLL file contains the function, what its input parameters are, and what its return value is. For example, the following code defines the POINTAPI data type to be a data structure containing two long integers x and y. The Polygon function is located in the file gdi32.dll located in the system directory. It takes as parameters a long named hdc, a POINTAPI structure, and a count. It returns a long integer.
Private Type POINTAPI
x As Long
y As Long
Private Declare Function Polygon Lib "gdi32" _
(ByVal hdc As Long, lpPoint As POINTAPI, _
ByVal nCount As Long) As Long
The hdc parameter is a handle to a device context (DC). This tells on what window the routine should draw. Most drawing API functions take a device context handle as a parameter. Forms and PictureBoxes have an hDC property that you can use for this parameter.
Notice that the lpPoint parameter is not passed ByRef, unlike the other parameters. That means the Polygon API function actually receives the address of this argument instead of its value. If this parameter is contained in an array, the API function can use the address to find the other items in the array. This is a common method for passing an array of values into an API function. The function takes the address of the first item in the array. Another parameter, in this case nCount, tells the function how many items are in the array.
The easiest way to create this kind of declaration is to use the API Text Viewer included with Visual Basic. This tool lets you look up the API functions, data structures, and constants that you want to use. The API Viewer then generates the declarations you need to include in your Visual Basic program.
Finally, after you declare the API function, your program can invoke it. The following code shows how a program might use the Polygon function to draw a polygon.
' Declare the points array.
Dim pts(1 To 10) As POINTAPI
' Clear the form.
' Initialize the x and y coordinates of the points.
' Draw the polygon.
Polygon hdc, pts(1), 10
Notice that the program passes the first entry in the array to give the API function the address of all of the entries in the array.
You may think this is a somewhat silly example. After all, you could use Visual Basic's Line statement to draw the edges of the polygon one at a time. The Polygon function, however, obeys Visual Basic's FillStyle and FillColor properties. If the Form has these properties set to vbFSSolid and vbRed, the polygon is filled with red. If you draw the polygon using the Line statement, the polygon is not filled.
Example program Polygon, shown in Figure 1, demonstrates this function. Whenever its form resizes, the program uses the Polygon function to draw a 13-pointed star on it.
Figure 1. Program Polygon uses the Polygon API function to draw a red star.
Irregular FormsOnce you know how to create a polygon, it is only a small step to creating a polygonal form. The CreatePolygonRgn API function creates a polygonal "region" that you can use for other Windows operations. The SetWindowRgn confines a Form or other window to a region. The following code shows how these two functions are declared for a Visual Basic program.
Private Type POINTAPI x As Long y As Long End Type Private Declare Function CreatePolygonRgn _ Lib "gdi32" (lpPoint As POINTAPI, _ ByVal nCount As Long, ByVal nPolyFillMode As Long) _ As Long Private Declare Function SetWindowRgn Lib "user32" _ (ByVal hWnd As Long, ByVal hRgn As Long, _ ByVal bRedraw As Boolean) As Long Private Const ALTERNATE = 1Once the functions have been defined, using them is easy. The program initializes an array of POINTAPI structures just as it would to draw a polygon. It uses CreatePolygonRgn to create the region, and then calls SetWindowRgn to constrain the form to the region.
Dim num_pts As Integer Dim pts() As POINTAPI Dim polygon_rgn As Long ' Allocate the points array. num_pts = 12 ReDim pts(1 To num_pts) ' Initialize the points. ... ' Create the polygonal region. polygon_rgn = CreatePolygonRgn(pts(1), num_pts, ALTERNATE) ' Confine the form to the polygonal region. SetWindowRgn hWnd, polygon_rgn, TrueWhen this code has finished, the form is no longer rectangular. Objects lying under the form and outside the polygonal region show through. If you click on them, those objects receive the click events.
Example program StarForm uses this code to display a star shaped form. Program StarForm is shown in Figure 2 on top of two copies of Notepad. Notice that the program's polygon cuts off the form's edges so there is no way to resize the form. It also cuts off the form's system menu so you cannot use it to close the form. Click the Close button or press Alt-F4 to close the form.
Figure 2. Program StarForm uses the CreatePolygonRgn and SetWindowRgn API functions to make a non-rectangular form.
This article's final example demonstrates the CreateFont API function. CreateFont allows a Visual Basic program to create fonts that are stretched, squashed, or rotated. This function is one of the most complex, taking 14 parameters. These parameters specify such values as the font's height, width, angle of rotation, weight (normal, bold, etc.), italics, underline, strikethrough, and font name. You can set many of the parameters to zero to make CreateFont use default values. Search the online help to learn more about the parameters.
The font name parameter deserves special mention at this point. Unlike the other parameters described so far, font name is a string. Normally Visual Basic does not store strings the way other languages do. It stores a string as a data structure that holds the length of the string and a pointer to where the string is actually located. Other languages treat strings as simple arrays of characters ending with a null character. Converting a string from the Visual Basic format into the API function's format and back is important though easy.
Most strings used by API functions are declared as in:
ByVal F As StringWhen a string is declared ByRef in an API declaration, Visual Basic automatically converts it when it calls the API function. Instead of passing the function the Visual Basic string data structure, it passes the address of the characters.
The CreateFont function returns a handle to a font. The program must use the SelectObject API function to select the font into a Form or PictureBox. SelectObject returns a handle to the previously selected font. You should save this handle for later.
After selecting the new font, you can use the Print method to draw text using the new font. When you are done with the font, you must use SelectObject to reselect the original font back into the Form or PictureBox. You should then use the DeleteObject API function to destroy the font you created and free its resources.
The following code shows how a program might use CreateFont.
Private Sub DrawText()
Const FW_NORMAL = 400 ' Normal font weight.
Const CLIP_LH_ANGLES = 16 ' Needed for tilted fonts.
Const SHOW_TEXT = "Rotated Text!"
Dim newfont As Long
Dim oldfont As Long
newfont = CreateFont(20, 0, 300, 300, 700, _
False, False, False, 0, 0, _
CLIP_LH_ANGLES, 0, 0, "Times New Roman")
' Select the new font.
oldfont = SelectObject(hdc, newfont)
' Display the text.
CurrentX = ScaleWidth / 2
CurrentY = ScaleHeight / 2
' Restore the original font.
newfont = SelectObject(hdc, oldfont)
' Free font resources (important!)
Example program MakeFont, shown in Figure 3, uses the CreateFont function to display text rotated at different angles.
Figure 3. Program MakeFont uses the CreateFont API function to display text at a bunch of different angles.
SummaryBy hiding complex functions from you, Visual Basic lets you concentrate on your application instead of worrying about obscure windowing details. Sometimes that means Visual Basic prevents you from using powerful Windows features. Using the API, you can access that power when you really need it.
Rod Stephens is a former senior member of GTE's technical staff. A programmer for over 13 years, Rod has designed and implemented more than a half dozen award-winning commercial applications, and author over 80 articles and seven books on Visual Basic Programming. Be sure to visit his VB education site, VB-Helper, at www.vb-helper.com!