Skip to content

Module support v1#1556

Draft
DefaultRyan wants to merge 29 commits intomasterfrom
feature/modules_v1
Draft

Module support v1#1556
DefaultRyan wants to merge 29 commits intomasterfrom
feature/modules_v1

Conversation

@DefaultRyan
Copy link
Copy Markdown
Member

@DefaultRyan DefaultRyan commented Mar 30, 2026

This is a draft of the initial modules support that so far seems to evade various toolchain implementation issues and builds successfully. I'm focusing on consumption, but also trying to get consumption with some basic component authoring to work as well.

Some credit goes to @sylveon and @YexuanXiao for the trailblazing they've done in their forks.

I'll continue to update and add notes as I iterate, but it's getting functional enough that publishing a draft is helpful.

Copy link
Copy Markdown
Contributor

@sylveon sylveon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

winrt_to_hresult_handler and friends needs WINRT_EXPORT extern "C++" to work properly when mixing non-modules and modules code in the same final DLL/EXE

w.write(strings::base_includes);
w.write(R"(
#if defined(__cpp_lib_modules) && defined(WINRT_IMPORT_STD)
import std;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just omit this bit and rely on the ixx to import std first if WINRT_IMPL_MODULES is defined?

ixx.write("#define WINRT_IMPL_GLOBAL_MODULE_FRAGMENT\n");
ixx.write(strings::base_includes);
ixx.write("#undef WINRT_IMPL_GLOBAL_MODULE_FRAGMENT\n");
ixx.write(strings::base_std_includes);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be able to avoid including any std header by writing import std below export module std

@@ -0,0 +1,33 @@

// This header provides the preprocessor macros needed by generated C++/WinRT
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest avoiding to repeat this code somehow, it's already in base_macros.

// header must be included textually.

#ifndef WINRT_EXPORT
#define WINRT_EXPORT
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theorically we could omit this bit as a module would always have WINRT_EXPORT defined.

@YexuanXiao
Copy link
Copy Markdown
Contributor

YexuanXiao commented Mar 31, 2026

I tested it locally. After enabling CppWinRTModule, compiling winrt.ixx.obj failed, showing that the symbol for CoGetCallContext could not be found. Manually linking Ole32.lib resolved the issue, but the header mode does not have this problem. I'm not sure what the cause is. Additionally, if both CppWinRTModule and BuildStlModule are enabled, it will break code that uses only header files.

@DefaultRyan
Copy link
Copy Markdown
Member Author

I tested it locally. After enabling CppWinRTModule, compiling winrt.ixx.obj failed, showing that the symbol for CoGetCallContext could not be found. Manually linking Ole32.lib resolved the issue, but the header mode does not have this problem. I'm not sure what the cause is. Additionally, if both CppWinRTModule and BuildStlModule are enabled, it will break code that uses only header files.

You'll need to provide more info on how you configured your test project. I'm not hitting this in the test_cpp20_module project, even after adding some use of winrt::access_token.

@YexuanXiao
Copy link
Copy Markdown
Contributor

YexuanXiao commented Mar 31, 2026

You'll need to provide more info on how you configured your test project. I'm not hitting this in the test_cpp20_module project, even after adding some use of winrt::access_token.

I downloaded the package built by GitHub CI, used the C++/WinRT console project template, installed the NuGet package, then set the C++ standard to C++20, enabled the CppWinRTModule and BuildStlModule options, removed and disabled pch, and used the following main.cpp:

#include <winrt/Windows.Foundation.h>
#include <cstdio>

using namespace winrt;
using namespace Windows::Foundation;

int main()
{
    init_apartment();
    Uri uri(L"http://aka.ms/cppwinrt");
    std::printf("Hello, %ls!\n", uri.AbsoluteUri().c_str());
}
import winrt;
import std;

using namespace winrt;
using namespace Windows::Foundation;

int main()
{
    init_apartment();
    Uri uri(L"http://aka.ms/cppwinrt");
    std::printf("Hello, %ls!\n", uri.AbsoluteUri().c_str());
}

Project.zip
Using a different .cpp results in different errors; the other files are the same. Even with the build from 9c021d2, this issue still exists. The version of MSVC used is 195035728.

@sylveon
Copy link
Copy Markdown
Contributor

sylveon commented Apr 1, 2026

Idk about exporting the impl namespace, feel like we should find a better solution.

@YexuanXiao
Copy link
Copy Markdown
Contributor

YexuanXiao commented Apr 1, 2026

If exporting impl is unavoidable, I still hope to avoid a single winrt module. A slightly cleaner approach is to provide winrt.impl and winrt.base, where winrt.base imports winrt.impl but only re-exports non-impl declarations:

export module winrt.base;
import winrt.impl; // Not re-exported
export namespace winrt {
using winrt::hstring;
...
}

winrt.impl is intended for use only by files generated by cppwinrt, while user code should use winrt.base. And generate independent modules for each root namespace, such as winrt.Windows.

@sylveon
Copy link
Copy Markdown
Contributor

sylveon commented Apr 1, 2026

My idea would be to generate the things required to produce as part of the module too, but that would eliminate the ability to share winrt.ifc between projects.

@YexuanXiao
Copy link
Copy Markdown
Contributor

Damaging the development experience just to hide the implementation is completely not worth it in my opinion, especially since users can already access them through headers anyway. Considering only the development experience in Visual Studio, we could request the MSVC and IntelliSense teams to support an attribute such as [[msvc::internal]] to prevent IntelliSense from showing it.

@DefaultRyan
Copy link
Copy Markdown
Member Author

DefaultRyan commented Apr 1, 2026

I started out exporting the impl namespace to get component authoring to work. I needed to just get things building and find out if I was hitting more MSVC module limitations, or if it was a simple fix.

I'm currently investigating the feasability of splitting of winrt_base and winrt_base.impl to reduce the scope of impl exports. If that goes well, I'll also look at making the component projection a module as well, to remove the impl namespace in component code.

But the big news is that I've done enough massaging on this code to get it to work in VS2022 (and VS2026 using the v143 toolset). At this point, I've got a checkpoint (9250b45) where the entire solution builds using the v143 toolset, including the 3 module test projects:

  • A shared library project that builds the big winrt.ixx so other projects can quickly consume the resulting ifc/obj files. (Big improvement from unsupported shared PCH workarounds)
  • A test code project that consumes the shared module and excercises some functionality.
  • A component project that consumes the shared module in a component authoring setup.

This also means that interested folks can checkout the branch and build things themselves and tinker with it.

So, the TODOs are:

  • Try to reduce the impl exporting.
  • Another test component project or two that does base+derived stuff to test inheritance. Also add a subclassed XAML type to the component to make sure that works properly.
  • Add end-to-end nuget tests to ensure the modules experience via the nupkg works out of the box.
  • Figure out if anything needs to be added for VS project references consuming another project's module. Also, try to streamline/standardize the shared IFC approach in the nuget to cut down on the need to manually add IFC/OBJ files to vcxproj files.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants