Skip to content

Latest commit

 

History

History
126 lines (81 loc) · 8.04 KB

how-its-done-in-visual-studio.md

File metadata and controls

126 lines (81 loc) · 8.04 KB

C++/WinRT Integration in Visual Studio

To better understand how C++/WinRT work from the perspective of a build system, we can look at how it is handled in Visual Studio. The officially recommend method of using C++/WinRT is through Visual Studio and the official NuGet package. The Windows SDK also supplies the C++/WinRT tooling, but it is generally quite outdated and not recommended to be used.

⚠️ Warning: The information here is work in progress and may be inaccurate.

Windows 10 SDK

See also: https://learn.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/intro-to-using-cpp-with-winrt#sdk-support-for-cwinrt

The Windows SDK has shipped C++/WinRT headers for system APIs and the cppwinrt tool for quite a while (likely starting from version 10.0.17134.0), though in more recent versions of the SDK these are present only for compatibility reasons. The version of C++/WinRT included in the Windows SDK will be quite outdated, therefore using it is usually a bad idea.

In the SDK, a copy of the generated headers is provided inside <WindowsSdkDir>\Include\<WindowsTargetPlatformVersion>\cppwinrt. The cppwinrt tool is inside the corresponding bin dir, i.e. <WindowsSdkDir>\bin\<WindowsTargetPlatformVersion>\<arch>\cppwinrt.exe.

MSBuild

To make this work using Visual Studio / MSBuild, the header include dir is specified in <WindowsSdkDir>\DesignTime\CommonConfiguration\Neutral\UAP\<WindowsTargetPlatformVersion>\UAP.props as the MSBuild property CppWinRT_IncludePath, but this is only set if the property has not been set already (which allows the C++/WinRT NuGet package to override it). This property is included as part of the WindowsSDK_IncludePath property, also specified in the same file.

VS Native Tools Command Prompt

Unlike MSBuild, the Visual Studio command prompt hardcodes the include paths to be set to the INCLUDE and EXTERNAL_INCLUDE environment variables to match the UAP.props defaults, so <WindowsSdkDir>\Include\<WindowsTargetPlatformVersion>\cppwinrt is always in the include path along with the rest of the include dirs.

This is done in Common7\Tools\vsdevcmd\core\winsdk.bat1 by adding the include paths to __VSCMD_WINSDK_INCLUDE. This script is called from Common7\Tools\vsdevcmd.bat1, which then combines the include paths from multiple sources including the winsdk one to form the INCLUDE and EXTERNAL_INCLUDE environment variables. There is no way to override this behaviour.

In order to use a custom set of C++/WinRT headers one must add the include path to the cl.exe command line or prepend the corresponding include path to these environment variables after calling vcvarsall.bat. Optionally one may also want to remove the SDK cppwinrt include path from the environment variables to avoid confusion (a script can try to look for %WindowsSdkDir%\include\%WindowsSDKVersion%\cppwinrt but this may not be reliable).

Visual Studio: NuGet

Content of the C++/WinRT NuGet Package

The official NuGet package contains the following:

  • The cppwinrt tool (an x86 binary executable)
  • MSBuild targets and props to add automatic C++/WinRT integration to the build
  • A property page schema for C++/WinRT properties
  • Static libraries of cppwinrt_fast_forwarder.lib

Build Integration (The Basics)

Microsoft.Windows.CppWinRT.props sets the property CppWinRT_IncludePath to PreventSdkUapPropsAssignment-- this is done to prevent the aforementioned UAP.props from adding the SDK cppwinrt headers to the include path, so that these headers will not be accidentally used. (UAP.props) only assign CppWinRT_IncludePath if it has not been set already.)

Microsoft.Windows.CppWinRT.targets adds various targets for build integration. One of the targets, CppWinRTMakePlatformProjection, runs the cppwinrt tool before the compiling steps to generate headers for the system APIs into the generated files dir using the winmd files in the Windows SDK. This file also reassign CppWinRT_IncludePath as the generated files dir but it does not have any effect on the build. The generated files dir is really used through being added to AdditionalIncludeDirectories.

The headers for referenced components and authored components are generated similarly. There are some fancy build targets which looks for the winmd files of the referenced components to invoke cppwinrt with. Exported components take more steps -- the winmd file has to be generated from IDL files inside the project using the midl tool.

Header Generation

The build target writes a response file containing the command line to use, then calls the cppwinrt tool with it. Presumably this avoids issues with command line length and escaping.

A list of platform winmd files is obtained with the glob <WindowsSdkDir>\References\<WindowsTargetPlatformVersion>\**\*.winmd (which can be overridden with properties). The Windows SDK also includes a single winmd file <WindowsSdkDir>\UnionMetadata\<WindowsTargetPlatformVersion>\Windows.winmd containing the API metadata of all the system WinRT APIs combined, but this is not used by the C++/WinRT NuGet package.

The platform API headers are generated by the following command (lines starting with # are comments), using a response file named by $(MSBuildProjectFile).cppwinrt_plat.rsp:

cppwinrt.exe @xxx.cppwinrt_plat.rsp

# The following is written to xxx.cppwinrt_plat.rsp:

-in <platform_winmd_file>
# Above is repeated for every platform winmd files.
-out <generated_files_dir>

The projection headers for referenced components are generated by the following command, using a response file named by $(MSBuildProjectFile).cppwinrt_ref.rsp:

cppwinrt.exe @xxx.cppwinrt_ref.rsp

# The following is written to xxx.cppwinrt_ref.rsp:

-in <reference_winmd_file>
# Above is repeated for every referenced components.
-ref <platform_winmd_file>
# Above is repeated for every platform winmd files.
-out <generated_files_dir>

The component headers, generated sources and component template code for authored components are generated by the following command, using a response file named by $(MSBuildProjectFile).cppwinrt_comp.rsp:

cppwinrt.exe @xxx.cppwinrt_comp.rsp

# The following is written to xxx.cppwinrt_comp.rsp:

-overwrite
-name <project_name>
-pch pch.h
# The previous line only included if the project uses precompiled header.
-prefix
-comp "<generated_files_dir>\sources"
# -comp is short for -component:
# "Generate component templates, and optional implementation"
-opt
# -opt is short for -optimize:
# "Generate component projection with unified construction support"
-in <component_winmd_file>
# Above is repeated for the component created in the project and for every
# components from static library dependencies.
-ref <platform_winmd_file>
# Above is repeated for every platform winmd files.
-out <generated_files_dir>

Authoring Components

To supply a WinRT component for use, the winmd file of the component also needs to be published. Authoring a C++/WinRT component requires you to craft the IDL file (in MIDL 3.0 syntax) of the component, which is picked up by the C++/WinRT build integration, which adds targets to run the midlrt tool to generate the winmd file. The cppwinrt tool is then used to generate headers, sources and template code, as mentioned in the previous section. The template code is intended for the developer to copy back to the project source code to be used as a starting point. The generated sources contains the glue code which binds the exported WinRT COM objects with the C++/WinRT-based classes implemented by the developer.

Visual Studio: C++/WinRT VSIX Extension

This extension provides project templates and NatVis debug visualizer for Visual Studio 2019. In Visual Studio 2022, these are already integrated in the installation out of the box.

Footnotes

  1. Paths are relative to VS install path, e.g. C:\Program Files\Microsoft Visual Studio\2022\Community 2