Andrei Smolin

From VB6 to .NET. At last…

In one of my previous lives, I was a VBA and VB6 developer: ReDim, InStr, Msgbox, If Not Obj Is Nothing and other ugly things were part of that nice life. Now I live another life and have both VB.NET and C# experience gradually shifting to C#. If you are a late wayfarer on the road from VBA/VB6 to .NET, it may be interesting for you to know what is essential when porting your Office-related VBA/VB6 code to .NET.

When I started working for Add-in Express, I had little experience with C# and was experienced enough in VBA/VB6. So, my choice of a .NET language was obvious – it was VB.NET. I found, however, that Add-in Express developers use C#, so I felt like I was defending VB developers' interests. For instance, I insisted that the manuals were VB-oriented. But gradually, I started using C# more and more often so that now when I create a sample project to test this or that case with Office, the project will be in C# with 100% probability.

So what areas are unfamiliar for a VBA/VB6 developer porting some code to .NET?

Interop assemblies

In VBA and VB6, you added a reference to the host application of your add-in and that was all. But you knew that using the oldest possible version of the host application on the development PC guarantees that your add-in will not fire run-time errors on the target PC.

How did that work? When you added a reference to an ActiveX server (all Office applications are ActiveX servers), VBA/VB6 read the type library of the ActiveX server and saved the type library internally to provide IntelliSense and the compiler with early binding information.

In .NET, they don't save the type library behind the scene. Instead, they insist on your knowing and controlling what type library to use. To do this, they provide a feature called “interop assembly”. An interop assembly is a .DLL containing early binding information about a given version of a given ActiveX server, say, of the Office application hosting your add-in. There are interops called primary; Microsoft supplies us with primary interops for applications from Office 2002, 2003, 2007, and 2010. As for Office 2000, there are no primary interops and you need to create interops yourself or to use interops created by someone else. Add-in Express provides interops for all versions of all Office applications.

Naturally, you can use several interops for the same Office application if you need your add-in to support several versions of the Office application. But I don’t think this is the way real programmers go because real programmers choose late binding.

Late binding

You use late binding to access interfaces and interface members which are available in the Office application version loading your add-in but are absent in the interop assembly used in your add-in project. This situation is absolutely normal when supporting several versions of a given Office application in an add-in; find some useful tips on this at Supporting several Office versions in an add-in. Interop assemblies and late binding..

In VBA/VB6, you used late binding by declaring a variable of type Object and calling some methods/properties with no IntelliSense support at design time. In VB.NET, they provided an option called Option Strict Statement; when Option Strict is Off, you use late binding exactly as in VBA/VB6; when Option Strict is On, you use late binding exactly as in C#. Here are some samples:

VB.NET, Option Strict = Off

Dim sheet As Object = ExcelApp.Sheets.Item(1)
MsgBox(sheet.Name)
sheet.Select(True)

When you write the last two lines in the IDE, IntelliSense doesn’t provide the list of members after you write “sheet” and press the dot key. That’s another side of late binding.

If you set Option Strict On in the code above, it requires rewriting the code as follows:

VB.NET, Option Strict = On

Dim sheet As Object = ExcelApp.Sheets.Item(1)
MsgBox(sheet.GetType()._
    InvokeMember("Name", Reflection.BindingFlags.GetProperty, Nothing, sheet, Nothing))
sheet.GetType()._
    InvokeMember("Select", Reflection.BindingFlags.InvokeMethod, Nothing, sheet, _
        New Object(0) {True})

The second construct looks significantly more complex. Yet, I believe it allows creating a more flexible and controllable code. Moreover, it allows detecting mistakes at the compile time so you get the working code faster!

Releasing COM objects

In VBA, you never dealt with releasing COM objects because everything was done by the compiler. In .NET, however, they use an approach to object releasing that isn’t compatible with COM and now, all of a sudden, you’ve become responsible for releasing COM objects. For instance, consider the following code line:

Call obj.Class1.Class2.Class3.Method3

