microsoft-ui-xaml icon indicating copy to clipboard operation
microsoft-ui-xaml copied to clipboard

Unable to implement classic COM interface on XAML component with C++/WinRT

Open sylveon opened this issue 5 years ago • 12 comments

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

  1. Create a new blank C++/WinRT UWP app in VS
  2. 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.
  3. In MainPage.cpp, add the implementation:
    IFACEMETHODIMP MainPage::SetSwapChain(IDXGISwapChain*) noexcept
    {
        // dummy implementation
        return S_OK;
    }
    
  4. Try building the app.
  5. 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 image

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.

sylveon avatar Sep 25 '20 15:09 sylveon

@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

StephenLPeters avatar Sep 25 '20 23:09 StephenLPeters

going with markup team for now.

StephenLPeters avatar Sep 25 '20 23:09 StephenLPeters

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.

sylveon avatar Sep 25 '20 23:09 sylveon

@RealTommyKlein - can you take a look?

fabiant3 avatar Sep 25 '20 23:09 fabiant3

My workaround was implementing the functionality in a subclass that doesn't use the XAML compiler: https://github.com/TranslucentTB/TranslucentTB/commit/8f68f3c0b1b1900b76c3900c98b8270da1e0c535

sylveon avatar Oct 01 '20 04:10 sylveon

Guessing that there's no workaround for this one? Just bit Terminal :/

DHowett avatar Nov 24 '20 20:11 DHowett

There's 2 options:

  • putting this very ugly macro at the bottom of .h and 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)

sylveon avatar Nov 24 '20 20:11 sylveon

@Scottj1s any chance someone from the Xaml compiler can take a look at this seemingly trivial bug?

kennykerr avatar Nov 24 '20 21:11 kennykerr

@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?

DHowett avatar Nov 24 '20 21:11 DHowett

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).

sylveon avatar Nov 24 '20 21:11 sylveon

Without this explicit instantiation, you get linker errors.

sylveon avatar Nov 24 '20 21:11 sylveon

Bump, still an issue

sylveon avatar Jul 15 '23 00:07 sylveon