Was this page helpful?
Your feedback about this content is important. Let us know what you think.
Additional feedback?
1500 characters remaining
Export (0) Print
Expand All
Expand Minimize

Replacing API Calls with .NET Framework Classes

 

Ken Getz
MCW Technologies

February 2002

Summary: Reduce your reliance on Win32 API calls by learning about specific and useful classes in the Microsoft .NET Framework; each class discussed here replaces one or more Win32 API calls you might have had to make in Microsoft Visual Basic 6.0 to accomplish the same goals. (18 printed pages)

Objectives

  • Discover specific replacements for existing Win32 API calls.
  • Learn about the Registry class.
  • Work with the FileVersionInfo class.
  • Work with environment and system information.

Contents

Avoiding the Win32 API
Working with the Registry
Using Common Dialog Boxes
Retrieving File Version Information
Retrieving Environment Information
Summary

Avoiding the Win32 API

There's no way to avoid calling the Win32 API, sooner or later if you're a Microsoft Visual Basic® 6.0 developer. There are just too many tasks developers must tackle that Visual Basic doesn't provide any means of accomplishing. For example, in Visual Basic 6.0, how can you:

  • Determine file version information?
  • Read and write to any location in the registry?
  • Determine user's special folders, such as the Microsoft Windows® Favorites, or Personal folders?
  • Retrieve a list of all available drives?
  • Find out the user's login name, or the computer name?
  • Retrieve a list of all open windows?

You can't solve any of these problems using only the tools available in Visual Basic 6.0. For each of these, developers need to work with the Windows API, and many developers have worked out solutions to these (and many other) tasks using the Windows API.

What's Wrong with the Windows API?

Why not just continue using the Windows API in the .NET environment? You certainly can, using the .NET Platform Invocation Services (referred to as "P/Invoke"). From the Visual Basic developer's point of view, calling the Windows API is no more difficult than using the familiar Declare statement. Using the Windows API has some serious drawbacks in the .NET world, however, and you might consider doing anything you can to avoid it. For example:

  • The .NET common language runtime is meant to be platform non-specific. Whenever you use a Windows API call, you're tying your code to the specific platform (that is, a specific version of Windows, and Windows itself, as opposed to some other operating system) on which you wrote it. Converting your code to another platform, should that ever become necessary, will require modifications to each line of code that uses the API call.
  • Calling the Windows API (or any unmanaged code in Dlls) from .NET isn't as simple as it was in Visual Basic 6.0. Limitations on the way structures work makes it tricky to pass structures to API calls, for example. In addition, API declarations from Visual Basic 6.0 will need to be altered, because of changes to data types and stricter type conversions.
  • Techniques for using the Windows API (and external code, in general) differ from language to language. If your intent is to work in multiple .NET languages, you'll need to master different techniques for each language.
  • Code that calls the Windows API requires users calling that code to have permissions to do so. This can compromise the security scheme for your application, and you'll need to plan ahead for this requirement.

The point here is simple: although you can continue to use the Windows API from Visual Basic .NET applications, you generally should look for alternatives provided by the .NET Framework if you can. Although the intent of the .NET Framework was not to shield you from ever having to use Windows functionality directly, the Framework does supply a large number of classes that can help replace your reliance on Windows API calls.

Although it might be convenient to have a complete listing of Win32 API calls, and the corresponding (if any) technique for accomplishing the same goal in the .NET Framework, this document doesn't tackle that task. In this document, you'll learn about some specific and useful classes provided by the .NET Framework to ease your pain. In each case, the class discussed here replaces one or more Win32 API calls you might have had to make in Visual Basic 6.0 to accomplish the same goals.

Working with the Registry

If you're like most Visual Basic 6.0 developers, you found the vaguely useful SaveSetting, GetSetting, GetAllSettings, and DeleteSetting methods built into Microsoft Visual Basic for Applications (VBA), but were most likely frustrated at their limitations: all of these methods worked only on keys under the HKEY_CURRENT_USER\Software\VB and VBA Program Settings key in the registry. If you wanted to read or write keys or values anywhere else in the registry, you were required to resort to complex API calls, or to count on someone else's code that handled these details for you.