In VBA, you write such code lines many, many times a day never thinking that the compiler creates instances of all the classes above and then releases the instances for you. In .NET, the necessity to release COM objects turns the code line into a short story resembling the approach often seen in C or C++:

Imports System.Runtime.InteropServices
...
Dim class1Instance As Class1 = obj.Class1
Dim class2Instance as Class2 = class1Instance.Class2
Dim class3Instance as Class3 = class2Instance.Class3
 
Call class3Instance.Method3()
 
Marshal.ReleaseComObject(class3Instance)
Marshal.ReleaseComObject(class2Instance)
Marshal.ReleaseComObject(class1Instance)

Isn’t it nice? But do you really need all this stuff? Yes, you need it as you need to release all COM objects that your code creates. Please find many details at When to release COM objects in Office add-ins developed in .NET.

Application Domain (AppDomain), shims and loaders

AppDomain is a new concept described in Application Domain in Wikipedia. If no special measures are taken, your COM add-in will be loaded to the domain called Default AppDomain. In .NET, there’s a problem when two or more add-ins are loaded into the same AppDomain: if an add-in releases a COM object, no other COM add-ins will be able to use that COM object.

To bypass this problem, every Office extension should be loaded into an AppDomain of its own. This can be done by “shimming” your add-in; a shim is an unmanaged DLL (“unmanaged” = written in a non-.NET language, such as VB6) that loads your Office extension to an AppDomain of its own (among other tasks). Add-in Express provides a shim called Add-in Express Loader; it provides helpful logging extremely useful when an error occurs while the add-in is being loaded or registered.

Deploying and updating the add-in

VB6 developers remember msvbvm60.dll which is the VB6 run-time library. Similarly, .NET Framework is a run-time library for your .NET-based add-in and it must be installed in order to run your add-in. Note that there are several .NET Framework versions and your add-in requires installing the .NET Framework version you have been using while developing it (or higher).

The .NET Framework versions are tied to the version of Visual Studio: VS 2003 uses .NET Framework 1.1, VS 2005 – .NET Framework 2.0, VS 2008 – .NET Framework 3.0 and 3.5, VS 2010 allows using .NET Framework 2.0, 3.0. 3.5 and 4.0. Note that there are “full” .NET Framework versions and versions having “Client Profile” in their names; being smaller, “Client Profile” versions may not include some features required by your add-in, so be aware. Pay special attention to Office extensions based on .NET Framework 1.1, see Why not to use Visual Studio 2003 (.NET Framework 1.1) for developing Office extensions.

To install .NET Framework, the user must have administrative permissions. You can supply the .NET Framework with your installer or specify that the user downloads it from Microsoft’s; that stuff is called pre-requisites. Be prepared: the .NET Framework weighs significantly more than msvbvm60.dll.

Typical ways to deploy the add-in are:

  • Running an .MSI installer by the end-user or by an administrator; also, see HowTo: Install a COM add-in automatically using Windows Server Group Policy. Add-in Express supports this deployment type by creating a ready-to-run setup project for your add-in project.
  • Using ClickOnce (available for per-user add-ins only). Add-in Express supports this by providing the ClickOnce Publish wizard; when you are done, you need to copy files to the destination location.
  • Using ClickTwice :), a web-based deployment and update technology similar to ClickOnce but allowing to use .MSI installers; it allows deploying and updating both per-user and per-machine Office add-ins. Add-in Express supports this by providing a Publish wizard. Find more about that technology at Add-in Express 2010 MSI-based web deployment – ClickTwice :)

There are many other things that you will find strange (let’s put this carefully). Yet, the language itself moves ahead and in fact, I like this. But still I prefer using C# :) Don’t ask me why :)

Good luck.

One Comment

  • Nicholas Hebb says:

    Your comment about C# was interesting. I’m porting my add-in from VB6 to .NET. I started with VB.Net, but I found that the syntax was just too similar to VB6. It’s frustrating when you keep finding yourself writing VB6 in VS.NET. I started over with C#, and despite the fact that I threw away a bunch of code, I’m much happier.

Post a comment

Have any questions? Ask us right now!