Visual Basic

Hide Your API Calls

Unfortunately, Visual Basic still can't do everything. There are times when we need to get information that only the dreaded Windows API can provide. A common requirement is to get version information about a file. Although we can package up the Visual Basic code into a code module and make this part of the base code, we still need to put all of the API declarations and constants at the top of the module.

If we're using base code in such a way that we add only the modules we need, things are further complicated by the fact that we then need to declare the APIs as Private in each module in which they're used. This way we end up with duplicated definitions, potentially declared differently.

This also allows developers to bypass the rule that API parameters should only be declared As Any where this is absolutely necessary. Declaring parameters with a data type of Any bypasses the compiler's type checking and allows any data type to be passed to the API. Where this is necessary-such as with SendMessage, which can take a long integer or a string as its final parameter-it is best to declare the function twice. This allows you to fully specify the parameter type and the two declarations can then be distinguished by being aliased to different names (such as SendMessageLng and SendMessageStr).

The best solution to the problem is to remove the API declarations from the Visual Basic code and put them in a type library. A type library performs the same function as the declarations in code, but instead of adding dozens of lines of code, all we need to add to the project is a reference to the type library. Given that one type library can end up being used by many projects, this makes for a much safer and cleaner way of declaring API functions. Constants needed for the API calls and help text (which will appear in the Object Browser) can also be included in a type library.

Type libraries can be a bit tricky to set up and get right but, once created, simplify your project. (Use OLEVIEW.EXE to "de-compile" a type library back to its source-it's an easy way to learn IDL.) The main difficulty in creating type libraries comes from the fact that the API declarations need to be in C format.

I have included a sample type library on the accompanying CD. The TEMPLATE.ODL file is the source file for the type library and can be opened with Notepad or any text editor. The TEMPLATE.TLB file is the compiled type library from the ODL file and is the file that should be selected in the References dialog box. Once that is done, the APIs that have been declared can be browsed via the Object Browser.

To create a type library you need two tools that ship with the Visual Basic CD:

  • GUIDGEN.EXE will generate GUIDs for you. (Peet Morris also provides a Visual Basic version of GUIDGEN in Chapter 7.) This is a process that is hidden from you when you create ActiveX components but, put simply, a GUID is the key that is used in the Registry when an ActiveX component is registered. Type libraries require these as well, but whereas Visual Basic generates these and keeps them hidden from you, type libraries require a much more manual process.
  • MKTYPLIB.EXE is the type library compiler that will turn your ODL source file into the TLB object file.

Registry Tools

GetSetting and SaveSetting are valuable tools for getting and saving information to the Registry. However, one of the parameters is the application name, which allows for potential discrepancies between (or even within) applications. To avoid these discrepancies, it is probably best to wrap these functions up in your own functions that hide the application name parameter. This way, you ensure a consistent approach to storing information in the Registry.

Another nice feature that you can implement takes advantage of one of the subclassed properties of the App object I mentioned earlier, App.InDesign. If your GetSetting and SaveSetting functions use App.ExeName and you have subclassed the App object, you can store program information in different locations depending on whether you are running in design mode or as a compiled component.

GetSetting and SaveSetting are good for getting and storing program-related information, but sometimes you might need to retrieve information from other areas of the Registry. Functions for doing this require a number of API calls and can be tricky to create. This immediately makes them prime candidates for being written into a common module with more user-friendly parameters than the APIs.

Another example of how subclassing can be used to extend the language would be to add the ability to access any part of the Registry to Visual Basic's Registry functions.