The .NET Framework provides a robust pair of classes (Registry and RegistryKey) in the Microsoft.Win32 namespace that makes working with the Registry simple—no more API calls required!

As a demonstration, click the Work with the Registry button on the main form in the sample project. This form provides a list of all the values found in the HKEY_LOCAL_MACHINE hive, at the SOFTWARE\Microsoft\Windows\ CurrentVersion\Run key. You can right-click any item in the list, and select to insert a new item, or edit or delete the selected item, as shown in Figure 1.

Tip   The sample form has also been designed so that if you press Enter in the list box, you will edit the currently selected item. Pressing Delete will attempt to delete the item, and pressing Insert will add a new value. These items correspond to the items on the context menu for the list box.

Figure 1. Retrieving and modifying information in the Windows Registry is easy using the Registry and RegistryKey classes

The .NET Framework provides two useful classes that make it easy for you to work with the Windows Registry. The first, Registry, provides fields corresponding to each of the standard Registry hives:

  • ClassesRoot (HKEY_CLASSES_ROOT)
  • CurrentConfig (HKEY_CURRENT_CONFIG)
  • CurrentUser (HKEY_CURRENT_USER)
  • DynData (HKEY_DYN_DATA)
  • LocalMachine (HKEY_LOCAL_MACHINE)
  • PerformanceData (HKEY_PERFORMANCE_DATA)
  • Users (HKEY_USERS)

To use the Registry class, simply retrieve a reference to the hive you need. The sample form's LoadList procedure includes code like this, to work with the HKEY_LOCAL_MACHINE hive in the registry:

Imports Microsoft.Win32

Dim reg As RegistryKey = Registry.LocalMachine

The RegistryKey class is the one that does all the work. It provides a group of methods for working with the Registry. Table 1 lists all the useful methods of the RegistryKey class.

Table 1. RegistryKey class methods

Method Description
CreateSubKey Creates a new subkey or opens an existing subkey.
DeleteSubKey Deletes the specified subkey.
DeleteSubKeyTree Deletes a subkey and any child subkeys recursively.
DeleteValue Deletes the specified value from this key.
GetSubKeyNames Retrieves an array of strings that contains all the subkey names.
GetValue Retrieves the specified value.
GetValueNames Retrieves an array of strings that contains all the value names associated with this key.
OpenSubKey Retrieves a specified subkey, with optional write access.
SetValue Sets the specified value.

The RegistryKey class also provides these three properties:

  • Name: retrieves the name of the key.
  • SubkeyCount: Retrieves the number of subkeys associated with the key.
  • ValueCount: Retrieves the number of values associated with the key.

The ListLoad procedure, from the sample form, retrieves all the values from the requested key and adds them to the list box on the form:

Private Const conRegKey As String = _
 "SOFTWARE\Microsoft\Windows\CurrentVersion\Run"

Private Structure RegData
    Public Value As String
    Public Data As String

    Public Overrides _
     Function ToString() As String
        Return Me.Value
    End Function
End Structure

Private Sub ListLoad()
    Dim reg As RegistryKey = Registry.LocalMachine
    Dim astrValues() As String
    Dim strValue As String
    Dim rd As RegData

    ' Clear the existing items from the list box.
    lstItems.BeginUpdate()
    lstItems.Items.Clear()

    ' Open the registry key, then load
    ' the list box with values from the key.
    reg = reg.OpenSubKey(conRegKey)
    astrValues = reg.GetValueNames()
    For Each strValue In astrValues
        rd.Value = strValue.ToString
        rd.Data = reg.GetValue(strValue)
        lstItems.Items.Add(rd)
    Next
    lstItems.EndUpdate()
End Sub

Choosing to edit or add a new value from the sample form runs this code:

Private Sub AddOrEdit( _
 ByVal rd As RegData, _
 ByVal Mode As frmAddValue.AccessMode)
    Dim reg As RegistryKey = Registry.LocalMachine
    Dim frm As New frmAddValue(Mode)

    frm.KeyName = rd.Value
    frm.KeyData = rd.Data

    If frm.ShowDialog() = DialogResult.OK Then
        If frm.KeyName <> String.Empty Then
            reg = reg.OpenSubKey(conRegKey, True)
            reg.SetValue(frm.KeyName, frm.KeyData)
            ListLoad()
        End If
    End If
