microsoft / CsWinRT

C# language projection for the Windows Runtime
MIT License
534 stars 102 forks source link

Generated source for embedded mode uses public on contract types. #1668

Open PDeets opened 2 weeks ago

PDeets commented 2 weeks ago

Describe the bug I'm using embedded mode to call a WinRT API where I have a NuGet with the winmd, but not an interop assembly. Everything builds OK, but the generated source uses "public" for contract types. For example, in Windows.Foundation.cs, I see this:

[global::Windows.Foundation.Metadata.ContractVersion(262144u)]
public enum FoundationContract
{
}

The issue is in the write_contract function in src/cswinrt/code_writers.h. The other write_* functions use the internal_accessibility to pick between "internal" and "public", but write_contract just uses "public" all the time.

This makes it so if I have two assemblies both using C#/WinRT embedded mode and one depends on the other, there are build warnings about FoundationContract being defined in two places.

To Reproduce

  1. Use C#/WinRT's embedded mode while including Windows.Foundation.

    <LangVersion>9</LangVersion>
    <CsWinRTEmbedded>true</CsWinRTEmbedded>
    <CsWinRTIncludes>
        Windows.Foundation;
    </CsWinRTIncludes>
    <CsWinRTExcludes>
        Windows.Foundation.Diagnostics;
        Windows.Foundation.PropertyType;
    </CsWinRTExcludes>
    <CsWinRTWindowsMetadata>10.0.19041.0</CsWinRTWindowsMetadata>

Expected behavior No WinRT types are exposed as public from the generated assembly.

Actual Behavior Windows.Foundation.FoundationContract is exposed as public from the generated assembly.

Version Info I'm using version 2.0.7 of the Microsoft.Windows.CsWinRT NuGet.

PDeets commented 2 weeks ago

I worked around the issue by adding this to my project:

<Target Name="FixPublicContractTypes" AfterTargets="CsWinRTGenerateProjection">
    <!-- Workaround for https://github.com/microsoft/CsWinRT/issues/1668. Windows.Foundation.cs is generated
         with two public enums that should be internal. This target switches them to be internal. -->
    <PropertyGroup>
        <FileToFix>$(CsWinRTGeneratedFilesDir)Windows.Foundation.cs</FileToFix>
    </PropertyGroup>

    <WriteLinesToFile File="$(FileToFix)" Lines="$([System.IO.File]::ReadAllText($(FileToFix)).Replace('public enum', 'internal enum'))" Overwrite="true" Encoding="Unicode" />
</Target>