Skip to content

Commit 3300664

Browse files
committed
Update docs
1 parent 19ed79d commit 3300664

File tree

4 files changed

+192
-11
lines changed

4 files changed

+192
-11
lines changed

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ With development on the original project slowing, we believe it's time for the c
88

99
## How to use
1010

11-
Unfortunately, for reasons I don't currently understand, the package ID Microsoft.Windows.CppWinRT cannot be modified. Therefore, it cannot be published to NuGet either. Until a solution is found for this issue, C++/WinRT Plus will only release the package on GitHub. You can add a NuGet.config file with the following content in your root sln/slnx directory, and place the packages into LocalPackages, so that NuGet can find them.
11+
You can add a NuGet.config file with the following content in your root sln/slnx directory, and place the packages into LocalPackages, so that NuGet can find them.
1212

1313
```config
1414
<?xml version="1.0" encoding="utf-8"?>
@@ -25,13 +25,12 @@ Microsoft's C++/WinRT version number format is 2.0.{yymmdd}.1. Since Microsoft's
2525
## Changelog
2626

2727
2026/2/22: Removed support for C++17 and C++/CX. Existing projects need to upgrade their C++ standard to C++20.
28+
2026/2/24: C++/WinRT Plus now supports C++ standard modules. See the [blog post](./about_module.md) for details.
2829

2930
<hr>
3031

3132
The following content is from the original C++/WinRT documentation and remains a valuable resource.
3233

33-
<hr>
34-
3534
# The C++/WinRT language projection
3635

3736
C++/WinRT is an entirely standard C++ language projection for Windows Runtime (WinRT) APIs, implemented as a header-file-based library, and designed to provide you with first-class access to the modern Windows API. With C++/WinRT, you can author and consume Windows Runtime APIs using any standards-compliant C++20 compiler.

about_module.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
# C++/WinRT Plus: Bringing C++ Standard Modules to Windows Development
2+
3+
## Implementation Overview
4+
5+
Here's how this was implemented.
6+
7+
This week, I implemented C++ standard modules on my own C++/WinRT fork ([C++/WinRT Plus](https://github.com/YexuanXiao/cppwinrtplus)). I spent approximately 36-40 hours of effective time implementing it (including this article). While I don't consider my understanding of modules to be as deep as those who implemented them personally, I possess sufficient knowledge to understand compiler complaints. Therefore, I initially viewed it as a challenge, uncertain whether it would work. The results turned out to be excellent.
8+
9+
During the implementation process, I used AI to help me understand how C++/WinRT works. The C++/WinRT code generator was simpler than I estimated, so AI interpreted it well. Due to the high code quality of C++/WinRT itself, I was able to make gradual progress even during the most difficult moments.
10+
11+
The current implementation status is that all header files can be built as modules. I'm uncertain whether anything is missing, but I haven't seen any bad news. I am seeking more people interested in the project to help improve C++/WinRT Plus and reduce my burden. My fork maintains full compatibility and test coverage, except it no longer supports C++17 and C++/CX. I even discovered [a bug](https://github.com/microsoft/cppwinrt/issues/1540) in the C++/WinRT tests.
12+
13+
If you are also interested in C++ modules/C++/WinRT, or if you are currently using C++/WinRT, I hope you can try my fork. If you are satisfied with it, please share it with more people.
14+
15+
### MSVC Compiler Status
16+
17+
The MSVC team has fixed a large number of bugs over the past few years, and it can be said that modules are now highly usable. One remaining bug is that cross-module using declarations don't work, requiring alias declarations as a replacement. Actually, apart from this bug I knew about in advance, I haven't encountered any other MSVC bugs. The bug was [marked as fixed a few days ago](https://developercommunity.visualstudio.com/t/C-modules-compile-error-when-using-a-u/10981263#T-ND11047313) and will be released with the next version.
18+
19+
### C++/WinRT Characteristics
20+
21+
C++/WinRT is a header-only library, which brings many conveniences. This means any header files can be combined without conflicts. More valuable is that C++/WinRT doesn't use internal linkage, avoiding various potential issues.
22+
23+
However, C++/WinRT still needed significant improvements to achieve modularization.
24+
25+
- First, C++/WinRT doesn't strictly use `std::` qualification for things existing in the C standard library, which meant I had to manually fix all missing prefixes.
26+
- Second, C++/WinRT mistakenly placed a declaration in a file that should only define macros. I believe this was an oversight in the collaboration process, since C++/WinRT intentionally separates macros (which also brings many conveniences), but missed this one. My solution was to split that declaration into a separate header file.
27+
28+
Finally, C++/WinRT actually chose the wrong path when attempting to support modules by having all header files share a single module, which leads to terrible results. This would make the BMI (or .ifc) file 260MB in size—9 times larger than the STL's 29MB. It's foreseeable that with such a design, compilation will slow down.
29+
30+
### The Solution
31+
32+
Therefore, I implemented a more complicated approach:
33+
34+
**Header File Pattern:**
35+
36+
```cpp
37+
#pragma once
38+
#ifndef WINRT_XXX_H
39+
#define WINRT_XXX_H
40+
#pragma push_macro("WINRT_EXPORT")
41+
#undef WINRT_EXPORT
42+
#if !defined(WINRT_MODULE) // legacy header path
43+
#define WINRT_EXPORT
44+
#include <winrt/base.h>
45+
#include <dep headers>
46+
#else
47+
#define WINRT_EXPORT export
48+
#endif
49+
// declarations/definitions
50+
#pragma pop_macro("WINRT_EXPORT")
51+
#endif
52+
```
53+
54+
**Module Interface Units (.ixx):**
55+
56+
```cpp
57+
module;
58+
#define WINRT_MODULE
59+
#include <intrin.h>
60+
#include <cstddef>
61+
#include <version>
62+
#ifdef _DEBUG
63+
#include <crtdbg.h>
64+
#endif
65+
// This file only defines macros and does not contain any declarations
66+
#include "winrt/module.h"
67+
export module Windows.XX;
68+
import deps;
69+
70+
#include "impls"
71+
```
72+
73+
Since macros in the global module fragment are visible to subsequent `#includes` in the current file, defining the `WINRT_MODULE` macro on the second line will convert subsequent header files into module implementation files.
74+
75+
This design allows you to import exactly what you want, not unnecessary junk. This is especially significant for the `Windows.UI.Xaml` namespace, which generates over 90MB of BMI, accounting for one-third of the total. (I advocate for using WinUI3.)
76+
77+
### Build Configuration
78+
79+
**MSBuild Limitations:**
80+
81+
Unfortunately, due to special reasons (which won't change in the future), when using MSBuild with C++/WinRT Plus, once interface units are added, they are compiled. Therefore, by default, even if you don't use `Windows.UI.Xaml`, it still consumes valuable time.
82+
83+
**Namespace Exclusion:**
84+
85+
Thus, C++/WinRT Plus provides the ability to disable certain namespaces via a configuration file. You can create a `CppWinRT.config` file in your solution directory with the following content:
86+
87+
```xml
88+
<?xml version="1.0" encoding="utf-8"?>
89+
<configuration>
90+
<exclude>
91+
<prefix>Windows.UI.Xaml</prefix>
92+
<!-- Windows.ApplicationModel.Store depends on Windows.UI.Xaml -->
93+
<prefix>Windows.ApplicationModel.Store</prefix>
94+
</exclude>
95+
</configuration>
96+
```
97+
98+
This can significantly reduce compilation time. Note that namespace exclusion occurs at a very early stage of the build process, so a clean build needs to be performed for it to take effect.
99+
100+
If you're using MSBuild, you can still use C++/WinRT modules even with C++20. You just need to enable module support in the C++/WinRT options.
101+
102+
### CMake Integration
103+
104+
Starting from CMake 4.3 (the CMake version in current Visual Studio 2026 Insider is 4.2), CMake supports compiling standard library modules. Therefore, you only need to write the following `CMakeLists.txt` to use both `std` modules and C++/WinRT modules:
105+
106+
```cmake
107+
cmake_minimum_required(VERSION 4.3)
108+
project(winrt_module LANGUAGES CXX)
109+
110+
set(CMAKE_CXX_MODULE_STD 1)
111+
set(CMAKE_CXX_STANDARD 23)
112+
set(CMAKE_CXX_EXTENSIONS OFF)
113+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
114+
115+
set(CPPWINRT_EXE "cppwinrt" CACHE FILEPATH "Path to cppwinrt executable")
116+
set(CPPWINRT_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/cppwinrt")
117+
118+
execute_process(
119+
COMMAND "${CPPWINRT_EXE}" -input local -output "${CPPWINRT_OUT_DIR}" -modules -verbose
120+
RESULT_VARIABLE CPPWINRT_RESULT
121+
)
122+
if(NOT CPPWINRT_RESULT EQUAL 0)
123+
message(FATAL_ERROR "cppwinrt failed with exit code ${CPPWINRT_RESULT}")
124+
endif()
125+
126+
file(GLOB CPPWINRT_MODULES
127+
LIST_DIRECTORIES false
128+
CONFIGURE_DEPENDS
129+
"${CPPWINRT_OUT_DIR}/winrt/*.ixx"
130+
)
131+
list(SORT CPPWINRT_MODULES)
132+
133+
add_executable(main main.cpp)
134+
target_sources(main
135+
PRIVATE
136+
FILE_SET cxx_modules TYPE CXX_MODULES BASE_DIRS "${CPPWINRT_OUT_DIR}/winrt" FILES ${CPPWINRT_MODULES}
137+
)
138+
target_include_directories(main PRIVATE "${CPPWINRT_OUT_DIR}")
139+
target_link_libraries(main PRIVATE runtimeobject synchronization)
140+
```
141+
142+
## Best Practices for C++ Modules
143+
144+
What I want to tell users about implementing C++ modules:
145+
146+
**Use Standard Library Modules:**
147+
148+
If you want to use modules, you should use `std` modules (or `std.compact`). If you write `#include <standard headers>` in the global module fragment, all declarations from the standard library will exist both in that module and in other modules using the same approach. This not only increases module size but also greatly affects compilation efficiency.
149+
150+
When compiling modules, the C++ compiler merges all definitions from the global module fragment, which is extremely time-consuming as it needs to verify they're structurally identical. This is why it's called the global module fragment.
151+
152+
Three major standard libraries actually [support compiling standard library modules in C++20 mode](https://github.com/microsoft/STL/issues/3945), so it's not very reasonable for CMake to restrict this to C++23.
153+
154+
**Avoid modules without names:**
155+
156+
I don't recommend using `import <standard headers>` because it's not part of the Module TS and actually has numerous ambiguous issues. Additionally, I don't recommend using the cl compiler option `/translateIncludes` because I don't believe it can convert existing header projects to modules, and it's certainly not modules itself.
157+
158+
## Performance Analysis
159+
160+
I tested the performance difference between using precompiled headers (PCH) and modules. The test method involved including all header files and importing all modules.
161+
162+
**Build Time and File Size:**
163+
164+
- Building the PCH took 1 minute and 40 seconds
165+
- Building the modules took 2 minutes
166+
- PCH file size: 2.4GB
167+
- Module intermediate files: 480MB
168+
169+
This result is not surprising, as in my C++/WinRT module implementation, the module implementation files require more complex preprocessing, and also need to analyze dependencies. Clearly, this test favors PCH. Moreover, since the PCH includes all declarations, it is expected that the PCH approach will become slower as the number of source files increases. Once the module is precompiled, using it will be super fast.
170+
171+
**Memory Usage:**
172+
173+
When using modules, memory fluctuated between several tens of MB and 300MB, with a final spike reaching 1.1GB. When using PCH, memory gradually grew to 2.5GB during the first minute, then stabilized at 300MB.
174+
175+
### Conclusion
176+
177+
Modules also solve an important problem: they do not export any macros, providing a clean interface. Therefore, even though PCH is slightly faster than modules in the most silly tests, its disadvantages in other areas are enough to outweigh its advantages.

nuget/readme.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
# Microsoft.Windows.CppWinRT NuGet Package
1+
# YexuanXiao.CppWinRTPlus NuGet Package
2+
3+
Please read the [repository](https://github.com/YexuanXiao/cppwinrtplus)'s README.md for usage instructions.
24

35
## Overview
46

@@ -64,7 +66,6 @@ C++/WinRT behavior can be customized with these project properties:
6466
| CppWinRTMergeNoValidate | true \| *false | Disables mdmerge validation |
6567
| CppWinRTUsePrefixes | *true \| false | Uses a dotted prefix namespace convention (versus a nested folder convention) |
6668
| CppWinRTUseModules | true \| *false | Generate and compile C++/WinRT namespace modules (.ixx) instead of winrt.ixx; disables PCH for generated files |
67-
| CppWinRTConfigFile | path | Configuration file for including or excluding namespaces or other declarations/definitions |
6869
| CppWinRTPath | ...\cppwinrt.exe | NuGet package-relative path to cppwinrt.exe, for custom build rule invocation |
6970
| CppWinRTParameters | "" | Custom cppwinrt.exe command-line parameters (be sure to append to existing) |
7071
| CppWinRTFastAbi | true \| *false | Enables Fast ABI feature for both consuming and producing projections |
@@ -74,29 +75,33 @@ C++/WinRT behavior can be customized with these project properties:
7475
| CppWinRTEnableDefaultPrivateFalse | true \| *false | Indicates whether this project uses C++/WinRT optimized default for copying binaries to the output directory |
7576
\*Default value
7677

77-
To customize common C++/WinRT project properties:
78+
To customize common C++/WinRT project properties:
7879
* right-click the project node
7980
* expand the Common Properties item
8081
* select the C++/WinRT property page
8182

83+
You can prevent unnecessary namespaces from being generated, such as, by adding a CppWinRT.config file to the solution directory. Since some modules are very large, this can effectively reduce compilation time.
84+
8285
The format of the configuration file is:
8386

8487
```xml
8588
<?xml version="1.0" encoding="utf-8"?>
8689
<configuration>
8790
<include>
8891
<prefix>Windows.Foundation</prefix>
89-
<prefix>Windows.UI</prefix>
9092
</include>
9193
<exclude>
92-
<prefix>Windows.UI.Composition</prefix>
94+
<prefix>Windows.UI.Xaml</prefix>
95+
<prefix>Windows.ApplicationModel.Store</prefix>
9396
</exclude>
9497
</configuration>
9598
```
9699

97-
It is equivalent to passing -config \<path\> to cppwinrt.exe, which combines the -include and -exclude options.
100+
It is equivalent to passing `-config <path\` to cppwinrt.exe, which combines the `-include` and `-exclude` options.
101+
102+
Note that namespace exclusion occurs at a very early stage of the build process, so a clean build needs to be performed for it to take effect.
98103

99-
Configuration files are only supported on Windows.
104+
It's suggest excluding Windows.UI.Xaml and Windows.ApplicationModel.Store, as the latter depends on the former. By excluding Windows.UI.Xaml, the BMI size can be reduced by one-third.
100105

101106
## InitializeComponent
102107

nuget/readme.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
========================================================================
2-
The YexuanXiao.CppWinRT Plus NuGet package automatically generates C++/WinRT projection headers,
2+
The YexuanXiao.CppWinRTPlus NuGet package automatically generates C++/WinRT projection headers,
33
enabling you to both consume and produce Windows Runtime classes.
44
========================================================================
55

0 commit comments

Comments
 (0)