microsoft-ui-xaml
microsoft-ui-xaml copied to clipboard
Unable to implement classic COM interface on XAML component with C++/WinRT
Describe the bug
When creating a XAML control/page, it's not possible to make it implement a classic COM interface (as C++/WinRT does it, by appending it to the base template arguments: Something : SomethingT<Something, IMyClassicInterface>) because the XAML compiler codegen does not account for this case.
Steps to reproduce the bug
- Create a new blank C++/WinRT UWP app in VS
- Open MainPage.h, and add the following code:
- Include
<windows.ui.xaml.media.dxinterop.h>. - Change the struct declaration to
struct MainPage : MainPageT<MainPage, ISwapChainPanelNative>. - Add
IFACEMETHOD(SetSwapChain)(IDXGISwapChain*) noexcept;to the struct members.
- Include
- In MainPage.cpp, add the implementation:
IFACEMETHODIMP MainPage::SetSwapChain(IDXGISwapChain*) noexcept { // dummy implementation return S_OK; } - Try building the app.
- Observe compiler errors.
Expected behavior
The app builds fine and a consumer is able to cast the XAML component to ISwapChainPanelNative and call the method provided by that interface.
Screenshots

Version Info
NuGet package version: [Microsoft.Windows.CppWinRT 2.0.200921.6]
Windows app type:
| UWP | Win32 |
|---|---|
| Yes | Yes |
| Windows 10 version | Saw the problem? |
|---|---|
| Insider Build (xxxxx) | |
| May 2020 Update (19041) | Yes |
| November 2019 Update (18363) | |
| May 2019 Update (18362) | |
| October 2018 Update (17763) | |
| April 2018 Update (17134) | |
| Fall Creators Update (16299) | |
| Creators Update (15063) |
| Device form factor | Saw the problem? |
|---|---|
| Desktop | Yes |
| Xbox | |
| Surface Hub | |
| IoT |
Additional context
I replied Yes to the app type being Win32 because this issue showed up in system XAML islands while implementing IInitializeWithWindow to pass on a window handle to MessageDialog from my hosting code to the XAML code. I'm using MessageDialog because of ContentDialog's limitation of one dialog open at a time per thread. Implementing threading proved to be a huge pain, so I've decided on using MessageDialog for now.
The bug is caused by this line in MainPage.xaml.g.hpp:
template struct MainPageT<struct MainPage>;
It tries to specialize MainPageT using different parameters than what I used in my declaration, and since the class uses CRTP, it tries to cast this back to MainPage, but because MainPage actually inherits from a different type (different template instantiations are effectively different types), the cast fails.
@kennykerr or @jevansaks I feel like we do this already, I think one of you two probably knows the answer. If not this sounds like a xaml compiler bug? @fabiant3
going with markup team for now.
Yeah, it is a XAML compiler bug. Without XAML (a normal WinRT runtime component) it does work, but the XAML compiler's explicit specialization in .xaml.g.hpp is what breaks it.
@RealTommyKlein - can you take a look?
My workaround was implementing the functionality in a subclass that doesn't use the XAML compiler: https://github.com/TranslucentTB/TranslucentTB/commit/8f68f3c0b1b1900b76c3900c98b8270da1e0c535
Guessing that there's no workaround for this one? Just bit Terminal :/
There's 2 options:
- putting this very ugly macro at the bottom of
.hand undefining it in the.cpp#define MyPage MyPage, IThing - Writing the code implementing the classic COM interface in a WinRT class not using the XAML Compiler (
Page->PageWithThing->MyPage)
@Scottj1s any chance someone from the Xaml compiler can take a look at this seemingly trivial bug?
@sylveon There was a brief but real moment when I realized I was here because I was also implementing IInitializeWithWindow in a XAML islands application that I was quite crestfallen because these are the depths to which we must sink.
Why does the XAML generated header need to forcibly instantiate this template anyway? Does it not suffice to let the consumer do it?
The XAML codegen does this because some members are forward declared in the .g.h file, which you include, and then implemented + instantiated in .g.hpp, which you don't include (it's built as part of XamlTypeInfo.g.cpp).
Without this explicit instantiation, you get linker errors.
Bump, still an issue