CsWinRT icon indicating copy to clipboard operation
CsWinRT copied to clipboard

KeyNotFoundException thrown by SourceGenerator for a nested public struct in an internal class and only showing as a build warning

Open xyzzer opened this issue 1 year ago • 1 comments

Summary I have a WinUI3 C++/WinRT (WindowsAppSDK) app "CppHost" with most of the XAML UI implemented in a separate C# DLL "CsUiLibrary". I also have a separate C#-based test app "CsUiTestHost" that loads the same DLL to test the different parts of the UI it implements that helps speed up some prototyping. I was trying to add some functionality in CsUiLibrary the that used p/invoke into some old Win32 APIs and everything was working great in the CsUiTestHost, but at one point I started integrating the new UI into the CppHost and started hitting issues like:

Microsoft.UI.Xaml.dll!00007FFF57B03106: 80040154 - Class not registered
Unhandled exception at 0x00007FFF57D95175 (Microsoft.ui.xaml.dll) in CppHost.exe: 0xC000027B: An application-internal exception has occurred (parameters: 0x000001FB60348740, 0x0000000000000004).

After hours of adding and removing various parts of the code I noticed there was this not-seen-before warning in the output of the CsUiLibrary that didn't scream at me or break the build that only showed with the new code, which helped narrow things down a bit since I could now keep undoing or re-adding bits of code checking for which parts cause the warning to show up or disappear:

1>CSC : warning CS8785: Generator 'SourceGenerator' failed to generate source. It will not contribute to the output and compilation errors may occur as a result. Exception was of type 'KeyNotFoundException' with message 'The given key was not present in the dictionary.'.

It turns out the issue triggers when I define an internal class (I use a lot of internal classes to implement things in CsUiLibrary without cryptic/hidden projection errors, but some things I need to make public anyways because XAML C++/C# compilers likes it that way) and a nested public struct inside of it. Now, marking the nested struct as internal - fixes the problem and the problem only occurs if the struct is tagged with StructLayoutAttribute.

To Reproduce Create WinUI 3 C++ Desktop app and a C# WinUI 3 Desktop Class Library. Reference the C# library from the C++ host app and add something like a UserControl in the library to display the C++ host app and F5-test to make sure it works fine at this stage. Now add below class to the C# library:

namespace Foo.Interop
{
    internal static class Bar
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct Xyz
        {
            public int Abc;
        }
    }
}

This builds fine, but should show the error-as-warning KeyNotFoundException I mentioned earlier in the library build output and when you run the C++ host app now app - you get the unhandled "Class not registered" exception that crashes the app.

Expected behavior This builds and runs fine in a C# host app, so the exception from SourceGenerator shouldn't occur. I suspect there's a good reason why the WinUI class library msbuild project template hides the error without blocking the build, but maybe it's not a good enough reason or there should be an easier way to help match the runtime exception with an original error. That's likely more a WinUI problem than CsWinRT even if these things typically go in pair.

Version Info C++ host lists these imports:

  <Import Project="..\packages\Microsoft.WindowsAppSDK.1.5.240404000\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\packages\Microsoft.WindowsAppSDK.1.5.240404000\build\native\Microsoft.WindowsAppSDK.props')" />
  <Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.240412.1\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.240412.1\build\native\Microsoft.Windows.CppWinRT.props')" />
  <Import Project="..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.props')" />

C# library lists these details:

    <TargetFramework>net6.0-windows10.0.22621.0</TargetFramework>
    <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
    <RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
    <UseWinUI>true</UseWinUI>
    <CsWinRTComponent>true</CsWinRTComponent>

Additional context

xyzzer avatar May 21 '24 19:05 xyzzer

See https://github.com/microsoft/CsWinRT/issues/1202

asklar avatar Jun 27 '24 21:06 asklar