The following is a public service announcement intended to help anyone who has a C++ project that was created with an old Visual Studio version like VS 2005 or 2008, and now wants to keep editing it with a more modern Visual Studio. We hope that our suggestions will save you a lot of time and angst. If you’re not facing this problem, you have our permission to skip this post. Have a wonderful day.
Recently, we decided to upgrade the .NET 2.0-targeted builds of our .NET side components to use Visual Studio 2012. For a long time, we’ve done these 2.0 builds on Visual Studio 2008, but we decided to use Visual Studio 2012 for both 2.0-targeted and 4.0-targeted builds, making use of VS 2012 multi-targeting capabilities. Our .NET code contains both C# and C++ projects. (Note that, even though we’re using VS 2012, everything below holds true for VS 2013, too.)
The C# projects upgraded without any problem, and without any additional input from the developers. The upgraded C++ projects, on the other hand, had problems: although the generated DLLs referenced .NET 2.0 libraries,
BadImageFormatException was thrown when they were used. Examination of the DLLs showed that, despite their referencing 2.0 libraries, they were .NET 4.0 binaries.
Examining the C++ project properties, under the “Common Properties/Framework and References” header, there is a “Targeted Framework” property. Even though we upgraded a 2.0-targeted project, the targeted framework was .NET 4.0. VS 2012 “helpfully” and silently not only upgraded our project from VS 2008, but also from .NET 2.0 to .NET 4.0. (Thanks, Visual Studio 2012!)
The targeted framework property is also a read-only field, so despite the fact that Visual Studio is supposed to support multi-targeted C++ projects, you can’t actually change the target framework inside Visual Studio.
Fortunately, there’s a way around this problem; you need to edit the text of the project file directly. First, unload the project by right-clicking it in the Solution Explorer and selecting “Unload.” Then, right-click on the project in Solution Explorer and select “Edit.” The project (an XML file) is displayed in an editor. (You can also use Notepad or any other editor to do this.) Look for the
<TargetFrameworkVersion/> element (or add it to the “Globals” property group if it’s not there already), and change its value from v4.0 to v2.0. Save away the file, close the editor window, right-click on the project node in Solution Explorer, and select “Reload.”
Now, if you now open the project properties and look at “Common Properties/Framework and References,” you’ll see that the targeted framework has been changed to .NET 2.0.
It’s important to note that, even though we’re targeting .NET 2.0 in VS 2012, we still need to have C++ tools from VS 2005 or 2008 installed on the machine; they’re not included in VS 2010, 2012, or 2013. And just installing the .NET 2.0 redistributables is insufficient. This is an unfortunate design decision on Microsoft’s part, and raises the question of why we even have multi-targeting in Visual Studio if we still need to have VS 2008 around and can’t uninstall it. It also raises the question of why the .NET 2.0 tools aren’t included in the VS 2012 installer, even as an optional install. Storage and bandwidth are cheap these days, and there seems to be no reason why the 2.0 tools can’t be packaged in the newer Visual Studio installs. Instead, what generally happens is that the developer upgrades the project to the new Visual Studio, successfully builds it, uninstalls the earlier Visual Studio because it seems to no longer be needed, and the project no longer builds until the old Visual Studio (or at least the C++ tools) is reinstalled.
At this point, we’re still not finished. The generated DLLs still can’t be run without exception. (They’re still .NET 4.0 binaries.) We still have to change the native multi-targeting settings, even if we’re only generating managed C++ DLLs. Open the C++ project’s properties, and under the “Configuration Properties/General” heading, look for the “Platform Toolset” property. Just as with the target framework property, Visual Studio (again “helpfully” and silently) upgraded this value from “v90” (VS 2008 tools) to “v110” (VS 2012 tools) or whatever version of the tools are used by the currently running Visual Studio. Thankfully, we can easily change this value (unlikely the target framework property) because there’s a pull-down.
Unfortunately, since this comes under the “Configuration Properties” heading, we have to change this value for every build configuration in the project. In other words, you need to change it at least for the Release and Debug builds, but in many cases there may be ten or more build configurations. Leaving aside the question of why this property is configuration-specific while the target platform version property is common to all configurations, or even why the target framework version and platform toolset properties are even two separate properties, since each platform toolset seems to correspond to one particular target framework version, how can we change the platform toolset property for all the build configurations in a single action and be assured that we don’t miss any? Just as was done with the target framework version, it’s done by directly editing the project file. Unload the project by right-clicking on it and selecting “Unload,” then right-click on it again and select “Edit.” Once the project file’s XML is open in an editor, do a “Replace All,” searching for “
<PlatformToolset>v110</PlatformToolset>” (or whatever the current value is), and replacing it with “
Once the change has been made everywhere it appears in the project file, save the file and reload the project. When you rebuild the project, the DLL should be properly executable on .NET 2.0 without throwing a
We’re putting this out here as a service to our developers, and to any other developers who stumble over this issue when upgrading their C++ projects to a new version of .NET. Good luck, and let’s hope that Microsoft eventually fixes these pitfalls in future versions of Visual Studio.