This article is a collection of tips and tricks for migrating projects from Visual Studio 2008 to Visual Studio 2012. Migrating a large application (around 120 projects, 4MLO) written with C++, C#, C++/CLI and involving different technologies and frameworks I have stumbled on different problems that I want to share, hopefully to make the transition easier for others.
This article is not about new features in Visual Studio 2012 and .NET 4.5, it is about problems you might encounter when you migrate. Of course, these are not all the problems, but some that I can share from experience.
Some of the issues mentioned in this article are not specific to Visual Studio 2012 or .NET 4.5, but were actually introduced in Visual Studio 2010 and .NET 4.0. Therefore, if you are already familiar with this version, you probably know at least some of theme.
The first most important issue for VC++ projects is that the format of the project files has changed. In Visual Studio 2010, VC++ moved away from VCBuild and started using MSBuild. With this switched the project files also changed to an MSBuild format. The new files have the extension .vcxproj, but there is an automatic conversion from the old .vcproj files to the new format. This can however lead to some warnings or even errors in the build.
One of the issues that I encountered was caused by the fact that I used to explicitly specify the Output File in the Linker settings. I used to do settings like $(OutDir)\MyAppD.exe in a Debug configuration and $(OutDir)\MyApp.exe in a Release configuration. That triggers some warnings:
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.CppBuild.targets(1137,5): warning MSB8012: TargetPath(D:\Marius\VC++\MFCApplication1\Debug\MFCApplication1.exe) does not match the Linker's OutputFile property value (D:\Marius\VC++\MFCApplication1\Debug\MyAppD.exe). This may cause your project to build incorrectly. To correct this, please make sure that $(OutDir), $(TargetName) and $(TargetExt) property values match the value specified in %(Link.OutputFile).
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.CppBuild.targets(1139,5): warning MSB8012: TargetName(MFCApplication1) does not match the Linker's OutputFile property value (MyAppD). This may cause your project to build incorrectly. To correct this, please make sure that $(OutDir), $(TargetName) and $(TargetExt) property values match the value specified in %(Link.OutputFile).
To fix this the most appropriate solution is to use $(OutDir)$(TargetName)$(TargetExt) as the Output file and make the appropriate settings of these properties under the General page.
Windows XP Support
The RTM release of Visual Studio 2012 has dropped support for targeting Windows XP and Windows Server 2003 for VC++ projects, requiring minimum Windows Vista and Windows Server 2008. After great demand Microsoft have brought back the support for those operating systems in Update 1. By default, the toolset for VC++ projects is set to Visual Studio 2012 (v110), but after installing Update 1 a new toolset called Visual Studio 2012 - Windows XP (v110_xp) is available (installed side-by-side). Change to this toolset if you want to still target Windows XP/Windows Server 2003.
However, you should notice that if your application is mixed and also using .NET, the 4.5 version also no longer supports these older operating systems. In this case you either drop support for them, or do not migrate your managed projects to .NET 4.5. .NET 4.0 is the last framework version that supports those operating systems.
MFC ODBC Bugs
To my surprise, some important routines that were using CDatabase for SQL server access no longer worked and even crashed the application. Investigating the problems I have discovered a couple of bugs in MFC:
Until fixes will be available it is possible to work-around these problems by deriving your own class from CDatabase and:
- Provide another method for returning the connection string that is first decrypting an encrypted version of the connection string (CDatabase keeps one internally, but it's available for derived classes); I have proposed an implementation here.
- Override OpenEx() and make sure to null pointers to released memory. I have proposed an implementation here.
For C++/CLI projects it is not possible to specify the version of the .NET framework you want to target from the IDE. The only available option is to manually set the desired value in the .vcxproj file. What you have to do is adding a <TargetFrameworkVersion> element under <PropertyGroup Label="Globals">.
Regardless you use .NET 4.0 or .NET 4.5 (which share the same 4.0 CLR), you may run into a runtime error like this:
Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information.
That occurs when your application targeting CRL 4.0 tries to load a mixed-mode assembly (directly, or indirectly through one of the loaded modules) that is built with a previous version of the .NET framework that targets CLR 2.0 (or even 1.x). The problem occurs because .NET 4.0 has changed the way it binds to older mixed-mode assemblies. There are two possible fixes:
You can read more about this problem here.
You may have projects that have references to COM servers, and therefore using interop assemblies, and code that may look like this:
static void Main(string args)
var o = new SomeLib.DummyClass();
When you migrate this to .NET 4.0/4.5 you get the following error:
1>[…]: error CS1752: Interop type 'SomeLib.TestClass' cannot be embedded. Use the applicable interface instead.
1>[…]: error CS0143: The type 'SomeLib.TestClass' has no constructors defined
.NET 4.0 (and of course 4.5) allow embedding type information for COM types directly into managed assemblies instead of using an interop assembly (basically statically linking everything that is needed, instead of requiring an additional interop assembly) (see MSDN for details). However, this has some limitations, and one of them is that classes cannot be embedded. Detailed information about the problem can be found here.
There are basically two solutions:
I would prefer the later, since it still leverages the benefit of embedding the interop types and making unnecessary the interop assemblies.
(Notice that if you build COM components, primary interop assemblies for COM components are still necessary if you want your COM component to be consumed from applications using a previous version of the .NET framework).
Applications using WPF need to add a new reference (in addition to PresentationCore, PresentationFramework and WindowsBase): System.Xaml.dll.
Visual Studio Setup Project is no longer available in Visual Studio 2012. If you have such projects then you have several alternatives:
- Continue to develop and build these projects with a previous version of Visual Studio;
- Use InstallShield; a free, limited edition is available from Visual Studio and you can convert (with some tools) existing .vdproj files;
- Use WIX (Windows Installer XML) toolset, but you need at least version 3.7, because this is the first one that supports Visual Studio 2012;
- Use any other (free or commercial) tools for developing setup projects.
Which one should be the best option probably depends on various factors. I would personally prefer switching to WIX. The only feature that WIX is missing is handling pre-requisites, but you can use dotNetInstaller for that. A comparison of various deployment tools is available here.
Template assembly directives used to be resolved using project references. So if you had to reference an assembly called something.dll in a .tt file you first had to add a reference to it and then use it in the assembly directive:
<#@ assembly name="something.dll" #>
In Visual Studio 2010 the way assembly directives are resolved has changed (the complete list of changes for T4 in Visual Studio 2010 can be found here). The T4 engine is now sandboxed to keep the T4 assemblies separate from the project assemblies. Therefore migrating T4 projects to VS2010/VS2012 may result in compiler errors. The possible solutions for this problem are:
- Put the assembly in GAC and use namespace reference or fully qualified type name
- Put the assembly in Visual Studio Public Assembly Folder and use namespace reference or fully qualified type name
- Use fully qualified paths
- Use a Windows Environment Variable to build fully qualified paths
- Use Visual Studio macros to build fully qualified paths
You can learn more about the problem and the solutions here and here.
A first important aspect is even though you can still target Windows XP for native projects, remote debugging only works for Windows 7/Server 2008 R2 and newer operating systems. For all the previous versions (Windows XP, Windows Vista, Windows Server 2003 and Windows Server 2008) this feature is no longer available. You can check the platform compatibility and system requirements. If this is still important to you then you must use the debugger from a previous version of Visual Studio (together with the remote tools).
Another feature that affects the debugging experience is that Visual Studio 2012 by default will try to load symbols for all modules from Microsoft symbol servers. The result is when the debugger attaches to a process it takes a long time to load everything. That can vary from several seconds, to several minutes, depending how many symbol files it must download (the bigger the application and more modules it loads, the longer it takes).
I recommend changing the default "All modules, unless excluded" to "Only specified modules". You can leave the list of specified modules empty, or you can specify the modules you want.
As mentioned in the beginning, this article is a collection of lessons learned migrating various projects from Visual Studio 2008 and .NET 3.5 to Visual Studio 2012 and .NET 4.5. You will probably encounter other problems too, but I hope this article can guide you in solving some of them.