End Sub

This code opens the registry key again, this time requesting permission to write to the key (the second parameter to OpenSubKey makes this request). The code then calls the SetValue method, passing in the key name and value from the dialog box form shown in Figure 1. To make things simple, the SetValue method can either add a new value or modify an existing value. If the value isn't already there, the SetValue method adds it for you.

To delete a value, the sample form calls the following code:

Private Sub DeleteKey(ByVal rd As RegData)
    Dim strText As String
    Dim reg As RegistryKey = Registry.LocalMachine

    If lstItems.SelectedIndex = -1 Then
        Exit Sub
    End If

    ' Delete selected key.
    strText = String.Format( _
     "Are you sure you want to delete ""{0}""?", _
     rd.Value)

    If MessageBox.Show(strText, _
     "Delete Registry Value", _
     MessageBoxButtons.YesNo, _
     MessageBoxIcon.Question) = DialogResult.Yes Then
        ' Open the key, allowing writing.
        reg = reg.OpenSubKey(conRegKey, True)
        reg.DeleteValue(rd.Value)
        ' Reload the list box.
        ListLoad()
    End If
End Sub

This code opens the key with a request to be able to write to it, and then calls the DeleteValue method to delete the selected value.

Given the information in the sample form, and the documentation provided with the .NET Framework, you should be able to simply accomplish any Registry-related task, without needing to use the Windows API. This is a simple object model, and provides much more power than Visual Basic 6.0 developers have previously had.

Tip   You can also work with the Registry on remote machines, if you have the necessary permissions. You can call the RegistryKey.OpenRemoteBaseKey method to retrieve a base key on other computers, rather than simply using one of the properties of the Registry class to represent Registry hives on your own machine.

Using Common Dialog Boxes

Windows provides a group of common dialog boxes, making it easy for developers to request information from their users. You've no doubt seen, and used, the file open and save, color, printer, and font common dialog boxes. Visual Basic 6.0 developers had two choices when it came to using these dialog boxes. They could either:

  • Use a Microsoft ActiveX® control that provided a simple object model wrapping up the common dialog boxes, but this control had severe deployment issues based on the number of different versions of the control and the underlying DLLs. (This single problem was the biggest "DLL Hell" issue for many Visual Basic 6.0 developers.)
  • Use the Windows API directly, sending messages and providing callback functions to manage the various common dialog boxes.

Neither solution was perfect, and neither is currently necessary because of additions to the .NET Framework. If you investigate within the System.Windows.Forms namespace, you'll find the ColorDialog, FileDialog, FontDialog, and PrintDialog classes. These classes, all built into the framework (that is, not requiring either API calls nor an ActiveX control) make it simple for you to incorporate this standard functionality into your applications.

Each of these classes provides a series of properties you can set before displaying the dialog box, using the ShowDialog method of the class. Discussing each class in detail is beyond the scope of this article, but the sample project does use the FileDialog class, allowing you to select a file. If you select the File Version Info button on the main form, you can use the Select a File button on the demonstration form to display the File Open dialog box, as shown in Figure 2.

Figure 2. Use the FileDialog class to display Windows common dialog boxes

Although the FileDialog class doesn't provide the same level of flexibility as you can get when programming the Windows API directly, it does provide a rich set of properties you can use to control the behavior of the dialog box. You can determine where to look for files, you can select one or more files, and you can retrieve the selected file name or names once you're done. Table 2 lists the subset of the properties and methods of the FileDialog class that you're likely to use.

Table 2. The FileDialog object properties and methods

Property/Method Description
AddExtension Indicates whether the dialog box automatically adds an extension to a file name if the user omits the extension.
CheckFileExists Indicates whether the dialog box displays a warning if the user specifies a file name that doesn't exist.
CheckPathExists Indicates whether the dialog box displays a warning if the user specifies a path that does not exist.
DefaultExt The default file extension.
DereferenceLinks Indicates whether the dialog box returns the location of the file referenced by the shortcut or whether it returns the location of the shortcut (.lnk).
FileName The file name selected in the file dialog box.
FileNames (Read-only) The file names of all selected files in the dialog box.
Filter The current file name filter string, which determines the choices that appear in the "Save as file type" or "Files of type" box in the dialog box.
FilterIndex The index of the filter currently selected in the file dialog box.
InitialDirectory The initial directory displayed by the file dialog box.
RestoreDirectory Indicates whether the dialog box restores the current directory before closing.
ShowHelp Indicates whether the Help button is displayed in the file dialog.
Title The file dialog box title.
ValidateNames Indicates whether the dialog box accepts only valid file names.
Reset (Method) Resets all properties to their default values.
ShowDialog (Method) Displays the file dialog box. Returns DialogResult.OK if the user pressed OK, otherwise, returns DialogResult.Cancel.

