Yesterday I blogged about a problem ISV's can run into when they are selling .NET class libraries compiled with Visual Studio.NET 2003 to customers using Visual Studio.NET 2002. Today I've found a solution, which seems to be used by other ISV's already, and which solves the problem which caused me to post my rant yesterday. Below I'll first describe in a few words the exact definition of the problem and then the solution which works without any problem for your customers. After that I'll enlighten a bit the things you have to keep an eye on when implementing this technique.
A short version of the problem: ISV Acme is currently building a set of class libraries targeting .NET which are sold in a package. Acme's developers are using Visual Studio.NET 2002, and the class libraries are thus using .NET 1.0. Acme's class libraries use additional assemblies from the .NET framework like System.Data. Because Acme's developers are subscribers of the MSDN universal program, they received Visual Studio.NET 2003, which targets .NET 1.1. Joe and Jack, Acme's main developers, migrate to Visual Studio.NET the same day they received the DVDs and are very happy with the new IDE and its new features. Visual Studio.NET 2003 upgrades the projects for the class libraries Acme is planning to sell to make the projects target .NET 1.1, and Joe and Jack couldn't care less, who wants to go back to Visual Studio.NET 2002 when you have Visual Studio.NET 2003 on your machine? Acme's project leader orders them not to include any .NET 1.1 features because customers using .NET 1.0 should also be able to use the class library.
Now, Acme finishes its set of class libraries and releases them to the public. Janice, Early's sister, is one of the first customers to buy Acme's wonderful class libraries, however because her boss, Peter Jansen, is from The Netherlands and doesn't want to spend any money on new Visual Studio.NET versions, so Janice is still using Visual Studio.NET 2002. The class libraries are added to the project Janice is working on and she compiles the code. It compiles without errors. Excited, she starts a trial run of her project. However her flawlessly compiled code throws an exception:
Unhandled Exception: System.IO.FileNotFoundException: File or assembly name System.Data,
or one of its dependencies, was not found.
File name: "System.Data"
at Acme.Libraries.CoolStuff.MagicTricks.DoMagic(int seed)
=== Pre-bind state information ===
LOG: DisplayName = System.Data, Version=1.0.5000.0, Culture=neutral,
PublicKeyToken = b77a5c561934e089
Stunned she looks at her monitor. Version 1.0.5000? That's weird. Being a geek for ages, she knows .NET 1.0 uses version number 1.0.3300 for its assembly files.1.0.5000 must be .NET 1.1. She can't use .NET 1.1, because she runs Visual Studio.NET 2002 which is hard-linked to .NET 1.0 and her boss is not willing to pay for Visual Studio.NET 2003. She calls Acme and tells Joe the problem she's having. Joe understands her problem and tells her he will look into it a.s.a.p.
Joe fires up the old test machine in the corner which still has Visual Studio.NET 2002 installed on it and creates a test application using the Acme class libraries. Testing it, he runs into the same problems Janice experienced. Jack tries to help but like Joe he can't seem to fix this easily inside Visual Studio.NET 2002. Apparently their class libraries reference .NET 1.1 and that is causing trouble on a machine with .NET 1.0 installed and not .NET 1.1. They try different workarounds, but none of them seems to be very nice: or they force the customer to perform significant actions or they force Joe and Jack to keep track of multiple projects or compile on the command-line. (Jack has worked with SunOS and Gnu Make for years and refuses to do that, Joe agrees immediately). However their boss orders them to come up with a solution a.s.a.p. since Janice is a good customer.
The solution is quite simple and yet so easy to miss. Jack comes up with an idea of fooling Visual Studio.NET 2003. It works, the newly compiled class libraries, when send to Janice, work flawlessly on her .NET 1.0 machine. What's the trick? Visual Studio.NET 2003 converts everything in your project to .NET 1.1: references, project format etc. The only thing left untouched is the code itself. If you compile a converted project with Visual Studio.NET 2003, it will target .NET 1.1, and the assemblies created will have references to .NET 1.1 assemblies, with version number 1.0.5000. To avoid this, Jack simply removed in the Visual Studio.NET 2003 projects of the class libraries the references to .NET 1.1 assemblies and replaces them with references to .NET 1.0 assemblies. (which was installed on his machine also). All projects in a complete solution have to have references to the same assembly versions (in this case .NET 1.0 versions). Jack saves the solution and the project files, closes Visual Studio.NET 2003, re-opens it and rebuilds the complete solution. The class library assemblies now compiled by Visual Studio.NET 2003 contain references to .NET 1.0 assemblies and will run without trouble on a .NET 1.0 installment, when used in a .NET 1.0 program.
What made them fall into this pitfall? Visual Studio.NET 2003's eagerness to help the developer out: when it creates a new project or converts a 2002 project, it will create references to .NET 1.1 assemblies and a developer will probably not think about these references until a customer like Janice calls with an error. If you have code which has to run on .NET 1.0 also, and you are like Joe and Jack and want to use Visual Studio.NET 2003, keep in mind that you have to change the references to the framework assemblies in your projects by hand, all of them.
Things you have to keep in mind
Now, the story about Jack and Joe is probably one that looks a lot like the situation you're in or will be in in a short notice. Will it always work? If you write .NET 1.0 compliant code it will. However you should keep in mind the following things when using this trick:
- Be aware that Visual Studio.NET 2003 will offer you to develop .NET 1.1 specific code and if you are not careful, you will not notice this (but your .NET 1.0 using customer will).
- If you have compiled your projects during a session in Visual Studio.NET 2003 and you change the assembly references in the projects, be aware that the CLR can't unload assemblies from an appDomain. It can be that Visual Studio.NET 2003 has still .NET 1.1 assemblies in core and compiles will then not succeed (probably show double declaration errors and other goo). After changing the assembly references, save the solution and restart Visual Studio.NET 2003. Compilation will then succeed.
- When you have more than one project in a solution, keep in mind that all projects in a solution have to have the same versions of a .NET assembly referenced (so you can't have one project reference to System of .NET 1.1 and another project to System from .NET 1.0), most likely your compiles will fail due to double declaration errors. If this is not possible, give the projects which have to be used on .NET 1.0 their own Solution.
- You can't mix framework versions. An assembly has to reference one version, it can't use System.Data from .NET 1.0 and System.Xml from .NET 1.1
As always this list is probably not complete. Always think about why you choose for the option to support the total range of frameworks in your code. If there are big reasons for migrating the code to .NET 1.1 and take advantage of its new features and just a small set of reasons to keep the code on .NET 1.0, you probably will move to .NET 1.1 completely and this trick is not required.
Special thanks to Thomas Tomiczek for giving me the hint that made me stumble into this solution (which he knew already :) ) and Sam Gentile for good blog-advice.