Last week I blogged a lot about the new features in VB Migration Partner 1.10, including Gosub refactoring, dead code removal, type inference, and If merging. In this post I illustrate a new exciting feature that makes new version 1.10 even more powerful and versatile.
When migrating large N-tier applications you typically want to adopt the so-called staged migration or phased migration approach, in which you convert one component at the time while still keeping the bulk of the application in VB6. There are actually two different flavors of staged migration:
In-out: you start converting data (inner) components, then
proceed outwardly with business components and finally with user
Out-in: you start converting user interface (outer) components and then proceed towards business components, and finally data (inner) components
The approach you take usually depends on your priorities and the very reasons why you’re migrating the VB6 code. If you want to improve the user interface you’re likely to choose the Out-in option; on the other hand, if you are migrating to .NET to leverage its superior scalability, its ability to communicate via WCF, and its smaller memory footprint, odds are that you’d like to implement the In-out scenario.
All the migration tools on the market can handle the Out-In scenario quite easily: if you convert a VB6 client application that references one or more COM component you get a .NET Windows Forms application that preserves all the references to the original COM objects and will therefore work exactly like the original VB6 client. So far, so good.
Things become less simple if you wish (or need to) adopt the In-Out scenario. In this case you'd have to generate a VB.NET component that is binary-compatible with the original component being migrated, so that all the existing VB6 clients continue to work with the new .NET component as they did with the original COM component.
Well, the interesting thing is that neither the Upgrade Wizard nor any other tool on the market, as far as we know, can generate binary-compatible .NET components. No other tool other than VB Migration Partner, that is, because we just added this important feature to release 1.10, as a response to a request from a customer.
Making the migrated VB.NET component binary-compatible with the original VB6 component – and therefore compatible with all existing VB6 clients – is as easy as adding a project-level BinaryCompatibility pragma:
VB Migration Partner generates a ComClass attribute for each public class in the generated VB.NET project; this attribute specifies the GUIDs of the original coclass, class interface, and event interface of the original VB6 class. In addition, a project-level Guid attribute in AssemblyInfo.vb is generated so that the .NET assembly has same GUID as the original type library. Finally, the Register for COM Interop setting is enabled in the MyProject property page. The neat effect of all these operations is that the .NET DLL is fully compatible with the original COM DLL and all existing VB6/COM clients can continue to work as before but use the new .NET DLL instead. (Clients don’t need to be recompiled.)
The BinaryCompatibility pragma is ignored and no warning message is emitted if the VB6 project type isn’t ActiveX DLL or if VB Migration Partner can’t find a COM DLL to be used to extract coclass and interface GUIDs. Notice that you can also use the BinaryCompatibility pragma at the file level, if you want to preserve binary compatibility with only a subset of the classes defined in the original COM component.
Interestingly, you might want to use the BinaryCompatibility pragma even if you aren't adopting the staged approach. In fact, even if the .NET components don't have to be compatible with the original COM components when the entire application is migrated, it is useful to preserve the compatibility until all the portions of the application have been converted, because it allows you to test a .NET component using existing COM clients.
Achieving binary compatibility requires more than just adding a few GUIDs and attributes, though. There are many subtle issues to be solved, for example enums, events, collection classes, apply the right MarshalAs attribute to UDT members, and a lot more. You need to wrap Public fields in properties and might need to transform a class into an interface before exposing it to COM clients.
In practice, to create a .NET component that is binary-compatible with an existing COM component you need to be a COM Interop guru and have a lot of time to spare… or you can just try out VB Migration Partner 1.10