The sample form, frmFileVersionInfo, calls the following code when you click Select a File:

Private Sub btnSelectFile_Click( _
 ByVal sender As System.Object, _
 ByVal e As System.EventArgs) _
 Handles btnSelectFile.Click

    Dim ofd As OpenFileDialog = New OpenFileDialog()
    ofd.Filter = _
        "Executable files (*.exe;*.dll;*.ocx)|" & _
        "*.exe;*.dll;*.ocx|" & _
        "Drivers (*.sys;*.drv;*.fnt)|" & _
        "*.sys;*.drv;*.fnt|" & _
        "All files (*.*)|*.*"
    ofd.FilterIndex = 1
    ofd.ShowReadOnly = False
    ofd.RestoreDirectory = True
    If ofd.ShowDialog() = DialogResult.OK Then
        If ofd.FileName.Length > 0 Then
            ' Display file version info.
            DisplayFileInfo(ofd.FileName)
        End If
    End If
End Sub

Tip   Just as you might when using the Visual Basic 6.0 CommonDialog ActiveX control, you set the Filter property of the FileDialog class to a string containing pairs of values separated with a vertical bar, like this: "Description|FileSpec".

In this example, once you've selected a file name, the sample form displays information about the file in the ListView control on the form. The next section discusses the FileVersionInfo class, which makes this possible.

Retrieving File Version Information

Developers, and compilers, can embed version information within executable, DLL, and driver files. You may have a need to retrieve some or all of this information as part of your applications, and doing so in Visual Basic 6.0 required a mass of API calls. This effort required calls to the GetVersionInfoSize, VerQueryValue, and GetFileVersionInfo Win32 API functions, and none was easy to work with from within Visual Basic.

The .NET Framework again comes to the rescue—this time, the FileVersionInfo object makes this all ridiculously simple. All that's required of you is to call the shared GetVersionInfo method of the FileVersionInfo object, passing in a file name. The sample form uses code like this:

Dim fvi As FileVersionInfo = _
 FileVersionInfo.GetVersionInfo(strFile)

After that, it's a simple matter of retrieving properties of the FileVersionInfo object. The sample form uses a small procedure, shown here, that adds each property name and value to a ListView control on the form:

Private Sub AddItem( _
ByVal strProperty As String, _
ByVal strValue As String)
    With lvwInfo.Items.Add(strProperty)
        .SubItems.Add(strValue)
    End With
End Sub

The code that does the real work, then, simply calls AddItem over and over, once for each property:

AddItem("Comments", fvi.Comments)
AddItem("CompanyName", fvi.CompanyName)
AddItem("Language", fvi.Language)
' Lines removed here…
AddItem("Product", fvi.ProductName)
AddItem("ProductPrivatePart", _
 fvi.ProductPrivatePart.ToString())
AddItem("ProductVersion", fvi.ProductVersion)
AddItem("SpecialBuild", fvi.SpecialBuild)

The result form, shown in Figure 3, displays all the version information that's available to you programmatically.

Figure 3. Retrieve file version information using the FileVersionInfo class

Retrieving Environment Information

The Win32 API provides a number of functions that allow you to determine users' environment settings. GetSystemMetrics and SystemParametersInfo are two such functions. Although you can still call these API functions from within your .NET applications, most likely, you won't need to. The Environment class (in the System namespace) and the SystemInformation class (in the System.Windows.Forms namespace) provide much of the same information as the API functions. In this section, you'll see examples demonstrating the features of these two classes.

Warning   Don't spend time looking for ways to set the users' environment settings from within these classes. All the information displayed here is read-only. If you still want to modify the environment, you'll need to find other means to do so.

