microsoft / CsWinRT

C# language projection for the Windows Runtime
MIT License
546 stars 104 forks source link

Known WinRT.SourceGenerator and CsWinRT issues #1809

Open manodasanW opened 2 weeks ago

manodasanW commented 2 weeks ago

There are 2 known issues related to the source generator included in the Windows SDK projection package referenced when using the Windows OS version target framework in the upcoming .NET SDK release for October.

Partial type nested within non-partial type

If you have a type marked partial implementing WinRT mapped interfaces nested within a type that isn't marked partial, you may see the below compiler error instead of a diagnostic message with a code fix indicating that the outer type needs to be made partial.

CS0260 Missing partial modifier on declaration of type '..'; another partial declaration of this type exists

To address this, you can either make the outer type partial to allow the generated source to compile or use the WindowSdkPackageVersion property to explicitly reference one of the versions with the fix that makes it a diagnostic message until it is available in the .NET SDK.

Special characters in assembly name (dashes)

If you have certain special characters in your assembly name, specifically a - and you have WinRT generic instantiation scenarios, you may notice the generated code doesn't compile due to the source generator fails to escape all possible special characters not allowed in identifiers.

The errors will be from one of these files and will be something like the below:

WinRT.SourceGenerator\Generator.WinRTAotSourceGenerator\WinRTGenericInstantiation.g.cs
WinRT.SourceGenerator\Generator.WinRTAotSourceGenerator\WinRTGlobalVtableLookup.g.cs

