microsoft / microsoft/microsoft-ui-xaml
WinUI XAML Compiler Generate Module-Unaware C++ Code and Lacks C++/WinRT 3.x Named Module Build Integration
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 8.4k
- Forks
- 942
- Avg merge
- 2d 7h
- Merged PRs (30d)
- 105
Description
# C++/WinRT 3.x named-module projects require first-class module-aware integration in the WinUI XAML compiler
## Summary
C++/WinRT 3.x supports per-namespace C++20 named modules such as `import winrt.Windows.Foundation;`, `import winrt.Microsoft.UI.Xaml;`, and `import winrt.MyProject.Controls;` when a project enables `true`. The current WinUI XAML compiler and its MSBuild integration, however, still generate C++/WinRT code according to the traditional textual projection-header model. This is not limited to one generated source file. The module-mode state is not propagated into the XAML compiler at all, the compiler's internal dependency model commonly lowers WinRT type dependencies directly to `winrt/.h` filenames, and multiple C++/WinRT XAML generators independently emit projection headers. As a result, a real module-enabled WinUI C++ project must build a compatibility layer around XAML-generated code by manually importing projections, defining `WINRT_IMPORT_MODULE`, force-including a project-owned preamble into generated translation units, and carefully managing textual WinRT/COM/Win32/STL headers that the XAML compiler still emits.
The C++/WinRT modules documentation already calls XAML projects a special case and documents an `/FI` module-preamble workaround for `XamlTypeInfo.g.cpp`, `XamlTypeInfo.Impl.g.cpp`, `XamlMetaDataProvider.cpp`, and `XamlLibMetadataProvider.g.cpp`: https://github.com/microsoft/cppwinrt/blob/d6cff316a991152071038668fb586a8b5ab1d51f/nuget/modules.md?plain=1#L361-L419. That workaround is useful as migration guidance, but it should not be the permanent framework contract. The XAML compiler already discovers most of the semantic WinRT dependencies required by its generated code. The missing piece is a supported integration path that preserves those dependencies as semantic projection dependencies until they can be emitted in a form compatible with the selected C++/WinRT consumption mode.
A real-world conversion that demonstrates the scale of this gap is OpenNet: https://github.com/hoshiizumiya/OpenNet/commit/92dacc87cefe4a3ee29e46c23c1c3490108ac42a. The project enables both `true` and `true`, but still has to add project-specific MSBuild targets and a forced-include compatibility header for XAML-generated C++. This is evidence of a framework integration gap, not a proposal that every application should copy the same workaround.
## Source revisions used for this analysis
The implementation analysis below was checked against `microsoft/microsoft-ui-xaml` revision `6112d936461edb6d81ce7db983c74cc60ea2bc28` and the current C++/WinRT module implementation/documentation around revision `d6cff316a991152071038668fb586a8b5ab1d51f`. Stable commit-pinned source links are preferable for implementation discussion because generated templates and MSBuild targets can change independently on `main`. The user-facing documentation links can still point at `master`/`main` so readers reach the current guidance.
## Why this is a framework-level integration problem rather than a single generated-file bug
It would be easy to describe the problem narrowly as "`XamlTypeInfo.g.cpp` emits `#include ` when a module project needs `import winrt...;`", but source inspection shows that the mismatch starts earlier and affects more of the C++/WinRT XAML code-generation pipeline. The XAML compiler currently has no explicit input that tells it that the containing C++/WinRT project is operating in named-module mode. Its project model has no corresponding mode state. Its page dependency analysis stores C++/WinRT dependencies in a header-shaped representation. App, binding, type-info, and metadata-provider generators contain their own textual projection includes. The XAML compiler therefore cannot make a correct module-aware decision because the information and abstraction required to make that decision do not currently exist at the appropriate layer.
The important distinction is between a semantic dependency and its C++ spelling. If generated code requires the `Microsoft.UI.Xaml.Controls` projection, the semantic dependency is "this generated unit needs the `Microsoft.UI.Xaml.Controls` C++/WinRT projection." In traditional mode that may be lowered to `#include `. In named-module mode it may instead be satisfied by `import winrt.Microsoft.UI.Xaml.Controls;`, or by an automatically generated module preamble whose imports are supplied before the generated code. The compiler should not conceptually treat the header filename itself as the dependency.
## Current C++/WinRT module model
C++/WinRT 3.x can generate `.ixx` module interface files for WinRT namespaces. The supported project switch is:
```xml
true
```
For `import std;`, the project also enables:
```xml
true
```
A consumer can then use projection modules such as:
```cpp
import winrt.Windows.Foundation;
import winrt.Microsoft.UI.Xaml;
import winrt.Microsoft.UI.Xaml.Controls;
```
C++/WinRT also supports module-generation filtering and cross-project consumption. Relevant properties and metadata include `CppWinRTModuleInclude`, `CppWinRTModuleExclude`, and `CppWinRTConsumeModule`. A namespace module can therefore be produced by the current project, supplied by a module-builder project, supplied through a referenced static library's BMI propagation, or intentionally excluded from local generation. This matters for the XAML integration design: the XAML compiler should identify semantic projection dependencies, while C++/WinRT/MSBuild should remain responsible for resolving where the corresponding IFC/BMI comes from.
The official C++/WinRT modules guide also documents three constraints that explain why legacy generated XAML code becomes difficult to consume in a module project. Module imports cannot simply be placed in a traditional PCH. Include-then-import is generally possible but defeats much of the purpose of using C++/WinRT modules because expensive projection headers are still parsed textually. Import-then-include of the same declarations is not supported because it can produce conflicting declarations. To make existing code that textually includes C++/WinRT projection headers compatible with already imported projections, C++/WinRT provides `WINRT_IMPORT_MODULE`; when this macro is defined, generated projection headers become mostly inert while still establishing the expected include guards. The XAML compiler is exactly the kind of external code generator that currently forces applications to depend on that compatibility behavior.
## Root cause 1: the XAML MSBuild integration does not propagate C++/WinRT module mode
The first structural problem is visible at the `CompileXaml` task boundary. The XAML build targets invoke `CompileXaml` with inputs such as `Language`, `RootNamespace`, `XamlPages`, `XamlApplications`, `CIncludeDirectories`, `ClIncludeFiles`, `ReferenceAssemblies`, `FeatureControlFlags`, `TargetPlatformMinVersion`, and `PrecompiledHeaderFile`. The task contract does not currently expose a property that corresponds to `CppWinRTBuildModule` or another explicit C++/WinRT projection-consumption mode.
Relevant source: https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/Targets/Microsoft.UI.Xaml.Markup.Compiler.interop.targets#L12-L1156. `MarkupCompilePass1` and `MarkupCompilePass2` both pass the normal native/XAML build inputs into `CompileXaml`, but they do not pass `$(CppWinRTBuildModule)` or an equivalent module capability. This means the fact that the C++/WinRT targets are generating named modules is lost at the boundary where XAML C++ generation begins.
The task implementation confirms the same limitation. https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/CompileXaml.cs#L4-L339 exposes many `ICompileXamlInputs` properties, including `PrecompiledHeaderFile`, but no C++/WinRT module-mode property. The serializable compiler-input model in https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/Exe/Microsoft.UI.Xaml.Markup.Compiler.MSBuildInterop/CompilerInputs.cs#L4-L199 likewise has no module-mode field, and `CompilerInputs.FromMSBuildTaskInputs` therefore has nothing to copy. Finally, the compiler-side project model in https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/XamlProjectInfo.cs#L4-L127 contains C++-specific information such as `ClassToHeaderFileMap` and `PrecompiledHeaderFile`, but again no state indicating whether projection dependencies should be consumed through named modules.
Conceptually the current pipeline is:
```text
.vcxproj
|
| CppWinRTBuildModule=true
v
C++/WinRT MSBuild targets
|
| know module mode
| generate winrt..ixx
| build/resolve IFCs
v
[module-mode information is not propagated into XAML compilation]
WinUI XAML MSBuild targets
|
v
CompileXaml task
|
v
CompilerInputs / CompileXamlInternal
|
v
XamlProjectInfo
|
v
CppWinRT_* generators
```
The current implementation is therefore not equivalent to an incorrect branch such as `if (CppWinRTBuildModule) GenerateHeaders();`. There is no module-aware branch to take. A complete solution needs an explicit integration contract before any individual T4 template can reliably emit module-compatible C++.
The existing input model already demonstrates that code-generation modes can be carried through this boundary when needed. `FeatureCtrlFlags` contains mode/capability information such as `UsingCSWinRT`, and the MSBuild targets construct and pass XAML feature-control flags into the compiler. The absence of an equivalent C++/WinRT named-module capability is therefore not a fundamental limitation of the architecture; it is a missing integration contract that can be added in the same general data-flow path or through a dedicated input if a feature flag is not semantically appropriate.
## Root cause 2: page dependency analysis lowers semantic WinRT dependencies directly to header filenames
The second structural issue appears in https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/PageDefinition.cs#L1-L363. `PageDefinition` maintains a collection named `NeededCppWinRTProjectionHeaderFiles`. During `EnsureNeededXamlHeaderFilesCalculated`, a discovered WinRT type is converted into a path using logic equivalent to:
```csharp
neededCppWinRTProjectionHeaderFiles.Add($"winrt/{adjustedType.Namespace}.h");
```
The dependency analysis itself is valuable and already substantially complete, but it is important to describe what the dependency represents and why the generated filename takes its current form.
C++/WinRT projections are fundamentally organized around WinRT metadata namespaces. An IDL declaration such as:
```idl
namespace OpenNet.UI.Xaml.Control.Graph
{
runtimeclass LiveGraph : Microsoft.UI.Xaml.Controls.Control
{
LiveGraph();
}
}
````
is represented in the generated Windows Metadata with a type whose namespace is:
```text
OpenNet.UI.Xaml.Control.Graph
```
C++/WinRT reads that metadata namespace (`TypeDef::TypeNamespace()`), groups projected types by namespace, and generates one public projection header for that namespace. In the traditional projection model, the resulting files are named directly from the metadata namespace, for example:
```text
winrt/OpenNet.UI.Xaml.Control.Graph.h
winrt/impl/OpenNet.UI.Xaml.Control.Graph.0.h
winrt/impl/OpenNet.UI.Xaml.Control.Graph.1.h
winrt/impl/OpenNet.UI.Xaml.Control.Graph.2.h
```
The public C++ projection is correspondingly exposed under:
```cpp
winrt::OpenNet::UI::Xaml::Control::Graph
```
With C++/WinRT 3.x named modules enabled, the same metadata namespace is also the identity used for the namespace module:
```cpp
import winrt.OpenNet.UI.Xaml.Control.Graph;
```
with a generated module interface file named approximately:
```text
winrt/winrt.OpenNet.UI.Xaml.Control.Graph.ixx
```
Therefore the traditional projection header and the named module are not unrelated dependencies. They are two C++ consumption representations of the same underlying WinRT metadata namespace:
```text
WinRT metadata namespace
OpenNet.UI.Xaml.Control.Graph
|
+--> traditional projection
| winrt/OpenNet.UI.Xaml.Control.Graph.h
|
+--> named-module projection
winrt.OpenNet.UI.Xaml.Control.Graph
```
This distinction matters when looking at the XAML compiler implementation. `PageDefinition` does not begin with an arbitrary header filename. It first resolves XAML types and obtains their underlying WinRT metadata namespaces. Its dependency analysis examines generated named-element fields, event types, event declaring types, compiled-binding member and declaring types, and nested generic argument types. For a projected type, it then immediately lowers the namespace to the traditional C++/WinRT header convention with logic equivalent to:
```csharp
neededCppWinRTProjectionHeaderFiles.Add(
$"winrt/{adjustedType.Namespace}.h");
```
For example, if a XAML page uses a control whose underlying WinRT type is:
```text
OpenNet.UI.Xaml.Control.Graph.LiveGraph
```
the XAML compiler has already resolved the namespace:
```text
OpenNet.UI.Xaml.Control.Graph
```
and currently materializes that dependency as:
```cpp
#include
```
That mapping is correct for the traditional C++/WinRT projection-header model: it mirrors the naming convention of the projection generated by C++/WinRT from the same WinRT metadata namespace.
The integration problem is therefore not that the XAML compiler fails to discover the relevant namespace, nor that its traditional header filename is incorrect. The problem is that the dependency is lowered to a header-specific representation too early.
With C++/WinRT 3.x, the same semantic projection dependency can instead be consumed as:
```cpp
import winrt.OpenNet.UI.Xaml.Control.Graph;
```
Consequently the information that should survive dependency analysis is conceptually:
```text
requires C++/WinRT projection namespace:
OpenNet.UI.Xaml.Control.Graph
```
rather than only:
```text
requires header:
winrt/OpenNet.UI.Xaml.Control.Graph.h
```
The final representation can then be selected according to the C++/WinRT consumption mode:
```text
WinRT metadata namespace dependency
|
v
OpenNet.UI.Xaml.Control.Graph
|
+----+----+
| |
v v
header mode module mode
| |
v v
#include import
OpenNet.UI...;
```
In other words, dependency discovery is largely already present. What is missing is a module-agnostic representation of the discovered C++/WinRT projection dependency and a consumption-mode-aware lowering step.
```cpp
#include
```
In named-module mode the same semantic dependency could instead be satisfied by:
```cpp
import winrt.OpenNet.UI.Xaml.Control.Graph;
```
or by an automatically generated preamble that imports that projection before the generated declarations are parsed. The compiler already has the type/namespace information required to make this decision; it currently lacks an abstraction that keeps the dependency semantic until final C++ emission.
A module-compatible internal model would conceptually look more like:
```text
CppWinRTProjectionDependency
Namespace = OpenNet.UI.Xaml.Control.Graph
```
with a later lowering step that chooses the correct C++ representation. The exact class/property names do not matter. What matters is separating "requires projection namespace X" from "include file `winrt/X.h`".
### `__has_include` does not provide module awareness
The current generators frequently wrap projection headers in `#if __has_include()`. This protects builds where a projection header is genuinely unavailable, but it cannot select between header consumption and named-module consumption. C++/WinRT continues to generate projection headers alongside module interfaces, so `__has_include()` can remain true in a module-enabled project. The existence of the header therefore says nothing about whether the intended declaration source for the translation unit is the textual header or `winrt.Namespace` IFC. It also says nothing about `CppWinRTModuleInclude`, `CppWinRTModuleExclude`, or whether an IFC is supplied by another project. Module selection has to come from build/configuration state, not from probing for a projection header.
This also explains why the current `__has_include` pattern can silently appear harmless when `WINRT_IMPORT_MODULE` is force-defined: the conditional succeeds, the header is included, and C++/WinRT deliberately makes most of the header body inert. In that configuration the generated source is not truly module-aware; it is being made compatible by an external macro that changes the meaning of the legacy include. That distinction matters because the required modules still have to be imported somewhere before those now-inert headers are encountered.
### TypeInfo dependency discovery has a narrower but still semantic source
`TypeInfoDefinition.NeededCppWinRTProjectionHeaderFiles` is not identical to the page dependency set. Its `LookupNeededCppWinRTProjectionHeaderFiles` walks generated TypeInfo entries and adds a projection dependency when generated metadata has a direct code reference, such as activation, collection handling, dictionary handling, or enum conversion. This is another reason to avoid a simplistic global string replacement: Page and TypeInfo discover dependencies from different semantic inputs, but both ultimately lower those dependencies to the same `winrt/.h` representation. A shared projection-dependency abstraction can preserve the different discovery algorithms while unifying only the final consumption strategy.
## Root cause 3: the problem affects multiple C++/WinRT XAML generators, not only TypeInfo Pass2
### Page Pass1
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/CppWinRT/CppWinRT_PagePass1.tt#L1-L95 generates the per-page `.xaml.g.h` declarations. It begins with traditional includes such as:
```cpp
#pragma once
#include
```
and then emits every entry in `Model.NeededCppWinRTProjectionHeaderFiles` as:
```cpp
#if __has_include()
#include
#endif
```
The generated template then declares `PageT`/`UserControlT` infrastructure and generated fields that directly use projected C++/WinRT types. Therefore the module-awareness problem exists before `XamlTypeInfo.g.cpp` is compiled. A fix that only changes TypeInfo Pass2 would leave the Pass1-generated page headers header-oriented.
### App Pass1
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/CppWinRT/CppWinRT_AppPass1.tt#L1-L78 is even more explicit. It directly includes projection headers rather than obtaining them from the page dependency collection:
```cpp
#include
#include "winrt/Microsoft.UI.Xaml.h"
#include "winrt/Microsoft.UI.Xaml.Markup.h"
#include "winrt/Microsoft.UI.Xaml.Interop.h"
#include "winrt/Windows.UI.Xaml.Interop.h"
#include "XamlTypeInfo.xaml.g.h"
#include "XamlMetaDataProvider.h"
```
The generated `AppT` then uses `Microsoft.UI.Xaml.Markup.IXamlMetadataProvider`, `Windows.UI.Xaml.Interop.TypeName`, `Microsoft.UI.Xaml.Application`, and other projected types. Therefore App generation needs the same projection-consumption abstraction even if page dependency discovery is refactored.
### BindingInfo Pass1
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/CppWinRT/CppWinRT_BindingInfoPass1.tt#L1-L289, which supplies infrastructure used by compiled `{x:Bind}` bindings, also directly includes projection headers:
```cpp
#include
#include "winrt/windows.foundation.h"
#include "winrt/windows.ui.xaml.interop.h"
#include "winrt/microsoft.ui.xaml.controls.h"
#include "winrt/microsoft.ui.xaml.data.h"
#include "winrt/microsoft.ui.xaml.markup.h"
```
The generated declarations define and use types such as `IInspectable`, `INotifyCollectionChanged`, `INotifyPropertyChanged`, `DependencyObject`, `DependencyProperty`, `ContainerContentChangingEventArgs`, `IComponentConnector`, `WindowActivatedEventArgs`, and related binding-tracking types. This means `{x:Bind}` participates directly in the same header-oriented generated-code model. A module-enabled XAML application cannot be considered fully supported if compiled-binding infrastructure continues to assume textual projection headers.
### TypeInfo Pass1
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/CppWinRT/CppWinRT_TypeInfoPass1.tt#L1-L224 emits another fixed group of STL, COM, Windows, and C++/WinRT dependencies:
```cpp
#include
#include
#include
#include
#include
#include
#include
#include "winrt/Microsoft.UI.Xaml.h"
#include "winrt/Microsoft.UI.Xaml.Data.h"
#include "winrt/Microsoft.UI.Xaml.Markup.h"
#include "winrt/Microsoft.UI.Xaml.Interop.h"
#include "winrt/Windows.UI.Xaml.Interop.h"
#include "winrt/Microsoft.UI.Xaml.XamlTypeInfo.h"
```
This file demonstrates why the real-world workaround is more complicated than importing a few namespaces. Generated XAML code interleaves STL, COM/Win32, and WinRT projection dependencies, so import/include ordering becomes part of the compatibility problem.
### TypeInfo Pass2
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/CppWinRT/CppWinRT_TypeInfoPass2.tt#L1-L441 optionally includes the configured PCH, then includes `` and ``, emits every `Model.NeededCppWinRTProjectionHeaderFiles` entry with `__has_include`, includes `XamlTypeInfo.xaml.g.h`, includes local XAML implementation headers from `Model.AllLocalXamlHeaderFiles`, and includes generated `.xaml.g.hpp` implementation fragments. This is the most visible failure point in module projects because it is a generated translation unit compiled late in the XAML build, but it is only one member of the affected generator family.
### XamlMetaDataProvider Pass1/Pass2
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/CppWinRT/CppWinRT_XamlMetaDataProviderPass1.tt#L1-L63 includes standard/COM headers and then includes both `XamlTypeInfo.xaml.g.h` and `XamlMetaDataProvider.g.h`. The latter crosses the boundary into C++/WinRT-generated component scaffolding. `CppWinRT_XamlMetaDataProviderPass2.tt` also participates in the generated metadata source flow. This boundary was explicitly called out during development of C++/WinRT named-module support as an awkward case because importing implementation-oriented module content into a project-wide preamble would be undesirable.
### Page Pass2 and generated `.xaml.g.hpp`
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/CppWinRT/CppWinRT_PagePass2.tt#L1-L391 includes the configured PCH when present and includes local XAML implementation headers from `Model.NeededLocalXamlHeaderFiles` before emitting `InitializeComponent`, connection logic, unload logic, event hookups, and compiled-binding code. These generated definitions rely on the declarations established in Pass1 and on local implementation headers. Module support therefore has to respect both projection dependencies and local implementation dependencies across the two XAML compilation passes.
The affected surface should consequently be reviewed as a family: `CppWinRT_PagePass1.tt`, `CppWinRT_PagePass2.tt`, `CppWinRT_AppPass1.tt`, `CppWinRT_AppPass2.tt`, `CppWinRT_BindingInfoPass1.tt`, `CppWinRT_BindingInfoPass2.tt`, `CppWinRT_TypeInfoPass1.tt`, `CppWinRT_TypeInfoPass2.tt`, `CppWinRT_XamlMetaDataProviderPass1.tt`, `CppWinRT_XamlMetaDataProviderPass2.tt`, and their generated `.cs` counterparts. A fix that modifies only one template is unlikely to provide first-class module support. See [#Related source locations](#Related-source-locations)
The `.tt` templates are the authoritative generator sources for these paths; the corresponding generated `.cs` files checked into the repository are outputs of T4 transformation. A production fix should update the template/generator source and then regenerate the derived `.cs` artifacts according to the repository's normal process rather than treating edits to generated `.cs` files as the primary implementation. This matters for review because a patch that appears to fix only `CppWinRT_PagePass1.cs`, for example, can be overwritten by the next T4 regeneration if the `.tt` source is unchanged.
## The two-pass XAML build creates more than one module boundary
The C++/WinRT modules guide documents the native XAML build order in terms of two passes. Simplified, the flow is:
```text
1. MarkupCompilePass1
-> generates .xaml.g.h declarations
2. ClCompile
-> compiles normal application C++ sources
3. MarkupCompilePass2
-> generates .xaml.g.hpp implementations
4. CompileXamlGeneratedFiles
-> compiles XamlTypeInfo.g.cpp and related generated metadata sources
```
This matters because the module dependency problem does not exist at only one translation unit. Pass1-generated `.xaml.g.h` files are consumed by hand-written implementation headers and normal application `.cpp` files before the late generated metadata translation units are compiled. Pass2-generated `.xaml.g.hpp` content is later included by `XamlTypeInfo.g.cpp`. The project therefore has at least two distinct dependency boundaries:
```text
Page.xaml
-> Page.xaml.g.h
-> Page.xaml.h
-> Page.xaml.cpp
```
and:
```text
Page.xaml.g.hpp
-> XamlTypeInfo.g.cpp
-> CompileXamlGeneratedFiles
```
A framework solution has to ensure that projection declarations required at both boundaries are available in a module-compatible way. Automatically force-including a preamble only into the final `XamlTypeInfo.g.cpp` compilation does not by itself solve the generated-header side of the problem.
## Projection dependencies and local implementation dependencies must remain separate
Any module-aware implementation needs to distinguish two fundamentally different dependency categories currently present in generated TypeInfo/page code.
### Public WinRT projection dependency
A generated include such as:
```cpp
#include
```
provides the public projected runtime types under a namespace such as:
```cpp
winrt::OpenNet::UI::Xaml::Control::Effect
```
In a module-enabled configuration, this dependency naturally corresponds to a projection module such as:
```cpp
import winrt.OpenNet.UI.Xaml.Control.Effect;
```
assuming the C++/WinRT/MSBuild module graph has made that module available to the translation unit.
### Local implementation dependency
The XAML compiler also includes local project implementation headers discovered through `ClassToHeaderFileMap` and `AdditionalXamlTypeInfoIncludes`. These headers are needed for a different reason. For a constructible local XAML type, `TypeGenInfo` can generate an activator equivalent to:
```cpp
ActivateLocalType
```
The public projection module `winrt.MyNamespace` does not by itself provide a complete definition of `winrt::MyNamespace::implementation::MyControl`. The implementation header is therefore still required unless a separate implementation-module design is intentionally introduced.
A correct design should consequently preserve at least the following conceptual distinction:
```text
Projection dependency
-> textual C++/WinRT projection header in header mode
-> named projection module or generated module preamble in module mode
Local implementation dependency
-> local implementation header
-> possibly a future implementation-module contract, but not implicitly replaced by the public projection module
```
This distinction is important because "replace every generated `#include` with `import`" would be incorrect. Only projection dependencies naturally map to public C++/WinRT namespace modules.
## Why the existing OpenNet workaround becomes complicated
OpenNet provides a concrete example of the amount of application-owned build integration currently needed. At commit `92dacc87cefe4a3ee29e46c23c1c3490108ac42a`, the project enables:
```xml
true
```
and:
```xml
true
NotUsing
```
The project then adds a custom MSBuild target after `ComputeXamlGeneratedCompileInputs` and before normal C++ compilation so selected XAML-generated translation units receive special metadata:
```xml
NotUsing
windows.h;
$(MSBuildProjectDirectory)\XamlGeneratedWorkaround.h
```
The corresponding compatibility header is not merely a list of imports. It has to establish `WINRT_IMPORT_MODULE`, manage include guards corresponding to COM/Win32 headers that generated XAML code may later include, manage the STL preprocessor boundary, and then import the required standard and WinRT modules. The real file can be inspected here: https://github.com/hoshiizumiya/OpenNet/blob/92dacc87cefe4a3ee29e46c23c1c3490108ac42a/OpenNet/XamlGeneratedWorkaround.h.
Representative content includes:
```cpp
#ifndef WINRT_IMPORT_MODULE
#define WINRT_IMPORT_MODULE
#endif
#ifndef __unknwn_h__
#define __unknwn_h__
#endif
#ifndef __RPC_H__
#define __RPC_H__
#endif
#ifndef __RPCNDR_H__
#define __RPCNDR_H__
#endif
#ifndef _INC_WINAPIFAMILY
#define _INC_WINAPIFAMILY
#endif
#ifndef _INC_WINPACKAGEFAMILY
#define _INC_WINPACKAGEFAMILY
#endif
#ifndef _OLE2_H_
#define _OLE2_H_
#endif
#ifndef _INC_WINDOWS
#define _INC_WINDOWS
#endif
#ifndef _INC_SDKDDKVER
#define _INC_SDKDDKVER
#endif
#undef _STL_COMPILER_PREPROCESSOR
#define _STL_COMPILER_PREPROCESSOR 0
import std;
import winrt.Windows.Foundation;
import winrt.Microsoft.UI.Xaml.Markup;
import winrt.Microsoft.UI.Xaml.Interop;
import winrt.Windows.UI.Xaml.Interop;
import winrt.Microsoft.UI.Xaml;
import winrt.Microsoft.UI.Xaml.XamlTypeInfo;
```
The guard suppression is not being proposed as the framework fix. It is evidence of the problem's severity. The application has become coupled to the textual includes and ordering chosen by generated XAML C++. If the XAML compiler adds, removes, or reorders a normal header in a future release, the compatibility layer may need to change even when the semantic XAML type dependencies remain identical. That is a fragile ownership boundary for application code.
The module guide's `/FI` solution is therefore best understood as a migration workaround that adapts a legacy generated translation unit to a module-enabled environment. A supported framework integration should ideally remove the need for each application to independently reproduce and maintain this compatibility logic.
## XAML-discovered dependencies are currently duplicated manually as C++ imports
OpenNet also demonstrates dependency duplication in hand-written page implementation headers. For example, `TaskSpeedGraphPage.xaml` declares namespaces such as:
```xml
xmlns:effects="using:OpenNet.UI.Xaml.Control.Effect"
xmlns:graph="using:OpenNet.UI.Xaml.Control.Graph"
```
The XAML compiler must already resolve these namespaces and the types used from them in order to compile the page. Nevertheless, in a module build the hand-written `TaskSpeedGraphPage.xaml.h` also contains:
```cpp
import winrt.OpenNet.UI.Xaml.Control.Graph;
import winrt.OpenNet.UI.Xaml.Control.Effect;
#include "UI/Xaml/View/Pages/TaskSpeedGraphPage.g.h"
```
The XAML source and the C++ implementation header are expressing the same projection dependencies twice:
```text
XAML type dependency:
using:OpenNet.UI.Xaml.Control.Graph
C++ module dependency:
import winrt.OpenNet.UI.Xaml.Control.Graph;
```
For a small sample this duplication is tolerable. In a large application with many local runtimeclass namespaces, third-party WinRT namespaces, event types, named fields, and compiled bindings, it becomes a significant maintenance burden. It is also error-prone because adding a new XAML type can require an apparently unrelated hand-written C++ import solely so generated declarations can compile.
This is particularly notable because `PageDefinition` already walks the field/event/binding type graph and therefore possesses most of the information needed to derive the projection dependency automatically.
## Historical context from the C++/WinRT module implementation
This boundary was recognized during development of the named-module feature itself. C++/WinRT PR #1575, "C++20 module support v2", is directly relevant: https://github.com/microsoft/cppwinrt/pull/1575.
During discussion, the C++/WinRT maintainer explicitly stated that "The XAML compiler emits module-unaware code." The same discussion describes a Terminal prototype that worked around XAML-generated projection-header includes by using a module preamble and defining `WINRT_IMPORT_MODULE`, and specifically calls out `XamlMetaDataProvider.h` including C++/WinRT's `XamlMetaDataProvider.g.h` as an uncomfortable implementation boundary. This historical note is important because it shows that the current problem is not an OpenNet-specific build accident. It was already identified as an unresolved integration boundary while first-class C++/WinRT named-module support was being designed.
C++/WinRT itself now has useful precedent for dual-mode generated code. Its generated component scaffolding can distinguish module-aware consumption and traditional header consumption, conceptually following a pattern such as:
```cpp
#ifdef WINRT_IMPORT_MODULE
import winrt.Windows.Foundation;
import winrt.MyComponent;
#else
#include
#include
#endif
```
The XAML compiler does not need to invent an unrelated philosophy. The useful principle is that generated code has a dependency on a projection namespace, and the representation of that dependency should match the C++/WinRT consumption mode selected by the project.
## Important complication: module mode is not simply a project-wide boolean for every namespace
Although propagating `CppWinRTBuildModule` into the XAML compiler is a necessary first step, a robust design should also account for C++/WinRT's namespace filtering and cross-project module consumption. `CppWinRTModuleInclude` and `CppWinRTModuleExclude` can limit locally generated namespace modules. `CppWinRTConsumeModule` can make IFCs from another module-builder project available. Static-library references can propagate BMIs. Therefore "module mode is enabled" does not automatically mean "every `winrt.` discovered by XAML is generated locally."
This argues for keeping responsibility separated. The XAML compiler should discover semantic projection dependencies. C++/WinRT/MSBuild should resolve whether the required namespace module exists locally or comes from another project. If generated XAML code directly emits `import winrt.Namespace;`, build integration must guarantee that the corresponding module is available. If a generated preamble is used instead, that preamble should be derived from the actual module graph rather than from an application-maintained hard-coded list.
There is another subtle consequence of `WINRT_IMPORT_MODULE`: it is used to make subsequent textual C++/WinRT projection headers mostly inert after equivalent modules have already been imported. A generated translation unit should therefore not casually define `WINRT_IMPORT_MODULE` and then expect arbitrary unimported projection headers to remain usable as a fallback. The integration must either ensure that the module-import set covers every projection dependency that will be made inert in that translation unit or choose another safe compatibility strategy. This is another reason why the XAML compiler and the C++/WinRT/MSBuild module graph need an explicit contract rather than a local template-only change.
## Why direct `import` in every generated header should not be assumed to be the only fix
A straightforward implementation idea is to emit `import winrt.Namespace;` directly from `.xaml.g.h` or other generated files whenever module mode is active. That may be valid in many translation-unit layouts, but it should not be treated as the only possible design without considering C++ module placement rules and the contexts in which generated headers can be included. A generated XAML header can be textually included from ordinary translation units, from user-authored module units, or from implementation headers with their own global-module-fragment requirements. Injecting imports into a header changes the expectations of every including context.
For that reason, several designs remain plausible: direct module imports emitted by generated files where their placement is guaranteed legal; a generated per-page or per-translation-unit module preamble that is included or force-included before generated XAML declarations; a project-level generated preamble whose imports are computed from the union of XAML projection dependencies; or an MSBuild/C++ integration that attaches the correct forced-include/module metadata to generated compilation items. The issue should require first-class module-aware behavior but leave room for the implementation to choose the safest placement strategy.
## Suggested framework ownership boundary
A clean ownership split would be:
```text
XAML compiler:
resolve XAML types;
collect semantic WinRT projection dependencies;
collect local implementation dependencies;
generate C++ that is compatible with the selected C++/WinRT consumption mode.
C++/WinRT:
generate projection headers;
generate winrt..ixx module interfaces;
provide module/header compatibility behavior such as WINRT_IMPORT_MODULE;
define module-generation filtering semantics.
MSBuild/C++:
propagate the selected C++/WinRT mode into XAML compilation;
resolve IFC/BMI locations;
attach generated-source compilation metadata;
order module production before consumption.
```
The XAML compiler should not be responsible for producing C++/WinRT namespace IFCs. Conversely, applications should not be responsible for reverse-engineering XAML-generated include order so they can manually construct a safe module environment.
## Proposed fix, layer 1: propagate module-consumption state through the MSBuild-to-XAML contract
The first required change is to make module mode an explicit XAML compiler input for native C++/WinRT projects. Conceptually, the XAML targets could pass a property derived from the supported C++/WinRT configuration:
```xml
```
The exact name is unimportant and may need to represent more than a boolean in the final design. What matters is that both `MarkupCompilePass1` and `MarkupCompilePass2` receive enough information to choose a projection-consumption strategy.
The corresponding input must be represented consistently in the task and executable/serializer paths. Candidate files to review include:
```text
src/XamlCompiler/Exe/Microsoft.UI.Xaml.Markup.Compiler.MSBuildInterop/ICompileXamlInputs.cs
src/XamlCompiler/BuildTasks/CompileXaml.cs
src/XamlCompiler/Exe/Microsoft.UI.Xaml.Markup.Compiler.MSBuildInterop/CompilerInputs.cs
src/XamlCompiler/Exe/Microsoft.UI.Xaml.Markup.Compiler.IO/XamlCompiler/InputSerializer.cs
src/XamlCompiler/BuildTasks/CompileXamlInternal.cs
src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/XamlProjectInfo.cs
src/XamlCompiler/Targets/Microsoft.UI.Xaml.Markup.Compiler.interop.targets
```
If generated output differs by module mode, this state must also participate in the XAML compiler's incremental-build invalidation/saved-state logic. Toggling `CppWinRTBuildModule` must not leave stale header-mode generated C++ in an otherwise module-enabled build or stale module-aware generated C++ after returning to header mode.
## Proposed fix, layer 2: represent projection dependencies semantically
The second change should remove the assumption that the primary dependency representation is a header filename. `PageDefinition.NeededCppWinRTProjectionHeaderFiles` and `TypeInfoDefinition.NeededCppWinRTProjectionHeaderFiles` are useful existing discovery points, but the semantic model should preferably retain the WinRT namespace/type dependency before formatting it into C++ syntax.
Conceptually:
```text
NeededCppWinRTProjectionNamespaces
- Windows.Foundation
- Microsoft.UI.Xaml
- Microsoft.UI.Xaml.Controls
- OpenNet.UI.Xaml.Control.Graph
- OpenNet.UI.Xaml.Control.Effect
```
could then be emitted through a common C++/WinRT dependency writer. The exact implementation could retain compatibility wrappers around the existing header-file properties while transitioning templates, but the important architectural change is that generator code should not have to reverse-engineer a namespace from `winrt/Foo.Bar.h` when deciding to emit a module dependency.
## Proposed fix, layer 3: centralize C++/WinRT projection dependency emission
`CppWinRT_CodeGenerator` is a natural place to centralize common projection formatting behavior. Today, different T4 templates independently hard-code their own projection headers or loop over header-file collections. A shared helper could conceptually expose operations such as:
```text
EmitRequiredProjectionDependencies(...)
EmitKnownProjectionDependency(namespace)
```
and apply the selected project strategy consistently.
In traditional header mode, output must remain behaviorally unchanged:
```cpp
#include
#include
```
In module mode, the helper could participate in whatever strategy is selected by the implementation, for example direct imports:
```cpp
import winrt.Microsoft.UI.Xaml;
import winrt.Microsoft.UI.Xaml.Controls;
```
or generated-preamble metadata instead of textual output at the call site.
Centralizing this decision would prevent `PagePass1`, `AppPass1`, `BindingInfoPass1`, `TypeInfoPass1`, and other generators from each inventing a slightly different module policy.
## Proposed fix, layer 4: preserve local implementation-header dependencies
The module conversion must not erase `ClassToHeaderFileMap`/`AdditionalXamlTypeInfoIncludes` behavior. Local implementation headers remain required when generated metadata refers to `implementation::Type`, local activators, implementation-only static methods, or other symbols that are not exported by the public WinRT projection module.
A safe first implementation should therefore treat local implementation includes as a separate category and preserve them in module mode. If Microsoft later wants to introduce implementation modules, that should be a separate explicit design with its own build graph, rather than an accidental consequence of public projection-module support.
## Proposed fix, layer 5: make generated App, Page, BindingInfo, TypeInfo, and metadata-provider code use the same mode-aware mechanism
After the dependency abstraction exists, the individual C++/WinRT generator templates should be audited. At minimum, the review should cover:
```text
CppWinRT_PagePass1.tt
CppWinRT_PagePass2.tt
CppWinRT_AppPass1.tt
CppWinRT_AppPass2.tt
CppWinRT_BindingInfoPass1.tt
CppWinRT_BindingInfoPass2.tt
CppWinRT_TypeInfoPass1.tt
CppWinRT_TypeInfoPass2.tt
CppWinRT_XamlMetaDataProviderPass1.tt
CppWinRT_XamlMetaDataProviderPass2.tt
```
Hard-coded `winrt/*.h` dependencies should be routed through the common mechanism. Dynamically discovered projection dependencies should also use that mechanism. STL and COM/Win32 dependencies should remain ordinary header dependencies unless a separate supported module strategy exists for them; the framework solution should avoid application-defined include-guard suppression.
## Proposed fix, layer 6: integrate generated translation units with the XAML two-pass build automatically
If the final design uses a generated module preamble or forced include, the XAML build targets should attach it automatically to the correct generated `ClCompile` items. Applications should not need to duplicate the current documentation's item-name conditions for `XamlTypeInfo.g`, `XamlTypeInfo.Impl.g`, `XamlMetaDataProvider`, and `XamlLibMetadataProvider.g`.
The existing `ComputeXamlGeneratedCompileInputs` and `CompileXamlGeneratedFiles` targets provide natural integration points because they own the generated C++ items and their late compilation. The solution also needs to respect the documented difference between static-library XAML projects and EXE/DLL projects, where Pass2 scheduling is attached to different parent targets. Whatever metadata is required for module-aware compilation should be present before both normal generated-source compilation and the later `CompileXamlGeneratedFiles` path execute.
If a generated preamble is used, it should be owned by the framework build rather than by the application, generated from the actual discovered projection dependencies, and ordered so that ordinary generated COM/Win32/STL includes remain legal. This would eliminate the need for application code to define implementation-detail include guards merely to protect the import/include boundary.
## Proposed fix, layer 7: coordinate with the actual C++/WinRT module graph rather than guessing module availability
The XAML compiler can discover namespaces, but C++/WinRT/MSBuild knows which modules actually exist. A complete implementation should therefore avoid assuming that every namespace discovered by XAML has a locally generated `.ixx` file simply because `CppWinRTBuildModule=true`.
Potential integration options include passing the relevant `CppWinRTModuleInclude`/`CppWinRTModuleExclude` policy into XAML compilation, passing an MSBuild item set describing available projection modules, generating a preamble after C++/WinRT projection/module discovery, or letting C++/WinRT targets consume a list of XAML-discovered namespace dependencies and produce the appropriate preamble. The exact direction can be chosen by maintainers based on target ordering and ownership, but the contract should keep semantic dependency discovery on the XAML side and module-resolution policy on the C++/WinRT/MSBuild side.
This also allows module-builder scenarios to work correctly. A required module may come from another project through `CppWinRTConsumeModule`; the XAML compiler should not care where its IFC is physically located as long as the C++ build graph resolves the import.
## Proposed fix, layer 8: retain complete backward compatibility for header-mode C++/WinRT projects
The existing textual projection-header behavior is valid and widely deployed. `CppWinRTBuildModule` defaults to false, and existing WinUI C++ projects should not need to change. The module-aware path should therefore be opt-in based on the supported C++/WinRT project configuration, and header-mode generated output should remain equivalent to current output.
This compatibility requirement is another reason to avoid ad-hoc global rewrites. A centralized projection-dependency abstraction can preserve the current output exactly in header mode while enabling a second supported lowering strategy in module mode.
## Suggested regression and integration test matrix
A single test that compiles `XamlTypeInfo.g.cpp` is insufficient because the affected surface spans both XAML passes, generated headers, compiled bindings, local activation, App generation, metadata-provider generation, and cross-project module resolution. The following matrix would provide meaningful coverage.
### Project types
```text
WinUI C++ application/EXE
WinUI C++ component/DLL with XAML
C++ XAML static library
```
The static-library case is important because the XAML module guide documents a distinct Pass2/`CompileXamlGeneratedFiles` scheduling path.
### Projection-consumption modes
```text
Traditional C++/WinRT projection headers
CppWinRTBuildModule=true with local module generation
CppWinRTBuildModule=true with a referenced module-builder project
```
The existing header path must remain unchanged.
### XAML scenarios
1. Built-in WinUI type only:
```xml
```
2. External WinRT control/type:
```xml
```
3. Local projected runtimeclass:
```xml
```
4. Named local element that generates a field:
```xml
```
5. Event dependency:
```xml
```
6. Compiled binding:
```xml
```
7. Compiled binding whose member type is a nontrivial WinRT projected type.
8. Generic WinRT types so nested generic argument dependency discovery is exercised.
9. A local constructible type that participates in generated TypeInfo and therefore requires `implementation::Type`.
10. A local collection or dictionary type that causes generated collection/dictionary metadata delegates.
11. An enum used by generated TypeInfo conversion code.
12. `ApplicationDefinition` so `CppWinRT_AppPass1/2` are covered.
13. `XamlMetaDataProvider` generation so the boundary with C++/WinRT-generated `XamlMetaDataProvider.g.h` is covered.
14. A namespace module supplied by another project rather than generated locally.
15. `CppWinRTModuleInclude`/`CppWinRTModuleExclude` configured so module availability is not identical to "all reachable WinMD namespaces."
16. A project that switches from header mode to module mode without cleaning, verifying that incremental state invalidation regenerates the appropriate C++.
17. A project that switches from module mode back to header mode without cleaning.
### Success criteria
In named-module mode, these scenarios should build without requiring the application to manually add projection imports solely for XAML-generated code, without requiring an application-defined `/FI` target for XAML-generated metadata translation units, and without requiring application code to define private Windows/COM/STL include guards to protect generated include ordering.
In traditional header mode, generated output and behavior should remain compatible with existing projects.
## Minimal regression example
A compact integration repro could use two local namespaces so that XAML type resolution necessarily crosses a projection boundary.
IDL:
```idl
namespace TestApp.Controls.Effects
{
runtimeclass AnimatedValue : Microsoft.UI.Xaml.Controls.Control
{
AnimatedValue();
String Value;
}
}
```
Page XAML:
```xml
```
Project:
```xml
true
true
```
The expected framework behavior is that the XAML-generated C++ receives the required `TestApp.Controls.Effects` projection dependency through the supported module-aware build/codegen path. The application should not have to manually add:
```cpp
import winrt.TestApp.Controls.Effects;
```
to `MainPage.xaml.h` only to satisfy generated fields/connectors, and it should not have to inject its own module preamble into `XamlTypeInfo.g.cpp`.
If the local implementation type is needed by generated TypeInfo activation, the framework should continue to include the appropriate local implementation header separately; that is not the same dependency as the public `winrt.TestApp.Controls.Effects` projection.
## Non-goals and scope clarification
This issue does not require the XAML compiler to generate C++/WinRT IFCs itself. The C++/WinRT generator and its MSBuild targets already own namespace module generation and module graph behavior.
This issue does not require all local C++ implementation types to become C++ modules. Local implementation headers can remain textual dependencies in an initial solution.
This issue does not require replacing the existing header-mode XAML code generator. Header mode should remain supported and unchanged when C++/WinRT module mode is disabled.
This issue should not be solved by requiring every application to maintain a hard-coded global list of `import winrt.*;` statements. The XAML compiler already discovers a large part of that dependency graph.
This issue should not be considered solved merely because `/FI ModulePreamble.h` can make one generated translation unit compile. The module-unaware dependency model also affects generated Pass1 headers, compiled-binding infrastructure, App generation, and metadata-provider boundaries.
## Correctness constraints for any proposed patch
A patch should be evaluated against several correctness constraints beyond "the sample compiles." First, header mode must remain source-compatible and should ideally produce byte-for-byte or semantically equivalent generated dependency prologues unless another intentional cleanup is made. Second, module mode must not accidentally replace local implementation dependencies with public projection modules. Third, generated imports or generated preambles must appear in a C++ context where module import declarations are legal; this is especially relevant when `.xaml.g.h` is included from user-authored module units. Fourth, the solution must not depend on `__has_include` as a proxy for IFC availability. Fifth, the generated dependency set must be sufficient before `WINRT_IMPORT_MODULE` makes later textual projection headers inert. Sixth, changing module configuration must invalidate cached XAML-generated C++ so incremental builds do not mix modes. Seventh, App, Page, BindingInfo, TypeInfo, and metadata-provider generation must follow one coherent policy. Eighth, static-library Pass2 compilation must receive the same generated-source metadata as EXE/DLL builds. Ninth, the design must permit IFCs supplied through project references/module builders and must not assume every namespace module is locally generated. Tenth, design-time builds and IntelliSense-oriented XAML generation should either receive the same mode information or have an explicitly documented fallback so generated design-time state does not diverge from real builds.
## Related source locations
XAML compiler project model:
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/XamlProjectInfo.cs#L1-L127
XAML MSBuild task:
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/CompileXaml.cs#L1-L339
Serializable compiler inputs:
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/Exe/Microsoft.UI.Xaml.Markup.Compiler.MSBuildInterop/CompilerInputs.cs#L1-L199
XAML build targets:
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/Targets/Microsoft.UI.Xaml.Markup.Compiler.interop.targets#L1-L1156
Page dependency analysis:
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/PageDefinition.cs#L1-L363
TypeInfo dependency analysis:
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/TypeInfoDefinition.cs#L1-L378
Common C++/WinRT generator:
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/CppWinRT_CodeGenerator.cs#L1-L129
Page Pass1:
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/CppWinRT/CppWinRT_PagePass1.tt#L1-L95
Page Pass2:
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/CppWinRT/CppWinRT_PagePass2.tt#L1-L391
App Pass1:
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/CppWinRT/CppWinRT_AppPass1.tt#L1-L78
BindingInfo Pass1:
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/CppWinRT/CppWinRT_BindingInfoPass1.tt#L1-L289
TypeInfo Pass1:
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/CppWinRT/CppWinRT_TypeInfoPass1.tt#L1-L224
TypeInfo Pass2:
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/CppWinRT/CppWinRT_TypeInfoPass2.tt#L1-L441
XamlMetaDataProvider Pass1:
https://github.com/microsoft/microsoft-ui-xaml/blob/6112d936461edb6d81ce7db983c74cc60ea2bc28/src/XamlCompiler/BuildTasks/Microsoft/Xaml/XamlCompiler/CodeGenerators/CppWinRT/CppWinRT_XamlMetaDataProviderPass1.tt#L1-L63
C++/WinRT module documentation:
https://github.com/microsoft/cppwinrt/blob/master/nuget/modules.md
C++/WinRT named-module implementation discussion:
https://github.com/microsoft/cppwinrt/pull/1575
OpenNet real-world module migration:
https://github.com/hoshiizumiya/OpenNet/commit/92dacc87cefe4a3ee29e46c23c1c3490108ac42a
generated-XAML compatibility header from that migration: https://github.com/hoshiizumiya/OpenNet/blob/92dacc87cefe4a3ee29e46c23c1c3490108ac42a/OpenNet/XamlGeneratedWorkaround.h
OpenNet page demonstrating XAML namespace dependencies and hand-written module imports:
https://github.com/hoshiizumiya/OpenNet/blob/92dacc87cefe4a3ee29e46c23c1c3490108ac42a/OpenNet/UI/Xaml/View/Pages/TaskSpeedGraphPage.xaml
https://github.com/hoshiizumiya/OpenNet/blob/92dacc87cefe4a3ee29e46c23c1c3490108ac42a/OpenNet/UI/Xaml/View/Pages/TaskSpeedGraphPage.xaml.h
## Recommended final direction
The recommended fix is not "replace some generated includes with imports" and not "document the `/FI` workaround more prominently." The long-term framework fix should make C++/WinRT projection consumption a first-class mode of the XAML C++ code generator.
The implementation should first propagate the selected C++/WinRT module-consumption state from MSBuild into both XAML compilation passes and into the compiler project model. It should then refactor projection dependency tracking so the primary information is the required WinRT namespace/type projection rather than the final header filename. A common dependency-emission/integration layer should be used by Page, App, BindingInfo, TypeInfo, and metadata-provider generators. In traditional mode, that layer should preserve today's textual projection headers. In module mode, it should express those same semantic dependencies through a supported named-module mechanism, either by emitting imports at locations whose legality is guaranteed or by generating/attaching a framework-owned module preamble to the appropriate generated compilation units. Local implementation headers required for `implementation::Type` activation must remain a distinct dependency category.
The build integration should automatically apply any required generated-source metadata before both normal C++ compilation and `CompileXamlGeneratedFiles`, including the static-library Pass2 path. Module availability should be resolved through the existing C++/WinRT/MSBuild module graph so that local generation, module-builder projects, include/exclude filters, and referenced BMIs remain supported. The application's source code should not need to manually duplicate XAML namespace dependencies as `import winrt.*;`, and it should not need to define private COM/Win32/STL include guards or maintain a project-specific `/FI` target merely to make framework-generated C++ compatible with an officially supported C++/WinRT build mode.
In short, the desired architecture is:
```text
XAML semantic type dependency
|
v
module-agnostic projection dependency model
|
+-----------------------------+
| |
v v
traditional header mode C++/WinRT module mode
#include supported module dependency
|
v
C++/WinRT/MSBuild resolves IFC
```
That would turn the current application-owned compatibility layer into a framework-owned integration contract, preserve existing header projects, and make C++/WinRT 3.x named modules a genuinely supported configuration for nontrivial WinUI/XAML applications rather than a mode that works only after manually adapting the XAML compiler's generated C++.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at Microsoft.UI.Xaml.Markup.Compiler.interop.targets and trace the CompileXaml inputs through CompileXaml.cs, CompilerInputs.cs, and XamlProjectInfo.cs. Then inspect PageDefinition.cs and the CppWinRT generators to understand where semantic projection dependencies become header filenames. Done means the XAML compiler has a supported module-aware integration for named-module projects rather than requiring an application-specific preamble workaround.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- build-system, desktop, devtools
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100