Using the System.Environment Class

The System.Environment class provides several disparate pieces of information that otherwise would require several different Windows API calls. Using System.Environment you can retrieve:

  • Information about available drives (the GetLogicalDrives method)
  • The number of milliseconds since Windows was started (the TickCount property)
  • General environment settings, provided by the CurrentDirectory, MachineName, OSVersion, SystemDirectory, UserDomainName, UserInteractive, UserName, and WorkingSet properties.
  • A list of special folders provided by using the GetFolderPath method.

If you've worked with the Windows API, you know that this selection of methods and properties replaces many API calls, including GetTickCount, GetLogicalDrives, GetSystemDirectory, GetComputerName, GetUserName, GetVersionEx, and so on.

The sample form shown in Figure 4 (click Environment Info on the main form), displays the results of the GetLogicalDrives method in the list box near the top. It contains a list of all the special folders (retrieved using the GetFolderPath method), and the list box towards the bottom of the form displays the results of many of the properties of the class.

Figure 4. Form showing properties and methods used by the System.Environment class

In addition, to test the TickCount property, click Test ClickCount to display the results of working with the StopWatch class defined in the sample form, and use this code:

Public Class StopWatch
    Private mintStart As Integer
    Public Sub Start()
        mintStart = Environment.TickCount
    End Sub

    Public Function Elapsed() As Integer
        Return Environment.TickCount - mintStart
    End Function

    Public Overrides Function ToString() As String
        Return String.Format( _
         "Started: {0}, Elapsed: {1}", _
         mintStart, Me.Elapsed)
    End Function
End Class

The FillProperties method, shown here, uses a copy of the AddItem method, shown earlier, to fill a ListView control with property names and results:

Private Sub FillProperties()
    AddItem("CurrentDirectory", _
     Environment.CurrentDirectory)
    AddItem("MachineName", Environment.MachineName)
    AddItem("OSVersion.Platform", _
     Environment.OSVersion.Platform.ToString)
    AddItem("OSVersion.Version", _
     Environment.OSVersion.Version.ToString)
    AddItem("SystemDirectory", _
     Environment.SystemDirectory)
    AddItem("UserDomainName", Environment.UserDomainName)
    AddItem("UserInteractive", _
     Environment.UserInteractive)
    AddItem("UserName", Environment.UserName)
    AddItem("WorkingSet", Environment.WorkingSet)
End Sub

The FillFolderList method actually contains some interesting code besides its use of the Environment class. This procedure's goal is to loop through all the members of the SpecialFolder enumeration provided by the Environment class. (This enumeration contains logical names for physical folders, such as Favorites, History, and so on.) The procedure adds each enumeration value's Name to the ListView control on the form, and also adds the result of passing the enumerated value to the GetFolderPath method of the Environment object. The procedure, shown here, does all the work:

Private Sub FillFolderList()
    Dim strName As String
    Dim astrNames() As String
    Dim aintValues As Array
    Dim i As Integer

    ' Fill the first column in the ListView control
    ' with the names in the SpecialFolder enumeration.
    astrNames = System.Enum.GetNames( _
     GetType(Environment.SpecialFolder))
    aintValues = System.Enum.GetValues( _
     GetType(Environment.SpecialFolder))
    For i = 0 To astrNames.Length - 1
        With lvwFolders.Items.Add(astrNames(i))
            .SubItems.Add( _
             Environment.GetFolderPath(aintValues(i)))
        End With
    Next
End Sub

Rather than hard-coding the parameters sent to the Environment object, this example uses the shared GetNames and GetValues methods of the Enum class. By passing GetNames the results of calling the Visual Basic .NET GetType function (passing in the specific enumeration type), you retrieve an array filled with the names of all the members of the numeration. Repeating the process with the GetValues method returns an Array object, containing all the values from the enumeration:

astrNames = System.Enum.GetNames( _
 GetType(Environment.SpecialFolder))
aintValues = System.Enum.GetValues( _
 GetType(Environment.SpecialFolder))

Given those two arrays, the remainder of the procedure loops through both, adding the values from astrNames to the ListView, then calling the GetFolderPath method of the Environment class to retrieve the corresponding paths:

