Visual Basic Fusion: Best Practices to Use Visual Basic 6 and Visual Basic .NET Together
Swigart Consulting LLC.
Microsoft Visual Basic 6
Microsoft Visual Basic 2005
Microsoft Visual Studio 2005
Summary: A Microsoft Visual Basic 6 application can access .NET class libraries, but to do so, it must go through an interoperability layer, known as a client callable wrapper. This wraps the desired .NET class, and exposes it so that it appears as a traditional COM object, which can be used from any environment that can consume COM objects. Learn how to create these wrappers. (18 printed pages)
Download the Visual Basic Fusion—Best Practices.msi code sample for this article.
Understanding the Visual Basic 6 and Visual Basic .NET Runtimes
Calling Into the Framework
Creating Wrappers with Visual Basic .NET
Using the Sample Code
Using the Interop Forms Toolkit 2.0
Many organizations have a significant Microsoft Visual Basic 6 code base and are seeking to make the best decisions regarding this legacy code. There are fundamentally three choices when deciding what to do with Visual Basic 6 applications. You can maintain a Visual Basic 6 application as-is, without incorporating any new technology into this legacy application. You can migrate the Visual Basic 6 application fully to Visual Basic .NET. Or you can keep your Visual Basic 6 code, and interoperate with Visual Basic .NET to easily add any functionality from the .NET Framework to your existing application. Best practices for this last option, known as Visual Basic Fusion, will be discussed in this paper.
Visual Basic 6 applications are essentially COM applications. When you want to perform certain operations from Visual Basic 6, such as accessing a database or building a user interface, you add a reference to the appropriate COM component, and then use the functionality that it contains. Other applications also typically expose functionality through COM components. For example, a Visual Basic 6 application can automate Microsoft Word or Excel because those applications expose COM interfaces.
Visual Basic .NET applications are not COM-based, and by default, do not expose COM interfaces. Visual Basic .NET applications access functionality in .NET class libraries. The Microsoft .NET Framework includes thousands of class libraries that let Visual Basic .NET applications communicate with databases, and to access the Internet, registry, file system, event log, system information, and more. The amount of functionality available in the .NET Framework Class Libraries (FCL) is significantly greater than the functionality available to Visual Basic 6.
Visual Basic 6 and Visual Basic .NET are built on incompatible technologies. A Visual Basic 6 application can access .NET class libraries, but to do so, it must go through an interoperability layer, known as a client callable wrapper. This wraps the desired .NET class, and exposes it so that it appears as a traditional COM object, which can be used from any environment that can consume COM objects (Visual Basic 6, Visual Basic 5, ASP, VBA, VBScript, and so on.).
As shown in the Using the .NET Framework Class Library from Visual Basic 6 article, the first step in calling into the .NET framework is to register the needed .NET framework DLL (known as an assembly) as a COM object. This can be accomplished with the regasm command line utility available with the .NET Framework SDK, which is installed as part of the Visual Basic Express product. If you don't have Visual Studio 2005 installed, I strongly recommend that you download and install the free Visual Basic Express.
Walkthrough 1. Registering the System.dll so that it can be used from Visual Basic 6
- Navigate to Start | Run.
- In the Run dialog, enter CMD, and click OK.
- Enter cd C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727.
- Enter regasm system.dll.
Once the assembly is registered, you can add a reference to it from Visual Basic 6, as though it were any other COM object:
Figure 1. Adding a reference to system.dll from Visual Basic 6
Again, Visual Basic 6 will be able to call into the System.dll .NET assembly because a COM callable wrapper has been created for it. However, for a .NET class and its functions to appear in the COM callable wrapper, the .NET class has to follow a set of fairly restrictive rules. As a result, only a very tiny percentage of the classes in System.dll will be callable directly from Visual Basic 6. One function that is callable from Visual Basic 6 is the WebClient.DownloadFile function.
Listing 1. Calling the DownloadFile function from Visual Basic 6
Dim webDownload As System.WebClient Set webDownload = New System.WebClient webDownload.downloadFile _ "http://www.ssec.wisc.edu/data/west/latest_westir.jpg", _ App.Path & "\latest_westir.jpg"
The previous example should not create the false impression that you can simply use Regasm with any .NET assembly and have all the .NET functionality available to Visual Basic 6. In almost every case, when you want to call into the .NET framework, 3rd party .NET components, or open source .NET libraries, you will have to write Visual Basic .NET wrapper classes. This is because a .NET class has to follow fairly strict rules to properly present itself as a COM object, and most .NET classes do not follow these rules.
To access the .NET functionality, you create a class in Visual Basic .NET that does follow the rules for COM interoperability, and this class calls into the desired .NET code on behalf of the Visual Basic 6 application.
Figure 2. Calling into the .NET Framework from Visual Basic 6 through Visual Basic .NET wrapper classes
Simple Wrapper Class
For a simple example, consider the .NET Framework Guid class. A GUID is a "Globally Unique Identifier." GUIDs are used many places in Windows to uniquely identify COM objects, files, users, and just about anything else. They take the form of strings such as: 13427d57-ccb5-42fd-91f7-b49d4c2ae9a3. The great thing about GUIDs is that if you properly generate one, you can be absolutely 100 percent certain that no one else will ever generate the same one.
In Visual Basic 6, there's no simple way to generate a GUID, but in Visual Basic .NET, it's just one line of code:
Listing 2. Generating a GUID in Visual Basic .NET
Dim g as Guid = Guid.NewGuid()
Unfortunately, the Guid class wasn't created in such a way that you can directly call it from Visual Basic 6. To use this functionality from Visual Basic 6, you create a very simple Visual Basic .NET wrapper class. This Visual Basic .NET wrapper class will expose itself as a COM object usable from Visual Basic 6, and make the call needed into the .NET Framework Guid class.
Details of creating wrapper classes are covered in "Can I Interest you in 5000 Classes ," but the basics will be repeated here:
Walkthrough 2. Creating the Visual Basic .NET wrapper for the Guid class
- Download and extract the sample code for this article.
- Copy the ComClass.zip file to C:\Documents and Settings\[User Name]\My Documents\Visual Studio 2005\Templates\ItemTemplates\Visual Basic.
If you are using Visual Studio 2005, you can skip step 2. However, Visual Basic Express does not include the COM Class template by default, and this template is very useful for exposing the .NET framework as COM objects. By copying the ComClass.zip file into this folder, the COM Class template will be available to Visual Basic Express.
- From Visual Basic Express, select the File | New Project menu command.
- In the New Project dialog:
- Select Class Library for the Template.
- Enter NetFxWrapper for the Name. This will become the name of the component and will appear in the list of references available in Visual Basic 6.
Figure 3. Creating the wrapper project in Visual Basic .NET
- Click OK.
- In the Solution Explorer, delete Class1.
- Select the Project | Add New Item menu command.
- In the Add New Item dialog:
- For Templates, select COM Class.
- For the Name field, enter GuidWrapper.
Figure 4. Creating the Visual Basic .NET Wrapper COM class
- Click Add.
- On line 20 (after End Sub, and before End Class) enter the following:
Listing 3. Wrapper function in Visual Basic .NET
Public Function NewGuid() As String Dim g As Guid = Guid.NewGuid() Return g.ToString() End Function
This will create an instance of a Guid class, and assign it to a newly generated Guid value. It will then convert the Guid value to a string, and return it.
- Select the Build | Build NetFxWrapper menu command. This will compile the component and register it as a COM object.
Now that you've created the wrapper, you can call it from Visual Basic 6.
Walkthrough 3. Using the wrapper from Visual Basic 6
- Start Microsoft Visual Basic 6.0.
- In the New Project dialog, select Standard Exe, and click Open.
- Select the Project | References menu command.
- Add a reference to NetFxWrapper.
Figure 5. Adding a reference to a COM object created in Visual Basic .NET
- Add a CommandButton and TextBox to the form.
- Set the Caption property of the CommandButton to Generate.
- For the click event of the CommandButton, enter:
Listing 4. Calling the wrapper from Visual Basic 6
Private Sub Command1_Click() Dim gw As NetFxWrapper.GuidWrapper Set gw = New NetFxWrapper.GuidWrapper Text1.Text = gw.NewGuid End Sub
- Run the application. Every time you click the button, the Visual Basic 6 application will call into the Visual Basic .NET wrapper and generate a new GUID. The GUID will be displayed in the TextBox.
Figure 6. Displaying the GUID
Rules for Creating Wrappers
The wrapper class is needed because the Guid class does not adhere to the rules for COM interoperability. These rules, which your wrapper class must follow, are:
- Wrapper must expose a default constructor
In Visual Basic 6, you first create an instance of an object, and then you use properties and methods to initialize the object. For example:
Listing 5. Initializing a connection object in Visual Basic 6
Dim cn As ADODB.Connection Set cn = New ADODB.Connection cn.ConnectionString = _ "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=mydb.mdb;" & _ "User Id=admin;Password=;"
To set up a connection object, you must first create an instance of it, and then set properties such as the ConnectionString. In Visual Basic 6, creating an object, and initializing its properties, must be done as separate steps.
However, in Visual Basic .NET, you can specify initialization information at the time you create an instance of the object. Equivalent code in Visual Basic .NET is:
Listing 6. Passing arguments to the constructor in Visual Basic .NET
Dim cn As New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=mydb.mdb;User Id=admin;Password=;")
You can see that the object is created and initialized in one line of code. This is possible because .NET types have a Sub New that is called when the object is created, and this Sub New can take arguments. A class can even have multiple Sub New constructor methods, each taking different types or numbers of arguments.
Listing 7. Visual Basic .NET class constructors
Public Sub New() End Sub Public Sub New(ByVal connectionString As String) End Sub
When you create an instance of a class, the Visual Basic .NET compiler figures out which Sub New to call automatically. If you created an instance of an OleDbConnection without any arguments, it would call the Sub New that doesn't take any arguments. If you create an instance of OleDbConnection with a connection string argument, the compiler automatically generates a call to the Sub New that takes a string as an argument.
The Sub New that doesn't take any arguments is known as the "default constructor," meaning that this is the one called, by default, if no arguments are supplied.
Since Visual Basic 6 cannot pass arguments at the time you create an instance of an object, a .NET class must provide a default constructor or you cannot use it directly from Visual Basic 6. One reason many of the .NET Framework classes are not directly usable is because their Sub New methods all require arguments. They don't expose a default constructor, so you have no way to create an instance of them from Visual Basic 6.
You can create wrapper classes for these objects. Your wrapper exposes a default constructor, but when your wrapper creates an instance of the actual .NET framework class, it passes the needed arguments to the Sub New of that class.
For a concrete example, consider the .NET Framework FileInfo class. This class will let you get many characteristics of a disk file. However, it does not contain a default constructor. The Sub New for FileInfo requires the path to the desired file, but it can be wrapped as follows:
Listing 8. Visual Basic .NET Wrapper for FileInfo
Private fi As FileInfo Public Sub New() End Sub Public Sub Initialize(ByVal filePath As String) fi = New FileInfo(filePath) End Sub
This wrapper contains a Sub New that accepts no arguments. This will allow this class to be registered for COM Interop so that a Visual Basic 6 application can create an instance of it. The internal FileInfo isn't created until the Visual Basic 6 application calls the Initialize method. This would be used from Visual Basic 6 as follows:
Listing 9. Using the FileInfo wrapper from Visual Basic 6
Dim fi As NetFxWrapper.FileInfoWrapper Set fi = New NetFxWrapper.FileInfoWrapper fi.Initialize ("c:\somefile.txt")
- Many data types must be converted between Visual Basic 6 and Visual Basic .NET
Most of the work in creating a wrapper boils down to converting Visual Basic .NET data types to something that Visual Basic 6 can work with. Consider the following two methods; one returns a string, and the other returns a Guid data type:
Listing 10. Wrapper function in Visual Basic .NET
Public Function NewGuidAsString() As String Dim g As Guid = Guid.NewGuid() Return g.ToString() End Function Public Function NewGuidAsGuid () As Guid Dim g As Guid = Guid.NewGuid() Return g End Function
From Visual Basic 6, calling NewGuidAsString will work just fine, as strings are easily passed between Visual Basic 6 and Visual Basic .NET. However, calling NewGuidAsGuid from Visual Basic 6 will generate the following error:
Figure 7. Error generated by calling NewGuidAsGuid from Visual Basic 6
This is because an object of type Guid cannot be passed directly to Visual Basic 6 from Visual Basic .NET. When you create wrappers for .NET classes, most of your time will be spent converting .NET data types to types that can be used from Visual Basic 6.
You can also use the .NET MarshalAs attribute to specify which Visual Basic 6 type should be used for arguments and return values. For example, if a Visual Basic .NET function returns a Decimal data type, this function cannot be called from Visual Basic 6. However, you can specify that the return value should be converted to a Currency type. This will let Visual Basic 6 consume the function without error.
Listing 11. Using MarshalAs to convert between Visual Basic 6 and Visual Basic .NET types:
Public Function ReturnDecimal() As _ <Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.Currency)> _ Decimal Return 1.1 End Function
The following table lists the automatic conversions done between Visual Basic .NET and Visual Basic 6 types:
Table 1. Type conversion from Visual Basic .NET to Visual Basic 6
Visual Basic .NET Type Visual Basic 6 Type Boolean Boolean Byte Byte Char Not supported, instead return to Visual Basic 6 as a string. DateTime Date Decimal Not supported, instead return as a Single, Double, or use the MarshalAs attribute to return as a Visual Basic 6 Currency. Double Double Guid Not supported, instead return to Visual Basic 6 as a string. Integer Long Long Not supported Object Object SByte Not supported in Visual Basic 6, instead return as an Integer. Short Integer Single Single String String TimeSpan Not supported, use functions like TotalSeconds or TotalMinutes and return as a Double.
- Shared functions don't interoperate
In Visual Basic 6, you have global functions that can be called from anywhere in your code. A problem with global functions is that it's hard to know that two different global functions are logically related. In Visual Basic .NET, you can instead group global functions together inside of a class. The advantage is that you can call the function by using the name of the class, without having to create an actual instance of the class:
Listing 12. Shared function
If Not File.Exists(someFile) Then MessageBox.Show("File not found") End If
In this example, the Exists function is being called right off the File class. The code has not created an instance of the File class to call this method; instead, Exists is being called right off of the File type. These kind of functions are known as Shared (or Static) functions, and they are use throughout the .NET Framework
A problem with Shared functions is that they don't interoperate with Visual Basic 6. For Visual Basic 6 to call a function the function has to be truly global, or called off of an instance of a class. Shared functions are neither.
The solution is to create a wrapper that you can create an instance of, and then use the wrapper to call the Shared function.
Listing 13. Wrapper that calls a shared function
Imports System.IO <ComClass(FileWrapper.ClassId, FileWrapper.InterfaceId, FileWrapper.EventsId)> _ Public Class FileWrapper Public Const ClassId As String = "1596574F-9EE1-4439-854B-DF503099CEAE" Public Const InterfaceId As String = "02F98DBC-9298-4E9D-A991-E618C1B1BC53" Public Const EventsId As String = "2286FD23-B22D-4D49-B2CE-F3CFEC5C1CA5" Public Sub New() MyBase.New() End Sub Public Function Exists(ByVal fileName As String) As Boolean Return File.Exists(fileName) End Function End Class
The FileWrapper class can be created from Visual Basic 6 because it exposes a default constructor. When the Exists function is called, it calls the shared File.Exists .NET function and returns the results. Visual Basic 6 is calling an instance method of FileWrapper, and FileWrapper is calling a shared method of the .NET Framework File class.
From Visual Basic 6, this can be used as follows:
Listing 14. Using File.Exists from Visual Basic 6
Private Sub Command5_Click() Dim fw As New NetFxWrapper.FileWrapper Label2 = fw.Exists("c:\platform.ini") End Sub
- Wrappers should not use overloaded functions
The .NET Framework allows you to have multiple functions with exactly the same name, but different arguments. Consider the following:
Listing 15. Overloaded functions in Visual Basic .NET
Public Sub Save(ByVal fileName As String) _image.Save(fileName) End Sub Public Sub Save(ByVal fileName As String, ByVal format As ImageFormatWrapper) Dim imageFormat As Imaging.ImageFormat Select Case format Case ImageFormatWrapper.Bmp imageFormat = Imaging.ImageFormat.Bmp Case ImageFormatWrapper.Emf imageFormat = Imaging.ImageFormat.Emf Case ImageFormatWrapper.Exif imageFormat = Imaging.ImageFormat.Exif Case ImageFormatWrapper.Gif imageFormat = Imaging.ImageFormat.Gif Case ImageFormatWrapper.Icon imageFormat = Imaging.ImageFormat.Icon Case ImageFormatWrapper.MemoryBmp imageFormat = Imaging.ImageFormat.MemoryBmp Case ImageFormatWrapper.Png imageFormat = Imaging.ImageFormat.Png Case ImageFormatWrapper.Tiff imageFormat = Imaging.ImageFormat.Tiff Case ImageFormatWrapper.Wmf imageFormat = Imaging.ImageFormat.Wmf End Select _image.Save(fileName, imageFormat) End Sub
One function takes a file name as an argument, and the other takes an additional image format argument. Both functions are named Save. When these functions are called from Visual Basic .NET, the compiler can determine which to call based on the arguments passed. If just one argument is passed, then the first function is called. If two arguments are passed, then the second function is called.
COM does not support overloaded functions, so if this class is exposed through COM interop, the COM wrapper must generate unique function names for these functions. To Visual Basic 6, they appear as follows:
Figure 8. Calling overloaded functions from Visual Basic 6
Notice that the Save functions are named Save and Save_2. This lets you identify which you want to call from Visual Basic 6, but the names are not descriptive. It would be better to not use overloads in your wrapper, and instead give each function a unique name:
Listing 16. Unique names for functions
Public Sub Save(ByVal fileName As String) _image.Save(fileName) End Sub Public Sub SaveWithImageFormat(ByVal fileName As String, _ ByVal format As ImageFormatWrapper) Dim imageFormat As Imaging.ImageFormat Select Case format Case ImageFormatWrapper.Bmp imageFormat = Imaging.ImageFormat.Bmp Case ImageFormatWrapper.Emf imageFormat = Imaging.ImageFormat.Emf Case ImageFormatWrapper.Exif imageFormat = Imaging.ImageFormat.Exif Case ImageFormatWrapper.Gif imageFormat = Imaging.ImageFormat.Gif Case ImageFormatWrapper.Icon imageFormat = Imaging.ImageFormat.Icon Case ImageFormatWrapper.MemoryBmp imageFormat = Imaging.ImageFormat.MemoryBmp Case ImageFormatWrapper.Png imageFormat = Imaging.ImageFormat.Png Case ImageFormatWrapper.Tiff imageFormat = Imaging.ImageFormat.Tiff Case ImageFormatWrapper.Wmf imageFormat = Imaging.ImageFormat.Wmf End Select _image.Save(fileName, imageFormat) End Sub
Now each function has a unique name, and is easier to use from Visual Basic 6.
Figure 9. Functions with unique names
- Wrappers should be placed in the Global Assembly Cache
When a COM object is registered, the system registry contains the file path to the COM object DLL. This way, when your program creates an instance of the COM object, the operating system can find that DLL and load it into memory.
.NET assemblies work differently. The .NET framework expects the assembly to be in the same location as the executable that's using it, or it expects the assembly to be in a location called the Global Assembly Cache (GAC). To be sure that your .NET wrapper classes can always be found at runtime, they should be placed in the GAC.
Putting an assembly in the GAC is a two step process. First, the assembly must be signed. This can be accomplished as follows:
- Return to the NetFxWrapper project in Visual Basic Express.
- In the Solution Explorer, double-click My Project. This will open up the project property pages.
- Click the Signing tab.
- Click the Sign the Assembly check box.
- In the Choose a strong name key file drop-down, click <New...>.
- For Key file name, enter MyKeyFile.
- Uncheck the Protect my key file with a password check box.
- Click OK.
- Select the Build | Build NetFxWrapper menu command.
This has added a key file to the project, and every time the project is compiled, the resulting DLL is signed with this key. Assemblies must be signed before they can be placed in the global assembly cache.
- Navigate to Start | Run.
- Enter cmd and click OK to open a command window.
- In the command window, change to the directory that the wrapper class is compiled to. On my machine, this is: C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\NetFxWrapper\NetFxWrapper\bin\Release.
- Enter "C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil" -i NetFxWrapper.dll.
Note Pay special attention to the quotes around the path to gacutil. You should get a message saying the assembly was successfully added to the global assembly cache.
When you deploy a Visual Basic 6 application that uses a wrapper class, your application installer will need to use gacutil to install the wrapper class into the GAC.
While this article has focused on the best practices when using the Visual Basic Fusion approach, it's worth taking a moment to examine all the options for dealing with Visual Basic 6 applications, and determine if Visual Basic Fusion, or some other tactic, is the best option for a particular Visual Basic 6 application.
The options include maintaining a Visual Basic 6 application as-is, migrating the entire application to.NET, or using the Visual Basic Fusion technique. Each is the appropriate solution for certain situations.
In this option, you are electing to simply keep an application written in Visual Basic 6 as Visual Basic 6, and not use the .NET framework at all.
Simple: The application was written in Visual Basic 6 (or migrated to Visual Basic 6), so keeping the application here is the simplest option. It's likely that your developers already know this language and development environment, and can make many changes or bug fixes to the application rapidly.
Adding New Functionality: Visual Studio 6.0 was released in 1998. Visual Basic 6 has not been enhanced, with the exception of service packs, since then. However, the functionality expected of today's applications has changed. Applications are often expected to auto-update when a new version is available, work off-line, cache data locally, incorporate Internet functionality, possibly communicate with Web services, and more. While technology exists today to make these tasks easy, this technology is not available in Visual Basic 6, so making these kinds of enhancements with only Visual Basic 6 can be extremely labor intensive. The end result is that many Visual Basic 6 applications simply won't be enhanced to include this type of new functionality because it's too costly and difficult to do so with only Visual Basic 6.
Vanishing Point: Since the introduction of Visual Basic .NET in 2002, there has been a migration of developers from Visual Basic 6 to Visual Basic .NET. As a result, the vast majority of new Visual Basic information (books, articles, blog posts, newsgroup posts, and so on) have been about Visual Basic .NET. At the same time, the amount of information available regarding Visual Basic 6 has been declining. In addition, many developers feel that moving to Visual Basic .NET puts them on a stronger career path, as this language will continue to be updated and enhanced. This does not mean to imply that as of this writing, it's hard to find technical help on-line for Visual Basic 6 issues, or that it's hard to hire a Visual Basic 6 developer, but over time, Visual Basic 6 is expected to go the path of many legacy languages, both in terms of information available, and talent pool.
Migrate to Visual Basic .NET
This option entails porting a Visual Basic 6 application completely to Visual Basic .NET.
Access to technology: Migrating an application to Visual Basic .NET is the best way to insure that you will be able to quickly incorporate new innovations and technologies. Microsoft and other companies are focusing much of their efforts on functionality for the .NET Framework. This means that new SDKs will favor, and possibly only work with, .NET languages. New technologies like Web services, application auto-updating, and Internet functionality (http and ftp support) are natively available to .NET applications, but will not be back-ported all the way to Visual Basic 6. Sites like GotDotNet and SourceForge contain thousands of redistributable components, with source code, that can be used directly from .NET applications.
Developer productivity: In addition, new versions of Visual Studio, which are vastly more productive than Visual Studio 6.0, can only be used with .NET applications.
Risk: Migrating an entire application from Visual Basic 6 to Visual Basic .NET is not trivial. If the application is critical, and the developers are not experienced with Visual Basic .NET, then there is risk that the migration will not be successful.
Time: It takes a certain amount of time to migrate an application and get it to the point where it does in .NET what it did in Visual Basic 6. While this puts the application on a much better footing for future enhancements, the time and cost to migrate cannot be ignored.
Visual Basic Fusion
Visual Basic Fusion is the act of using .NET functionality from within a Visual Basic 6 application.
Access to technology: By writing small Visual Basic .NET shims, you can access the full .NET framework class library, call Web services, or use 3rd party or open source .NET libraries from your Visual Basic 6 applications.
Low Risk: Most of the application code remains in Visual Basic 6, and continues to work as-is. The amount of Visual Basic .NET code that you write is relatively small. This means that you don't have the risk of a failed migration, because you aren't migrating the entire application. Also, developers who are not experienced with Visual Basic .NET need only learn small portions of the .NET Framework to access needed functionality.
High ROI: The investment in this approach is very low compared with migrating an entire application, so the return on the investment is high, especially in the short term.
Visual Basic 6: Since most of the application remains in Visual Basic 6, you are required to continue to use the Visual Basic 6 IDE and the Visual Basic 6 language when working on the application. This language and IDE are not as productive as Visual Basic .NET.
Debugging: To effectively debug an application that uses Visual Basic 6 and Visual Basic .NET code, you need to have both development environments open at the same time, and there's special knowledge that's required to debug effectively between Visual Basic 6 and Visual Basic .NET.
Wrapper Development: To access .NET functionality from Visual Basic 6, you are required to write wrappers around the .NET classes so that they can be exposed as COM objects usable from Visual Basic 6. These wrappers would not be required if the entire application was migrated to Visual Basic .NET.
This article includes sample code which illustrates all the discussed topics. The sample code will extract to a folder named "Best Practices Code." Inside of this folder, you will find the following:
- Install.bat—Used to register and install the .NET wrappers so they can be used from Visual Basic 6.
- NetFxWrapper—Folder that contains the source code for the Visual Basic .NET wrapper classes.
- Best Practices Visual Basic 6—Folder that contains the Visual Basic 6 sample which uses the wrapper class.
- ComClass.zip—Contains the ComClass project item. If this file is placed in your C:\Documents and Settings\[User Name]\My Documents\Visual Studio 2005\Templates\ItemTemplates folder, then this item template will be available to your Visual Basic Express projects.
If you do not have Visual Studio 2005 or Visual Basic Express installed, you can simply double-click on the install.bat to register the wrapper classes. You can then open the Visual Basic 6 project, run it, and examine the code to see how it uses the wrappers.
If you have Visual Studio 2005 or Visual Basic Express, you can also open the source code for the wrapper classes, and examine how they are constructed.
The Interop Forms Toolkit 2.0, released in June 2007, enables the Visual Basic Fusion approach by allowing you to extend your Visual Basic 6 applications with .NET components. The Toolkit makes it easy to display .NET Forms and UserControls in Visual Basic 6 as if they were regular ActiveX controls. It also provides additional capabilities around things like application-level events and global variables. Overall it’s very easy to use, and the Microsoft-recommended approach for extending Visual Basic 6 applications.
Visual Basic Fusion is a useful approach for existing Visual Basic 6 applications that must be enhanced on an on-going basis. Visual Basic Fusion lets you access any of the functionality provided by the .NET Framework from your existing applications without rewriting them. To use the Visual Basic Fusion approach, you must create Visual Basic .NET wrapper classes to access .NET Framework functionality. These wrapper classes are often trivial to write, but must follow certain rules so that they expose themselves correctly as COM objects. This article has shown you the best practices for creating wrapper classes, and has provided guidelines for determining if Visual Basic Fusion, or some other approach, should be used for existing Visual Basic 6 applications.
About the author
Scott Swigart (www.swigartconsulting.com) spends his time consulting with companies on how to best use today's technology and prepare for tomorrows. Along this theme, Scott is a proud contributor to the Visual Basic Fusion site, as it offers information and tactics of real use for Visual Basic developers who want to build the most functionality with the least effort. Scott is also a Microsoft MVP, and co-author of numerous books and articles. If you have any question or comments about this article, feel free to send e-mail to firstname.lastname@example.org.