error CS0116: A namespace cannot directly contain members such as fields, methods or statements
error CS1514: { expected
error CS1022: Type or namespace definition, or end-of-file expected
error CS0118: 'WinRT.Text' is a namespace but is used like a variable
error CS0103: The name 'GenericHelpers' does not exist in the current context

To address this, you can use the WindowSdkPackageVersion property to explicitly reference one of the versions with the fix that properly escapes the assembly name when using it in generated code.

Solution

Both issues are fixed in the below Windows SDK projection package which will be ingested in the next .NET SDK update. Until then, a project can set the WindowsSdkPackageVersion property in their csproj, in a common props file, or in Directory.Build.props to explicitly reference that version. Replace xxxxx in the property with the OS version in your .NET target framework.

If your project targets .NET 6:

<WindowsSdkPackageVersion>10.0.xxxxx.46</WindowsSdkPackageVersion>

https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.26100.46 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.22621.46 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.22000.46 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.20348.46 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.19041.46 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.18362.46 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.17763.46

If your project targets .NET 8 or later:

<WindowsSdkPackageVersion>10.0.xxxxx.47</WindowsSdkPackageVersion>

https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.26100.47 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.22621.47 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.22000.47 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.20348.47 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.19041.47 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.18362.47 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.17763.47

If your project targets .NET 8 or later and needs UWP support:

<WindowsSdkPackageVersion>10.0.xxxxx.48</WindowsSdkPackageVersion>

https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.26100.48 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.22621.48 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.22000.48 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.20348.48 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.19041.48 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.18362.48 https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref/10.0.17763.48

If you run into any other errors from code generated by WinRT.SourceGenerator, please open an issue in the CsWinRT repo.

Type redirection

If you run into type conflict errors between Microsoft.WinUI and Microsoft.Windows.SDK.NET.dll, see the CsWinRT 2.1.1 release notes for details about the forwarded types for WinAppSDK 1.6.

Obsolete errors

If you run into the error below indicating that ObjectReferenceWrapperAttribute has been obsolete, you can update to the latest CsWinRT version (2.1.x) to generate your projection which doesn't rely on it. If you are unable to move to the latest CsWinRT version, you can choose to fix your version of the Windows SDK projection to an older version before CsWinRT 2.1.x such as the .34 version by specifying the WindowsSdkPackageVersion property in your csproj or in a common props file until you are able to update.

'ObjectReferenceWrapperAttribute is obsolete.' This attribute is only used for the .NET Standard 2.0 projections.
<WindowsSdkPackageVersion>10.0.xxxxx.34</WindowsSdkPackageVersion>

** replace xxxxx with the OS version that your target in your target framework.

AdriaanLarcai commented 2 weeks ago

I have tried these suggestions with a Windows App SDK 1.6 project using 10.0.xxxxx.47. But I'm still seeing this exception during build time in WinRTGenericInstantiation.g.cs:

error CS0116: A namespace cannot directly contain members such as fields, methods or statements

As well as an additional exception:

The type '<invalid-global-code>' already contains a definition for '_initialized'

manodasanW commented 2 weeks ago

@AdriaanLarcai Can you set the property EmitCompilerGeneratedFiles in your csproj and then rebuild. You should then be able to find the generated files under the obj folder under like the generated folder and from there can you share here the snippet of the class that is leading to the compiler error? Based on <invalid-global-code>, I feel like there is a scenario we are not detecting properly.

AdriaanLarcai commented 1 week ago

@manodasanW I finally got a chance to do this for you.

So it looks like when the generator is generating for an IEnumerable of nullable double values (nullable is disabled in the project), it tries to generate a class that looks like this:

  internal static class IEnumerable_double?
{
    private static readonly bool _initialized = Init();
    internal static bool Initialized => _initialized;

    private static unsafe bool Init()
    {
        return global::ABI.System.Collections.Generic.IEnumerableMethods < double ?, ABI.double?>.InitCcw(
           &Do_Abi_First_0
        );
    }

    [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
    private static unsafe int Do_Abi_First_0(IntPtr thisPtr, IntPtr* __return_value__)
    {
        *__return_value__ = default;
        try
        {
            *__return_value__ = MarshalInterface<global::System.Collections.Generic.IEnumerator<double?>>.
               FromManaged(global::ABI.System.Collections.Generic.IEnumerableMethods<double?>.Abi_First_0(thisPtr));
        }
        catch (global::System.Exception __exception__)
        {
            global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
            return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
        }
        return 0;
    }
}

Obviously the IEnumerable_double? is not a valid class name.

The same with IEnumerable with nullable int, etc.

internal static class IEnumerable_int?
{
    private static readonly bool _initialized = Init();
internal static bool Initialized => _initialized;

private static unsafe bool Init()
{
    return global::ABI.System.Collections.Generic.IEnumerableMethods < int ?, ABI.int?>.InitCcw(
       &Do_Abi_First_0
    );
}

[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
private static unsafe int Do_Abi_First_0(IntPtr thisPtr, IntPtr* __return_value__)
{
    *__return_value__ = default;
    try
    {
        *__return_value__ = MarshalInterface<global::System.Collections.Generic.IEnumerator<int?>>.
           FromManaged(global::ABI.System.Collections.Generic.IEnumerableMethods<int?>.Abi_First_0(thisPtr));
    }
    catch (global::System.Exception __exception__)
    {
        global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
        return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
    }
    return 0;
}
}

Hope this helps.

xkromkad commented 1 week ago

I've encountered similar issues with special characters in assembly names and found a helpful workaround. Replacing dashes and spaces in the assembly name and default namespace using the following:


<AssemblyName>$(MSBuildProjectName.Replace("-", "_"))</AssemblyName>
<RootNamespace>$(MSBuildProjectName.Replace(" ", "_").Replace("-", "_"))</RootNamespace>
Osirisoo0O commented 1 week ago

ApplicationTrigger is still unavailable. I hope it can be repaired as soon as possible..

bh-sijtnic commented 6 days ago

We started seeing this issue after updating to the latest .NET 6 and .NET 8 SDKs yesterday, both our NET 6 and NET 8 products are affected and are unable to compile with the latest SDK versions

I had already logged this issue before i found this topic https://github.com/dotnet/sdk/issues/44145

For now we work around the issue by specifying this version

10.0.19041.34
paulpv commented 4 days ago

Posting this to hopefully help others that start seeing this.

I am not certain of the OP issue, but on a relatively simple personal hobby project I am working on I started to see these error when compiling:

Type Code Description File Line
Error CS0116 A namespace cannot directly contain members such as fields, methods or statements obj\Debug\net8.0-windows10.0.26100.0\WinRT.SourceGenerator\Generator.WinRTAotSourceGenerator\WinRTGlobalVtableLookup.g.cs 5
Error CS1022 Type or namespace definition, or end-of-file expected obj\Debug\net8.0-windows10.0.26100.0\WinRT.SourceGenerator\Generator.WinRTAotSourceGenerator\WinRTGlobalVtableLookup.g.cs 6
Error CS1514 { expected obj\Debug\net8.0-windows10.0.26100.0\WinRT.SourceGenerator\Generator.WinRTAotSourceGenerator\WinRTGlobalVtableLookup.g.cs 5

My C# WinForms app and libs do not explicitly use WinRT (they do pinvoke some user32.dll calls).

I found https://github.com/dotnet/sdk/issues/44026 and then https://github.com/microsoft/CsWinRT/issues/1814 and now this issue.

I edited my .csproj file to add CsWinRTAotOptimizerEnabled as follows:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows10.0.26100.0</TargetFramework>
    ...
    <CsWinRTAotOptimizerEnabled>false</CsWinRTAotOptimizerEnabled>
  </PropertyGroup>
...

The problem went away.

I changed CsWinRTAotOptimizerEnabled to true.

The problem came back.

I will be leaving CsWinRTAotOptimizerEnabled set to false.