For i = 0 To astrNames.Length - 1
    With lvwFolders.Items.Add(astrNames(i))
        .SubItems.Add( _
         Environment.GetFolderPath(aintValues(i)))
    End With
Next

The upper ListView control shown in Figure 4 contains the output of this code.

Tip   The Enum class provides you with some tricks that simply weren't available in Visual Basic 6.0, such as the GetNames and GetValues methods shown in this example. Look in the .NET Framework documentation for more information on taking advantage of the capabilities of the Enum class.

Using the Windows.Forms.SystemInformation Class

When providing carefully crafted user interfaces, you often need to determine current Windows settings, such as the width and height of icons, or the width of scrollbars. In Visual Basic 6.0, you could use the GetSystemMetrics and SystemParametersInfo Windows API functions to determine many of these types of settings. Using the .NET Framework, you can take advantage of the SystemInformation class provided in the Windows.Forms namespace.

Although the code used in frmSystemInformation, shown in Figure 5, isn't very interesting, it does show off all the properties provided by the class. (Click SystemInformation Info on the main form to test this sample form.) The sample form works its way through all the properties of the SystemInformation class, displaying the name and current value of each in a ListView control on the form.

Figure 5. Sample form displaying all properties of the Windows.Forms.SystemInformation class

The sample form uses the same AddItem method shown earlier, and simply loops through all 60-or-so properties of the SystemInformation class, displaying the output:

AddItem("ArrangeDirection", _
 SystemInformation.ArrangeDirection)
AddItem("StartingPosition", _
 SystemInformation.ArrangeStartingPosition)
AddItem("BootMode", SystemInformation.BootMode)
' and so on...

Summary

  • Although you can use Platform Invocation Services (P/Invoke) in Visual Basic .NET to work with unmanaged code, and therefore call the Windows API directly, you should look for alternatives when creating Visual Basic .NET applications. You don't need to worry about the details of p/invoke, however, since the Declare statement handles the details for you.
  • The .NET Framework doesn't wrap up all the Windows API functionality, but much of what you might have used API calls for, in Visual Basic 6.0, is available to you in the Framework.
  • The Registry and RegistryKey classes make it easy to work with the Windows Registry, and make it possible for developers to avoid a number of API calls.
  • The FileDialog, ColorDialog, FontDialog, and PrinterDialog classes make it easy to work with common dialog boxes in Windows. There's no need to call the Windows API directly nor use the dreaded CommonDialog ActiveX control.
  • You can use the FileVersionInfo class to retrieve all the file version information embedded in executable, driver, and DLL files. This class replaces some difficult Windows API coding required in Visual Basic 6.0.
  • The Environment and SystemInformation classes make it easy to retrieve system settings that otherwise required calls to several different API functions.

About the Author

Ken Getz is a senior consultant with MCW Technologies and splits his time between programming, writing, and training. He specializes in tools and applications written in Microsoft Access, Visual Basic, and the rest of the Office and BackOffice suites. Ken is co-author of several books including Access 97 Developer's Handbook with Paul Litwin and Mike Gilbert, Access 2000 Developer's Handbooks with Paul Litwin and Mike Gilbert, Access 2002 Developer's Handbooks with Paul Litwin and Mike Gunderloy, Visual Basic Language Developer's Handbook with Mike Gilbert, and VBA Developer's Handbook with Mike Gilbert (Sybex). Ken co-authored a book with Paul D. Sheriff entitled ASP.NET Jumpstart. He co-wrote training materials and teaches for AppDev. Ken is a frequent speaker at technical conferences, and has spoken at the Microsoft Tech*Ed conferences since 1994. Ken is a technical editor for Access/VB/SQL Advisor magazine and contributing eEditor for the Informant Communication Group's Microsoft Office Solutions magazine.

About Informant Communications Group

Informant Communications Group, Inc. (www.informant.com) is a diversified media company focused on the information technology sector. Specializing in software development publications, conferences, catalog publishing and Web sites, ICG was founded in 1990. With offices in the United States and the United Kingdom, ICG has served as a respected media and marketing content integrator, satisfying the burgeoning appetite of IT professionals for quality technical information.

Copyright © 2002 Informant Communications Group and Microsoft Corporation

Technical editing: KNG Consulting

Show:
© 2015 Microsoft