VB .NET is a programming language designed to create applications that work with Microsoft's new .NET Framework. The .NET platform in turn aims at addressing many of the limitations of "classic" COM, Microsoft's Component Object Model, which provided one approach toward application and component interoperability. These limitations included type incompatibilities when calling COM components, versioning difficulties ("DLL hell") when developing new versions of COM components, and the need for developers to write a certain amount of code (mostly in C++) to handle the COM "plumbing." In contrast to VB, with its reliance on COM, VB .NET offers a number of new features and advantages. Let's take a look at some of these.
With the release of Version 4, Visual Basic added support for classes and class modules and in the process became an object-oriented programming language. Yet the debate persists about whether Visual Basic is a "true" object-oriented language or whether it only supports limited features of object orientation.
The debate centers around Visual Basic's support for inheritance, an object-oriented programming concept that allows a class to derive its properties and its functionality from another class. Proponents of the view that Visual Basic is object-oriented point to Visual Basic's support for interface-based programming and the use of virtual base classes. Yet relatively few VB programmers take advantage of interface-based programming. And interface-based programming itself does not allow a derived class to inherit the functionality of a base class; only virtual base classes can be inherited using the Implements keyword.
While the object-oriented character of previous versions of VB may be in doubt, there is no question that VB .NET is an object-oriented programming language. In fact, even if VB .NET is used to write what appears to be procedural code, it is object-oriented "under the hood," so to speak. Let's take as a simple example the clearly procedural, nonobject-oriented program. If we use ILDASM (.NET's intermediate language disassembler) to look at the IL generated for this source code, we see that internally, modMain is in fact defined as a class that has two methods, Increment and Main.
Figure : A procedural program shown using ILDASM
Example : A procedural program for VB .NET
Public Module modMainPublic Sub Main( )Dim x As Integerx = 10MsgBox(Increment(x))End SubPrivate Function Increment(iVar As Integer)Return(iVar+1)End FunctionEnd Module
A Common Type System
Traditionally, one of the problems of calling routines written in languages from Visual Basic or of calling Visual Basic routines from other languages is that such inter-language calls presuppose a common type system. This is the case when calling Win32 API functions from Visual Basic, but it is also applies to attempts to call methods in a VB COM component from other languages or to call methods in a non-VB COM component from VB.
For instance, until the addition of the AddressOf operator, which allows us to pass a pointer to a function or subroutine, there was no way to provide a callback function, which is required by most Win32 API enumeration functions. As another example, it is expected that members of structures passed to Win32 API functions be aligned on their natural boundaries, something that VB programmers had great difficulty accomplishing.
Problems of type compatibility tended to occur most often when scripted applications were used to call and pass arguments to COM components. An excellent example is the attempt to pass an array from a script written in JScript to a COM component, since COM sees JScript arrays as a string of commadelimited values rather than a COM-compatible array (called a SafeArray).
The .NET platform removes these difficulties by providing a common type system. Ultimately, all data types are either classes or structures defined by or inherited from the .NET Base Class Library. This common type system means that .NET components will be truly language-independent and that a .NET component written in one language will be seamlessly interoperable with .NET components written in any other .NET language. The problem of incompatible types simply disappears. On the surface, VB has retained its old type system. VB still supports the Long data type, for instance, although it is now a 64-bit data type instead of the 32-bit data type of VB 4 through VB 6. Casual inspection of the code shown suggests that VB has retained its type system.
However, if we use ILDASM to examine the IL generated from this Visual Basic code, we see that VB data types are merely wrappers for data types provided by the .NET Framework.
Figure : Wrapping the .NET type system
Example : Using the Visual Basic type system
Public Module modMain Public Sub Main( ) Dim s As String = "This is a string." Dim l As Long = 12344 Dim i As Integer = 10 End Sub End Module
The simple program in Example also supports this conclusion. The program instantiates an integer of type Long, a standard Visual Basic data type. It then calls the ToString method?a method of the Int64 class?to convert that number to its string representation. In other words, the variable l is really an Int64 data type masquerading as a traditional VB Long data type.
Example : Calling .NET type methods from a VB data type
Public Module modMainPublic Sub Main( )Dim l As Long = 64.31245Dim s As Strings = l.ToStringMsgBox(s)End SubEnd Module
Access to System Services: The Framework Class Library
Ever since VB added support for calls to routines in the Windows and Win32 APIs, many Visual Basic programmers came to regard API programming as a kind of black art. Not only were there a confusing and seemingly limitless array of functions that might be called, but also passing parameters to routines and receiving their return values often seemed to be a mysterious process. Moreover, with the growing emphasis on object-oriented programming, the Win32 API, with its function-based approach to programming, seemed more and more archaic.
Although the Declare statement remains in VB and programmers can still call the Win32 API and routines in other external Windows DLLs, many of the common system services provided by the Win32 API, as well as by some COM components, are now provided by the .NET Framework Class Library. The Framework Class Library is a collection of types (classes, structures, interfaces, delegates, and enumerations) organized into namespaces.
To get some sense of the difference in programming style between the Win32 API and the .NET Framework Class Library, as well as to appreciate the simplicity and ease with which the Framework Class Library can be accessed, compare Examples 1-6 and 1-7. Example 1-6 is a VB 6 routine that creates a value entry in the registry to load a particular program on Windows startup. Note that all API constants must be defined, as must the API functions themselves.
In addition, the API functions must be called correctly. In particular, to avoid passing a BSTR rather than a C null-terminated string to the RegSetValueEx function, the string must be passed using the ByVal keyword. This is a common oversight that usually causes an application crash. In contrast, Example shows the comparable VB .NET code that uses the RegistryKey class in the Microsoft.Win32 namespace of the Framework Class Library. Note that the code is short and simple, and, therefore, far less error-prone.
Example : Writing to the registry using the Win32 API
Private Const ERROR_SUCCESS = 0&Private Const HKEY_CLASSES_ROOT = &H80000000Private Const HKEY_CURRENT_CONFIG = &H80000005Private Const HKEY_CURRENT_USER = &H80000001Private Const HKEY_DYN_DATA = &H80000006Private Const HKEY_LOCAL_MACHINE = &H80000002Private Const HKEY_PERFORMANCE_DATA = &H80000004Private Const HKEY_USERS = &H80000003Private Const REG_SZ = 1Private Const KEY_SET_VALUE = &H2Private Declare Function RegCloseKey Lib "advapi32.dll" _(ByVal hKey As Long) As LongPrivate Declare Function RegOpenKeyEx Lib "advapi32.dll" _Alias "RegOpenKeyExA" _(ByVal hKey As Long, ByVal lpSubKey As String, _ByVal ulOptions As Long, ByVal samDesired As Long, _phkResult As Long) As LongPrivate Declare Function RegSetValueEx Lib "advapi32.dll" _Alias "RegSetValueExA" _(ByVal hKey As Long, ByVal lpValueName As String, _ByVal Reserved As Long, ByVal dwType As Long, lpData As Any, _ByVal cbData As Long) As LongPrivate Sub LoadByRegistry( )Const cPGM As String = "C:TestTestStartup.exe"Dim hKey As Long, nResult As LongnResult = RegOpenKeyEx(HKEY_CURRENT_USER, _"SoftwareMicrosoftWindowsCurrentVersionRun", 0, _KEY_SET_VALUE, hKey)If nResult = ERROR_SUCCESS ThenRegSetValueEx hKey, "MyVBApp", 0, REG_SZ, ByVal cPGM, Len(cPGM)RegCloseKey hKeyEnd IfEnd Sub
Example : Writing to the registry using the Framework Class Library
Private Const cPGM As String = "C:VB ForumstartupTestStartup.exe"Private Shared Sub LoadByRegistry( )Dim oReg As RegistryKey = Registry.CurrentUserDim oKey as RegistryKey = _oReg.OpenSubKey("SoftwareMicrosoftWindowsCurrentVersionRun", _True)oKey.SetValue("MyVBApp", cPGM)EndSub
A Common Runtime Environment
Although VB traditionally had shielded the developer from many of the intricacies of Windows as an operating system or of COM as a method for interoperability, nevertheless, some slight knowledge of how the system worked was essential, or the developer was sure to run into trouble sooner or later. For instance, consider the following code fragment for VB 6:
Dim oObj As New cSimpleClassSet oObj = NothingIf oObj Is Nothing Then' Perform cleanupEnd If
Because of an idiosyncrasy of VB, objects declared and instantiated using the New keyword on the same line of code are not actually created until the first reference to that object. As a result, our attempt to determine if the object is Nothing instead recreates the object, and our cleanup code never executes.
This, at least, is usually a relatively benign error. Much more pernicious, however, are circular object references, where COM objects hold references to one another and therefore cannot be released, even though they've been set to Nothing in code. This situation creates a memory leak that eventually can result in a General Protection Fault.
Under .NET, many problems like these are eliminated because of the .NET platform's Common Language Runtime (CLR). The CLR, as its name clearly implies, provides a variety of services to applications and processes running under the .NET platform, regardless of the language in which they were originally written. These services include memory management and garbage collection. They also include a unified system of exception handling, which makes it possible to use the same set of debugging tools on all code, regardless of the particular .NET language in which it was written.