microsoft / microsoft-ui-xaml

Windows UI Library: the latest Windows 10 native controls and Fluent styles for your applications
MIT License
6.36k stars 678 forks source link

Windows.UI.Color color = Microsoft.UI.Colors.White; throws a System.TypeInitializationException in UnitTests #8265

Closed chrkon closed 1 year ago

chrkon commented 1 year ago

Describe the bug

I just use this simple line in my WinUI3 / .net 6 code.

Windows.UI.Color color = Microsoft.UI.Colors.White;

Suddenly some of my tests are failing. So I have made a small sample to reproduce this failure. The compiler doesn't mark this line as wrong. So I was a little suprised.

I don't understand the registration problem, because the application, which is using this line, is running without this exception. So I guess I have missed something in the Test Project.

This is the complete Test Code to recreate the behaviour:

using NUnit.Framework;
namespace TestProject1
{
    [TestFixture]
    public class Tests
    {
        [Test]
        public void colorTest()
        {
            Windows.UI.Color color = Microsoft.UI.Colors.White; // This line throws an exception!
            Assert.That(color, Is.EqualTo(Windows.UI.Color.FromArgb(255,255,255,255)));
        }
    }
}

and this is the Exception I got, when the test was running:

        System.TypeInitializationException : The type initializer for 'WinRT.ActivationFactory`1' threw an exception.
        ----> System.Runtime.InteropServices.COMException : Klasse nicht registriert (0x80040154 (REGDB_E_CLASSNOTREG))
        at WinRT.ActivationFactory`1.As(Guid iid)
        at Microsoft.UI.Colors.Make___objRef_global__Microsoft_UI_IColorsStatics()
        at Microsoft.UI.Colors.get__objRef_global__Microsoft_UI_IColorsStatics()
        at Microsoft.UI.Colors.get_White()
        at TestProject1.Tests.Color() in C:\Users\xxxx\source\yyyy\zzzz\colorTest2\TestProject1\UnitTest1.cs:line 10
        --COMException
            at WinRT.BaseActivationFactory..ctor(String typeNamespace, String typeFullName)
        at WinRT.ActivationFactory`1..ctor()
        at WinRT.ActivationFactory`1..cctor()

The project settings (TestProject1.csproj) looks like this:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
      <TargetFramework>net6.0-windows10.0.22000.0</TargetFramework>
      <TargetPlatformMinVersion>10.0.22000.0</TargetPlatformMinVersion>
      <Nullable>enable</Nullable>
      <IsPackable>false</IsPackable>
      <SupportedOSPlatformVersion>10.0.22000.0</SupportedOSPlatformVersion>
      <PlatformTarget>x64</PlatformTarget>
      <Platforms>x64</Platforms>
      <WindowsPackageType>None</WindowsPackageType>
      <EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
    <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.230217.4" />
    <PackageReference Include="NUnit" Version="3.13.3" />
    <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
    <PackageReference Include="NUnit.Analyzers" Version="3.3.0" />
    <PackageReference Include="coverlet.collector" Version="3.1.2" />
  </ItemGroup>

</Project>

Does somebody has a hint?

Kind regards, Christof

Steps to reproduce the bug

1.) Create a nUnit Test project. 2.) Add the code from above. 3.) Add nuget package "Microsoft.WindowsAppSDK" 4.) Run the test.

Expected behavior

The test should be green. No exception expected.

Screenshots

No response

NuGet package version

None

Windows version

Windows 11 (21H2): Build 22000

Additional context

No response

sotanakamura commented 1 year ago

I cannot reproduce the bug in C++. No exception.

    MainWindow::MainWindow()
    {
        InitializeComponent();
        winrt::Windows::UI::Color color = Microsoft::UI::Colors::White();
    }
chrkon commented 1 year ago

Hi @sotanakamura, you are right. In the application this line will work. No Exception! But if this line is part of a unit test or if it is called from a unit test, the code will fail.

As a work around I have removed all Microsoft.UI.Colors.xxxx terms from our project. Instead I use static readonly fields created with Windows.UI.Color.FromARGB(aaa,rrr,ggg,bbb).

I think there is something missing in the Testproject, but I have no idea what is wrong.

Kind regards, Christof

sotanakamura commented 1 year ago

By the way, why do you assign microsoft namespace color to windows namespace color? Please test with following code and report the result;

Microsoft.UI.Color color = Microsoft.UI.Colors.White;
chrkon commented 1 year ago

Have you got tried this?

Microsoft UI Color

Microsoft.UI.Color doesn't exist. Also Windows.UI.Colors.xxx doesn't exist.

I can't use other color types from other namespaces, because I use Win2D in combination with WinUI3 to draw graphics in realtime. And other color types are not supported in the Win2D/WinUI3 combination.

sotanakamura commented 1 year ago

Sorry,it was my mistake.

sotanakamura commented 1 year ago

It seems that Microsoft.UI.Colors.White function works well when the application starts. This sample threw an exception.

int main()
{
    init_apartment();
    Windows::UI::Color color = Microsoft::UI::Colors::White();
}

When the application starts, no exception.

#include "pch.h"

using namespace winrt;
using namespace winrt::Microsoft::UI::Xaml;

int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
    init_apartment(apartment_type::single_threaded);
    Application::Start([](auto&&) {
        Application();
        Windows::UI::Color color = Microsoft::UI::Colors::White();
        Window window;
        window.Activate();
    });
}
chrkon commented 1 year ago

Hi @sotanakamura, thank you for investigating this problem. But I have no idea how to handle this. Is this a problem in .net, in WinUI, or is it a problem in nUnit?

Is it even a problem? Everything seems to work in the normal code. Only in the tests does this problem occur. What do you think?

Kind regards, Christof

sotanakamura commented 1 year ago

@gabbybilka Could you tell me where Microsoft.UI.Colors source code is? Or could you explain why calling Microsoft.UI.Colors static member functions before starting application throw a exception?

JaiganeshKumaran commented 1 year ago

I think what might be the problem. WinUI only registers the activation factories for these classes after Application.Start is called. For now, you can use System.Drawing.Color.White and cast it to a Microsoft.UI.Color, or simply construct the colour yourself (all fields zero for white).

chrkon commented 1 year ago

Hi @JaiganeshKumaran, thank you for your answer. My actual workaround is to set all colors manually with Windows.UI.Color.FromArgb(aaa,rrr,ggg,bbb).

Could you explain how to cast the System.Drawing.Color.White into a Microsoft.UI.Color type? The compiler complains if I try to do it.

grafik

Error: CS0234 The type or namespace name 'Color' does not exist in the namespace 'Microsoft.UI' (are you missing an assembly reference?)

chrkon commented 1 year ago

Hi everybody, today we had found the same issue with the Microsoft.UI.Text.FontWeights type.

Windows.UI.Text.FontWeight = Microsoft.UI.Text.FontWeights.Normal

This code line will work correctly in the application. But it will fail, if it is called from a unit test. We got the same error message as above with the color type:

System.TypeInitializationException : The type initializer for 'WinRT.ActivationFactory`1' threw an exception.
  ----> System.Runtime.InteropServices.COMException : Klasse nicht registriert (0x80040154 (REGDB_E_CLASSNOTREG))
   at WinRT.ActivationFactory`1.As(Guid iid)
   at Microsoft.UI.Text.FontWeights.Make___objRef_global__Microsoft_UI_Text_IFontWeightsStatics()
   at Microsoft.UI.Text.FontWeights.get__objRef_global__Microsoft_UI_Text_IFontWeightsStatics()
   at Microsoft.UI.Text.FontWeights.get_Normal()
   at GoRhythmCPS.Tests.Windows_UI_vs_Microsoft_UI_Test.FontWeightTest_MicrosoftWeights() in C:\Users\ckons\source\repos\Pulmokard\GoRythm-CPS\Application\GoRhythmCPS.Tests\Windows_UI_vs_Microsoft_UI_Test.cs:line 52
--COMException
   at WinRT.BaseActivationFactory..ctor(String typeNamespace, String typeFullName)
   at WinRT.ActivationFactory`1..ctor()
   at WinRT.ActivationFactory`1..cctor()

Again I created an own List of FontWeights as workaround. But it would be better if this issue could be fixed

Kind regards, Christof

JaiganeshKumaran commented 1 year ago

Hi @JaiganeshKumaran, thank you for your answer. My actual workaround is to set all colors manually with Windows.UI.Color.FromArgb(aaa,rrr,ggg,bbb).

Could you explain how to cast the System.Drawing.Color.White into a Microsoft.UI.Color type? The compiler complains if I try to do it.

grafik

Error: CS0234 The type or namespace name 'Color' does not exist in the namespace 'Microsoft.UI' (are you missing an assembly reference?)

Use Windows.UI.Color, not Microsoft.UI.Color. I don't think you can cast it like that, you probably need to construct it.

JaiganeshKumaran commented 1 year ago

For FontWeights.Normal, just write new FontWeight { Weight = 400 }. This value is even documented on the docs page.

chrkon commented 1 year ago

OK, I have replaced all

Windows.UI.Text.FontWeight = Microsoft.UI.Text.FontWeights.Normal

by

Windows.UI.Text.FontWeight = new Windows.UI.Text.FontWeight {Weight = 400};

in our project.

But both lines will work perfectly when the Application is running. Only in the unit Tests they behave different. This behavior should be corrected or it should be added to the documentation. It is a little bit confusing when Tests are red and the reason for this are some issues in the underlaying library.

chrkon commented 1 year ago

Dear @bpulliam , why this issue is set to CLOSED ???

I can agree with the change of Tag from "bug" to "question". But this issue is not solved for me, because I have not read an answer, which explains this behaviour. There is a workaround, but no explanation.

Kind regards, Christof

SoggyBottomBoy commented 3 months ago

This issue is not just related to colors or font weights but anything which gets registered after Application.Start. Fixing colors/font weights may seem easy enough but other things get more and more difficult. Is there any way to trigger all WinUI app dependences to get registered for a test project so that we don't have to find hacks to get around this?

chrkon commented 3 months ago

Today I had a strange experience with my "Workaround" Code mentioned above. I have the following Extension Method in my Project:

public static class ColorExtensions
{
    public static Color ToWindowsUIColor(this System.Windows.Media.Color color)
    {
        var A = color.A;
        var R = color.R;
        var G = color.G;
        var B = color.B;
        return Color.FromArgb(A, R, G, B);
    }
}

For this extension method I have this simple Unit Test:

 [Test]
 public void ColorTest_SystemWindowsMedia()
 {
     var color = System.Windows.Media.Colors.White.ToWindowsUIColor();
     Assert.That(color, Is.EqualTo(Color.FromArgb(255, 255, 255, 255)));
 }

This test works fine if I call it with the dotnet test <projectfile> command.

But it fails if I run this directly with the Nunit3 Runner. It throws this Error:

 System.TypeInitializationException : The type initializer for '<Module>' threw an exception.
   ----> System.TypeLoadException : Could not load type 'System.Windows.Media.DisableDpiAwarenessAttribute' from assembly 'WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.
    at GoRhythmCPS.App_Tests.WindowsUI_vs_MicrosoftUI_Test.ColorTest_SystemWindowsMedia()
    at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
    at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
 --TypeLoadException
    at ModuleInitializer.IsProcessDpiAware()
    at .cctor()

I don't understand why the ".DisableDpiAwarenessAttribute" attribute is needed for the colors.

Of course all dependencies are fulfilled, because i run the test in the output folder of the project. And the App is working fine. Only the Test will fail. And this only if the Nunit3 runner is used.

SoggyBottomBoy commented 3 months ago

@bpulliam can this issue please be re-opened.

I can't find any documentation on how to ensure WinRT types are correctly activated for unit test. I am using Xunit and need a way to register WinRT types as the solutions proposed in this issue may be simple enough for Color of FontWeight but does not scale to other types.

https://github.com/xunit/xunit/issues/2982