Introduction
NOTE: Not all code is in the page, there are constructors missing and other directives that are available in the download of the project.
This article will help you in the special case when you want to use a class "designed" in .NET in VB6 using the "COM class" available in Visual Basic .NET.
Background
There are still tons of companies trying to move from VB6 to .NET. This can be as easy as run the integrated VS conversion tool or if the company has the resources and time they will decide to do a rewrite.
What if this rewrite is in a new architecture, using n-tier and common DLLs that most of the new applications will use, cool... however what if once you finished one of your .NET DLLs, you have to integrate that new library into one of your VB6 apps.
Not really difficult using the "COM class" available in VB.NET and creating a Business Logic layer that calls your component, create a *.tlb file and reference this one in your VB6 project. However some types are not available in VB6; like the .NET Long
doesn't exist in VB6, and the .NET Integer
is actually the Long
in VB6. Not mentioning that Lists called Generics are not available at all in VB6, you could use array, but they work differently in .NET than in VB6.
Using the Code
Let's start with a simple problem.
You have the following classes:
You need to get a list of car dealers available, each dealer will have a description and a list of cars and each car its maker, model, mileage and price. So you have:
public class car
{
public string maker { get; set; }
public string model { get; set; }
public int mileage { get; set; }
public int price { get; set; }
}
public class carDealer
{
public string description { get; set; }
public List<car> carList { get; set; }
}
public class dealersList
{
public List<carDealer> list { get; set; }
}
You can get your data from your database, my example just hardcodes some values.
The important thing is, at the end you will have a list of the carDealer
class to return as a result. Your main manager class will look something like:
public class componentManager
{
private componentBusinessLogic _bl = new componentBusinessLogic();
public dealersList getDealers()
{
return _bl.getDealers();
}
}
You pretty much finished your DLL, your component to retrieve a list of dealers You could even alter a little bit of this class and accept a zipCode
parameter and retrieve based on that, but for ease of the article we will leave it like that, just retrieve all car dealers.
Now it's time to create the Interop traffic controller your VB6 project will use. You can do this using the COM class
in a Visual Basic .NET project. So create a new VB.NET class library project. This basically builds a ready to use interface for you, you just need to create a traffic manager between your C# class and your VB6 project. We will call this the Wrapper Component from now on.
Something like:
Public Function GetDealers() As dealerList
Return _bl.getDealers()
End Function
Here is the tricky thing. You could declare your carDealer
class as a List
(of car
) right?
The answer is no... generics are not supported in VB6, so what to do? Arrays? Not really, they are not dynamic like the generics. The solution: Collection.
By the way... _bl
in the code snippet above is an instance of my wrapper business logic which contains the logic to apply this change, from the <List>CarDealer
to a Collection.
So you will need to change your classes a little bit, and also you will need to include some directives on them in order to be visible to VB6 like:
Imports System.Runtime.InteropServices
<ClassInterface(ClassInterfaceType.AutoDual)> _
Public Class car
#Region "Fields"
Private _maker As String
Private _model As String
Private _mileage As Integer
Private _price As Integer
#End Region
#Region "Properties"
Public Property Maker() As String
Get
Return _maker
End Get
Set(ByVal value As String)
_maker = value
End Set
End Property
#End Region
End Class
<ClassInterface(ClassInterfaceType.AutoDual)> _
Public Class carDealer
#Region "Fields"
Private _description As String
Private _carList As New Collection
#End Region
#Region "Properties"
Public Property Description() As String
Get
Return _description
End Get
Set(ByVal value As String)
_description = value
End Set
End Property
Public Property CarList() As Collection
Get
Return _carList
End Get
Set(ByVal value As Collection)
_carList = value
End Set
End Property
#End Region
End Class
<ClassInterface(ClassInterfaceType.AutoDual)> _
Public Class dealerList
Private _list As New Collection
Public Property List() As Collection
Get
Return _list
End Get
Set(ByVal value As Collection)
_list = value
End Set
End Property
End Class
Call the C# project. Component
is the namespace of the C# project.
Dim CM As New Component.componentManager
Dim tDealerList As New Component.businessEntities.dealersList
tDealerList = CM.getDealers()
Use your head to convert your C# types to the VB types you are declaring in this project, nothing more than loop through the list
property inside tDealerList
, create a VB carDealer
instance, populate it and add it to a new collection. At the end, this collection will be the list in your VB dealierList
. Something like this:
For Each tDealer As Component.businessEntities.carDealer In tDealerList.list
Dim rCarDealer As New carDealer
rCarDealer.Description = tDealer.description
Dim rCars As New Collection
For Each tCar As Component.businessEntities.car In tDealer.carList
rCars.Add(New car(tCar.maker, tCar.model, tCar.mileage, tCar.price))
Next
rCarDealer.CarList = rCars
rCars = Nothing
rList.List.Add(rCarDealer)
Next
You are done.
You can build your app. Register your DLL for interop in the line command with:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm
"ComponentWrapper.dll" /tlb /codebase
C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\gacutil -i ComponentWrapper.dll
This will create a *.tlb file; open your VB6 project and make the reference to your tlb file. You won't be able to reference the DLL, so do not try.
Now in VB6, you pretty much can do this:
Dim list As New ComponentWrapper.dealerList
Dim cManager As New ComponentWrapper.CWManager
Set list = cManager.GetDealers()
And from here... it's all yours.
Points of Interest
The really cool thing about this is now you have a component your future .NET apps can use and also your VB6 apps in transition will be able to use with the wrapper, you just need to create the wrapper component and you are done.
The really annoying part of this learning curve that I took, was about the Generics not being possible to use in VB6. Finally I figured that in VB6 the only thing flexible enough was a collection. I know it can look like a disadvantage to use it sometimes, but I think in this particular case, as I am sure are many others, it was absolutely useful.
There are also many more guidelines to follow when rewriting a VB6 app into .NET and, if you are in the process of creating common DLLs to be used for .NET apps and for VB6 apps, I found the following link very useful for making this comparison:
History
- 8th January, 2009: Initial post