Windows Phone 8 Native Dev Tip: Hack your vcxproj file to selectively include the WinRT extensions

Today I wanted to share a Visual Studio hack that I discovered while working on CoPilot’s native component. I’m not sure how many other Windows Phone developers may find this useful, but what I can say is that when I discovered this it was practically a life changing event for me.

As you may know to consume the Windows Runtime (WinRT) API in a C++ project you need to set the “Consume Windows Runtime Extension” flag in your Visual Studio project.

This screenshot is from the properties file of the top-level project where I have a lot of interop between C++/CX and the XAML/C#. But CoPilot is a large solution that consists of some 40+ project files. Of those other 40 project files I only really needed to access the WinRT API from a few source files in a small number of projects. Turning on the Consume WinRT flag for only the projects where I needed it seems like it’d be simple enough, but in practice I found that I would frequently hit Internal Compiler Errors:

fatal error C1001: An internal error has occurred in the compiler. Internal Compiler Error in C:Program Files (x86)Microsoft Visual Studio 11.0VCWPSDKWP80binx86_armCL.exe. You will be prompted to send an error report to Microsoft later. 1 2 fatal error C1001 : An internal error has occurred in the compiler . Internal Compiler Error in C : Program Files ( x86 ) Microsoft Visual Studio 11.0VCWPSDKWP80binx86_armCL.exe. You will be prompted to send an error report to Microsoft later .

Apparently some of our code just doesn’t play nice with the WinRT extensions, so I had to figure out a way to only consume the WinRT extensions in the source files where I needed it. That or rewrite a lot of code to try to get rid of whatever is causing the internal compiler errors.

First let’s look at the vcxproj file. When your project consumes the WinRT extension you’ll get this in your vcxproj:

<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> <AdditionalUsingDirectories>$(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)</AdditionalUsingDirectories> <CompileAsWinRT>true</CompileAsWinRT> </ClCompile> </ItemDefinitionGroup> 1 2 3 4 5 6 < ItemDefinitionGroup Condition = "'$(Configuration)|$(Platform)'=='Debug|Win32'" > < ClCompile > < AdditionalUsingDirectories > $ ( WindowsSDK_MetadataPath ) ; $ ( AdditionalUsingDirectories ) < / AdditionalUsingDirectories > < CompileAsWinRT > true < / CompileAsWinRT > < / ClCompile > < / ItemDefinitionGroup >

Let’s take out the CompileAsWinRT node from here, since we’re getting those nasty compiler errors. Then scroll down the vcxproj file a bit and we’ll find the list of source files for this project. We can actually insert the CompileAsWinRT node there:

<ItemGroup> <ClCompile Include="source1.cpp" /> <ClCompile Include="source2.cpp"> <CompileAsWinRT>true</CompileAsWinRT> </ClCompile> </ItemGroup> 1 2 3 4 5 6 < ItemGroup > < ClCompile Include = "source1.cpp" / > < ClCompile Include = "source2.cpp" > < CompileAsWinRT > true < / CompileAsWinRT > < / ClCompile > < / ItemGroup >

source2.cpp will be able to use the WinRT API, but source1.cpp won’t. If you want to get really fancy, you can add some conditional statements into the CompileAsWinRT node, this will be handy if your vcxproj file is used for both Windows Phone and non-Windows Phone configurations:

<ItemGroup> <ClCompile Include="source1.cpp" /> <ClCompile Include="source2.cpp"> <CompileAsWinRT Condition="$(Configuration.Contains('WindowsPhone'))">true</CompileAsWinRT> </ClCompile> </ItemGroup> 1 2 3 4 5 6 & lt ; ItemGroup > < ClCompile Include = "source1.cpp" / > < ClCompile Include = "source2.cpp" > < CompileAsWinRT Condition = "$(Configuration.Contains('WindowsPhone'))" > true < / CompileAsWinRT > < / ClCompile > < / ItemGroup >

I’m not aware of any official MSDN documentation that shows you how to do this, but I can assure you it works. When using this method you do need to be careful with your header files though; for example let’s say in source2.cpp you define a class that’s going to create a WinRT object as a member variable. If source1.cpp or any other non-WinRT consuming source file includes source2.h it won’t recognize the WinRT types and will get you a compile error.

It’s also worth noting that I use static linking in all the projects where I include the WinRT extension with this method. When you static link a project that consumes the WinRT extension you’re going to get Linker Warnings about consuming the WinRT extension in a static lib, ignore them.