VBA and System Information
VBA provides almost no native support for operations involving system information; because Windows itself provides easy-to-call API functions for determining and controlling the environment, VBA doesn’t have to duplicate that functionality. Of course, some of the API functions are tricky to call, and information you need is scattered throughout the Windows API. In addition, the Windows API provides so many functions for working with system information, and their functional-ities overlap so much, that it’s difficult to know which one to use in any given circumstance.
To make it simpler to call the selected API functions, we’ve created a series of class modules that wrap up their functionality. Why class modules? That is, what do you gain by having this functionality wrapped up in a class as opposed to a standard module? Unlike other situations in which you use class modules, in this case you don’t care about the multiple instancing. (You’ll never create more than one instance of the Keyboard class in your application, for example.) What you do gain is the ability to treat disparate function calls as simple properties. For example, to retrieve information about a particular setting, you’ll often use the GetSystemMetrics API function. To change the same information, you need to use the SystemParametersInfo function. Rather than provide two separate functions for you, one to get and one to change the value, we’ve provided a single property, with its Let and Get Property procedures. This way, from your application, you can write simple code like this to retrieve a value, change it, and then set it back at a later time:
Dim lngBorderColor As Long
Dim sc As New SystemColors
‘ Store away the original color.
lngBorderColor = sc.ActiveBorder
sc.ActiveBorder = 255
‘ Do work in here…
‘ Now reset the color and release the object.
sc.ActiveBorder = lngBorderColor
Set sc = Nothing
In addition, class modules provide another benefit: because class modules trigger their Initialize event when you create a new instance of the class, the class can call an API function that initializes a data structure. Several of the system information functions require you to pass a single data structure, with many elements. For these functions, the corresponding class can call the function in its Initialize event, retrieve all the information at once, and return the various pieces of information to you as properties of the class. For more information, see the section “Creating the Memory Status Class” later in this chapter.
Each of the classes provided in this chapter is self-contained. If you’re interested only in controlling the keyboard, you’ll need to import only the single KEYBOARD.CLS module. If you need more information, import the classes you need. The bulk of this chapter, organized to match the class modules themselves, describes in detail each of the properties and methods of the classes. In each case, you can either dig into or skip over the details of how the class works. If you just need the functionality and don’t care about the details, skip over the description of the API calls and their usage. If, on the other hand, you want to understand exactly how these classes work or want to expand their functionality, all the information you need is here.
If you find yourself importing multiple classes, you may want to “factor out” the repeated API and constant declarations. Although there aren’t a great many of repeated declarations from module to module, there’s no point adding extra heft to your applications. Once you’ve imported all the classes you’ll need, you can copy the shared declarations to a standard module, remove the “Private” keyword, and use the new shared declarations for your API calls. Don’t forget that you’ll also need to move the necessary data structures and constants to a shared location.
The API Functions
Although you’ll find well over a hundred properties and methods covered in this chapter, we actually used only a few API calls. These API calls generally fall into one of three classes of functions:
- Functions that return a single value. GetComputerName, for example, returns only the name of the current computer, and GetCaretBlinkTime simply returns the number of milliseconds between “blinks” of the text-insert caret.
- Functions that allow you to specify one of any number of parameter values and return different pieces of information depending on the “question” you asked. GetSystemMetrics and SystemParametersInfo fall into this category. These functions allow you to choose an item of interest from a documented list of items, and each returns a single piece of information, based on the value you supplied.
- Functions that allow you to pass in a single data structure, which the function fills in with various pieces of information: GlobalMemoryStatus, GetSystemInfo, and GetSystemPowerStatus all fall into this category. Normally, for this type of function, the wrapper class calls the function in response to each property Get procedure, and the property returns just the element of the structure you require.
Using the GetSystemMetrics Function
The GetSystemMetrics function can return one of 70 or so values, depending on which you request. In each case, you pass it a single constant value, and it returns the piece of information you need.
You shouldn’t need to worry about specific constants and their values if you’re using the classes provided in this chapter. If you’re interested, however, your best reference information for GetSystemMetrics (and its partner, SystemParametersInfo) is the MSDN CD.
To find the number of mouse buttons, for example, you might use a call like this:
lngMouseButtons = GetSystemMetrics(SM_CMOUSEBUTTONS)
and to find out whether there’s a mouse with a wheel installed, you could use
fWheelMouse = GetSystemMetrics(SM_MOUSEWHEELPRESENT)
Of course, you don’t have to use either of these. You can retrieve both pieces of information using the Mouse class we’ve provided:
Dim oMouse As New Mouse
lngMouseButtons = oMouse.Buttons
fWheelMouse = oMouse.WheelPresent
If you see references to “mouse wheels” throughout this chapter, don’t go out looking for information on rodent transportation. This term refers to Microsoft’s input device with two mouse buttons and a rubberized wheel between the buttons.
You’ll find calls to GetSystemMetrics scattered throughout the classes provided with this chapter. When we gathered information for this chapter, it made more sense to group the classes based on the functionality of the information than on its source, so you’ll find calls to GetSystemMetrics, and other general-purpose API calls, throughout the various classes.
In addition to API calls, you’ll find the declarations for the functions and the constants they use. For example, you’ll find this block of code in the declarations area of MOUSE.CLS:
Private Const SM_CXCURSOR = 13
Private Const SM_CYCURSOR = 14
Private Const SM_MOUSEPRESENT = 19
Private Const SM_SWAPBUTTON = 23
Private Const SM_CXDOUBLECLK = 36
Private Const SM_CYDOUBLECLK = 37
Private Const SM_CMOUSEBUTTONS = 43
Private Const SM_CXDRAG = 68
Private Const SM_CYDRAG = 69
Private Const SM_MOUSEWHEELPRESENT = 75
Private Declare Function GetSystemMetrics Lib “user32” _
(ByVal nIndex As Long) As Long
This set of declarations declares the API function and provides the necessary constant values needed by the class. (All the constants beginning with “SM_” will be used by GetSystemMetrics.)