ONDotNet.com    
 Published on ONDotNet.com (http://www.ondotnet.com/)
 See this if you're having trouble printing code examples


O'Reilly Book Excerpts: VB.NET Core Classes In a Nutshell

An Introduction to the .NET FCL, Part 1

Related Reading

VB.NET Core Classes in a Nutshell
By Budi Kurniawan, Ted Neward

by Budi Kurniawan and Ted Neward

In part one of a five-part series of book excerpts from VB.NET Core Classes in a Nutshell, learn what it's like to develop without the .NET FCL library and its core classes.


Chapter 1
Introduction

The .NET Framework is Microsoft's new computing platform that simplifies the design, development, and deployment of computer applications. Developed particularly to facilitate the creation of Internet applications and distributed Internet applications, the .NET Framework features the .NET Framework Class Library (FCL), a systematic class framework to be used for the development of system tools and utilities as well as application software. This chapter assesses the significance of the .NET FCL and discusses accessing it from Visual Basic code.

Before the .NET FCL

Although programmers using languages like C++ have been using frameworks for system and application development from the very inception of their language (the Microsoft Foundation Class Library, or MFC, for instance, is a framework for developers of Windows applications using C++), comprehensive frameworks or class libraries are comparatively rare in Visual Basic programming. For the most part, programmers of previous versions of Visual Basic depended on two major sources to extend the Visual Basic language: the Win32 API, and ActiveX servers exposed through COM automation.

The Win32 API

The Win32 API is a procedural library that allows the developer to create programs that run under Windows and take advantage of core Windows operating system services. The Win32 API has been enhanced on a regular basis since it was introduced to support Windows NT 3.0, and it now consists of several thousand functions and constants located in a number of dynamic link libraries (DLLs). Because it is a loose collection of functions, there are'nt necessarily any consistent conventions in naming functions or in designating function parameters. The function-based style of programming using the Win32 API has a number of limitations:

Lack of consistency across the entire Win32 API

Although a collection of Win32 API functions may be interdependent, at the same time each function tends to be a more or less independent entity that is called in isolation from other functions in your program. This tends to make the Win32 API as a whole difficult for all programmers to learn and master.

Focus on C programmers

The Win32 API originally was developed as a set of functions that would be called primarily from C code. Although the Win32 API can be called from Visual Basic code, writing code that relies heavily on calls to external DLLs has always been something of an adventure in Visual Basic. Much of the challenge centers on the fact that the type systems used by C and Visual Basic are not completely compatible, so that Visual Basic data types have to be converted to data types expected by C language routines.

To get some sense of the difference in style between the function-based, procedural programming that characterizes the Win32 API and the object-oriented programming that characterizes the .NET Framework, Examples 1-1 and 1-2 contain the source code for a console mode routine that launches the application responsible for handling the data file whose name the user enters in a text box. Example 1-1 is written in Visual Basic 6.0 (although it could have run under any version from VB 4 through VB 6) and relies extensively on Win32 API calls, and particularly on calls to the registry API. Example 1-2 is written for Visual Basic .NET and relies on the .NET Framework Class Library, and particularly on its Registry and RegistryKey classes.

Example 1-1. Launching an application using the Win32 API
Option Explicit
  
Private Declare Function RegCloseKey Lib _ 
        "advapi32.dll" ( ByVal hKey As Long) _ 
        As Long
Private Declare Function RegOpenKey Lib _
        "advapi32.dll" alias "RegOpenKeyA" ( _
        ByVal hKey As Long, ByVal lpSubKey _
        As String, phkResult As Long) As Long
Public Declare Function RegQueryValue Lib _
       "advapi32.dll" _
       Alias "RegQueryValueA" ( _
       ByVal hKey As Long, _
       ByVal lpSubKey As String, _
       ByVal lpValue As String, _
       lpcbValue As Long) As Long
Private Declare Function RegQueryValueEx Lib _
       "advapi32.dll" _
       Alias "RegQueryValueExA" ( _
       ByVal hKey As Long, _
       ByVal lpValueName As String, _
       ByVal lpReserved As Long, _
       lpType As Long, _
       lpData As Any, lpcbData As Long) As Long
Public Declare Function WinExec Lib _
       "kernel32" ( _
       ByVal lpCmdLine As String, _
       ByVal nCmdShow As Long) _
       As Long
  
Public Const MAX_PATH = 260
  
Private Const HKEY_CLASSES_ROOT = &H80000000
  
Private Const ERROR_SUCCESS = 0&
  
Public Const REG_DWORD = 4
Public Const REG_SZ = 1
  
Public Const SW_SHOWNORMAL = 1
  
Private Sub Main()
   Dim strFile As String, strExten As String
   Dim strProgID As String, strExe As String
   Dim lPos As Long
   Dim hKey As Long, lStrLen As Long
  
   strFile = InputBox("Enter Name of File " & _
                      "to Open: ", "Open File", "")
   If strFile = "" Then Exit Sub
  
   ' Get file extension
   lPos = InStrRev(1, strFile, ".")
   If lPos = 0 Then
      MsgBox "Filename must include an extension."
      Exit Sub
   Else
      strExten = Mid(strFile, lPos)
   End If
  
   ' Get programmatic identifier
   If RegOpenKey(HKEY_CLASSES_ROOT, _ 
                 strExten, _
                 hKey) <> _
                 ERROR_SUCCESS Then
      MsgBox "File extension not found."
      Exit Sub
   End If
   lStrLen = 0
   Call RegQueryValue(hKey, "", "", lStrLen)
   strProgID = Space(lStrLen)
   Call RegQueryValue(hKey, "", strProgID, lStrLen)
   RegCloseKey hKey
  
   ' Get associated application
   strProgID = Left(strProgID, lStrLen - 1) & _
               "\shell\open\command"
   If RegOpenKey(HKEY_CLASSES_ROOT, _
                 strProgID, _ 
                 hKey) <> _
                 ERROR_SUCCESS Then
      MsgBox "Open command key not found..."
      Exit Sub
   End If
  
   lStrLen = 0
   Call RegQueryValue(hKey, "", "", lStrLen)
   strExe = Space(lStrLen)
   Call RegQueryValue(hKey, "", strExe, lStrLen)
   RegCloseKey hKey
  
   ' Launch application and pass its 
   ' filename as a parameter
   lPos = InStr(1, strExe, " %1")
   If lPos > 0 Then _
      strExe = Left(strExe, lPos)
   strExe = strExe & " " & strFile
   Call WinExec(strExe, SW_SHOWNORMAL)
End Sub

Example 1-1 is a relatively long program, largely because of the intricacies of working with the Win32 API. We need, of course, to declare all registry-related functions with their parameters, as well as all constants that we intend to use. In addition, each registry access requires that we do the following:

  1. Open the relevant registry key.

  2. Determine the length of the string we want to retrieve.

  3. Set the string buffer to the appropriate length.

  4. Retrieve the registry value.

  5. Adjust the string containing the registry value by removing its terminating null character.

  6. Close the open registry key.

In contrast, the VB.NET program in Example 1-2 is considerably shorter and simpler. In contrast to the numerous Declare and Const statements in Example 1-1, the program only needs to use the Imports statement to indicate which namespaces it will access. Registry access is also significantly simpler. The program relies on two classes: the shared Registry class, which provides access to HKEY_CLASSES_ROOT (HKCR), one of the top-level registry keys; and the RegistryKey class, which represents a registry key. As a result, once the program obtains a reference to HKEY_CLASSES_ROOT, registry access consists of the following steps:

  1. Open the appropriate subkey by calling the top-level key's open method, passing it the path to the subkey to be opened.

  2. Retrieve the newly opened key's default value.

  3. Close the open registry key.

Example 1-2. Launching an application using the .NET FCL
Option Strict On
  
Imports System
Imports Microsoft.Win32
Imports Microsoft.VisualBasic
  
Public Module modMain
   Public Sub Main()
      Dim strExten, strProgID, strExe As String
      Dim oProgID, oOpenCmd As RegistryKey
  
      Dim strFile As String = InputBox("Enter Name" & _ 
                                       " of File" & _ 
                                       " to Open: ", _
                                       "Open File", "")
      If strFile = "" Then Exit Sub
  
      ' Get file extension
      Dim iPos As Integer = InStrRev(strFile, ".")
      Try
         strExten = Mid(strFile, iPos)
      Catch
         MsgBox("Filename must include an extension.")
         Exit Sub
      End Try
  
      ' Get Programmatic Identifier
      Dim oHKCR As RegistryKey = Registry.ClassesRoot
      Try 
         oProgID = oHKCR.OpenSubkey(strExten)
         strProgID = CStr(oProgID.GetValue(Nothing))
         oProgID.Close()
      Catch
         MsgBox("File extension not found.")
         Exit Sub
      End Try
  
      ' Get associated application
      Try
         oOpenCmd = oHKCR.OpenSubkey(strProgID & _
                    "\shell\open\command")
         strExe = CStr(oOpenCmd.GetValue(Nothing))
         oOpenCmd.Close()
      Catch
         MsgBox("Open command key not found...")
         Exit Sub
      End Try
  
      ' Launch application and pass its 
      ' filename as a parameter
      iPos = InStr(1, strExe, " %1")
      If iPos > 0 Then _
         strExe = Left(strExe, iPos)
      strExe = strExe & " " & strFile
  
      Call Shell(strExe, AppWinStyle.NormalFocus)
   End Sub
End Module

COM Automation

In place of the function-based programming using the Win32 API, COM automation represented a clear step forward. COM was a more or less object-oriented technology that held out the promise of language independence; as long as a language understood the Component Object Model, it should be able to access and take advantage of COM components.

Example 1-3 shows a VB 6 program written using COM automation that, like the programs in Examples 1-1 and 1-2, launches the application responsible for handling the data file whose name the user enters in a text box. Like the VB.NET program in Example 1-2, it is a short and fairly simple program that relies on the WScript object available from the Windows Script Host object model.

Example 1-3. Launching an application using COM automation
Option Explicit
  
Private Sub Main()
   On Error Resume Next
  
   Dim lPos As Long
   Dim strFile As String, strExten As String
   Dim strProgID As String, strExe As String
  
   strFile = InputBox("Enter Name of File to Open: ", _
                      "Open File", "")
   If strFile = "" Then Exit Sub
  
   ' Get file extension
   lPos = InStrRev(strFile, ".")
   If lPos = 0 Then
      MsgBox "Filename must include an extension."
      Exit Sub
   Else
      strExten = Mid(strFile, lPos)
   End If
  
   ' Initialize WSH Shell object
   Dim oShell As WshShell
   Set oShell = New WshShell
  
   ' Get programmatic identifier
   strProgID = oShell.RegRead("HKCR\" & strExten & "\")
   If Err.Number <> 0 Then
      MsgBox "File extension not found."
      Exit Sub
   End If
  
   ' Get associated application
   strProgID = "HKCR\" & strProgID & "\shell\open\command\"
   strExe = oShell.RegRead(strProgID)
   If Err.Number <> 0 Then
      MsgBox "Open command key not found..."
      Exit Sub
   End If
  
   ' Launch application and pass it 
   ' filename as a parameter
   lPos = InStr(1, strExe, " %1")
   If lPos > 0 Then _
      strExe = Left(strExe, lPos)
   strExe = strExe & " " & strFile
  
   oShell.Run strExe, 5, True
End Sub

Despite its substantial popularity, COM suffered from a number of limitations:

In addition, COM did not offer an integrated class library comparable to the .NET FCL. Instead, the developers of each application or operating system service were free to implement whatever object model made sense to extend their application. As a result, there are major gaps in the functionality made available through COM automation, and there is not a good deal of consistency across object models.

The .NET platform and the .NET Framework Class Library were developed in an effort to address these weaknesses of COM.

In the next installment, learn about the .NET Framework Class Library.

Budi Kurniawan is a senior J2EE architect and author.

Ted Neward is an independent software development architect and mentor in the Sacramento, California area.

VB.NET Core Classes in a  Nutshell

Related Reading

VB.NET Core Classes in a Nutshell
By Budi Kurniawan, Ted Neward

Copyright © 2009 O'Reilly Media, Inc.