KEMBAR78
Use embedded CsWinRT by JohnMcPMS · Pull Request #5178 · microsoft/winget-cli · GitHub
Skip to content

Conversation

JohnMcPMS
Copy link
Member

@JohnMcPMS JohnMcPMS commented Feb 4, 2025

Issue

Updating the version of CsWinRT that we use lead to a problem with conflicting WinRT.Runtime assemblies (microsoft/winget-dsc#140). While that issue was mitigated by delisting the offending version, in order to ship any release with the newer version of CsWinRT, we need to embed the WinRT.Runtime in our assemblies.

Change

Move to use embedded CsWinRT, specifically in Microsoft.WinGet.Client.Engine and Microsoft.Management.Configuration.Processor. The dependent projects are updated to no longer reference CsWinRT, but requires some special handling to prevent errors.

Since the configuration code was leveraging a projection assembly, it is being replaced by the processor. The projected types are shared out to those dependent binaries via InternalsVisibleTo.

Also fixes an annoying behavior where an exception escaping from a command execution would prevent the --logs option from opening the log location (error cases being more likely to be the time one would want the logs of course).

Validation

Minor local validation that things work. Build regression tests will be the true test.

Microsoft Reviewers: Open in CodeFlow

@JohnMcPMS JohnMcPMS requested a review from a team as a code owner February 4, 2025 02:20
Windows.Data;
Windows.Devices;
Windows.Foundation;
Windows.Gaming;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity - why do we need Windows.Gaming?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears to be in the closure of things we need. The list was generated by iteratively building and adding namespaces until the unrecognized namespace/type errors stopped. It is possible that a more targeted set of types could be determined to prevent quite so much from being generated.

I will give that a try to see if it isn't too painful, as it will reduce build time and potentially allow us to not need to set up trimming.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, very painful.

I did however try removing just Windows.Gaming and found that it was Windows.UI.Input.Preview.Injection that was pulling it in. Since we can selectively exclude things, this pruning seems like a more sane way to get rid of some of the excess.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, 25% reduction isn't too bad for just trimming off one small portion. Thanks!!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the same optimization apply to Engine, or is that one more painful?

florelis
florelis previously approved these changes Feb 4, 2025
Windows.Management;
Windows.Media;
Windows.Networking;
Windows.Perception;
Copy link
Member

@florelis florelis Feb 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the same line as @Trenly's comment, could we do without the Windows.Perception namespace, and maybe Windows.Graphics?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In terms of size reduction, I am assuming that generated code file size translates linearly to resulting binary size. The Xaml file(s) were drastically larger than any other, so its removal resulted in the largest drop. The remaining namespaces are all a more gradual size gradient. The largest ones would result in ~500 KB each if linear (current size is ~24 MB, which is already a size savings over the SDK assembly we get to leave out).

I tried removing some of them, but it takes around a minute to iterate and there are some things in each of these namespaces that are required. The best thing someone could do would be to write code to analyze the SDK use of a non-embedded build, then output the list of types needed. Presumably a huge list of types would be faster and a perfect pruning for output size.

Comment on lines 24 to 28
#if WinGetCsWinRTEmbedded
internal sealed class PowerShellConfigurationSetProcessorFactory : IConfigurationSetProcessorFactory, IPowerShellConfigurationProcessorFactoryProperties, IPwshConfigurationSetProcessorFactoryProperties
#else
public sealed class PowerShellConfigurationSetProcessorFactory : IConfigurationSetProcessorFactory, IPowerShellConfigurationProcessorFactoryProperties, IPwshConfigurationSetProcessorFactoryProperties
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This may be worse but if the access modifier is the only thing that changes, could we have only that be conditional? Not sure if C# would allow it

#if WinGetCsWinRTEmbedded
    internal
#else
    public
#endif
         sealed class PowerShellConfigurationSetProcessorFactory : IConfigurationSetProcessorFactory, IPowerShellConfigurationProcessorFactoryProperties, IPwshConfigurationSetProcessorFactoryProperties

<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.1.6" />
</ItemGroup>

<Import Project="..\..\targets\ReferenceEmbeddedCsWinRTProject.targets" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume none of these are affected when the Processor is not using Embedded?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only use case for that build is the nuget; these projects are not intended to ever ship with it after the merge.

@github-actions

This comment was marked as outdated.

@JohnMcPMS
Copy link
Member Author

JohnMcPMS commented Feb 4, 2025

@Trenly and @florelis

I took another run at building up the types required and was able to get the output down to 2.7 MB (and build time to a few seconds rather than a minute. Some lessons:

  1. Just include all of Windows.Foundation to start
  2. The inclusion filtering seems to require that the type be longer than the string given, so you have to cut off at least one character to get a specific type
  3. This will definitely be a fragile list in the event of new code or a CsWinRT version update

But given the massive build time and binary size savings it seems to be worth the effort.

[Edit] Also, this and other measures may be required as the last build failed due to running out of disk space :(

Windows.Perception;
Windows.Security;
Windows.Services;
Windows.Globalization.DayOfWee;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this. It sounds so festive to me. "Day of weeeeee~"
But brits may disagree with my interpretation 😆

</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)' == '$(DesktopFramework)'">
<LangVersion>10</LangVersion>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the move from global to DesktopFramework only intentional?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as the embedded code requires a higher version.

@JohnMcPMS JohnMcPMS merged commit 586bcbc into microsoft:master Feb 5, 2025
9 checks passed
@JohnMcPMS JohnMcPMS deleted the embed-everything branch February 5, 2025 19:39
JohnMcPMS added a commit that referenced this pull request Feb 5, 2025
## Issue
Updating the version of CsWinRT that we use lead to a problem with
conflicting WinRT.Runtime assemblies (microsoft/winget-dsc#140). While
that issue was mitigated by delisting the offending version, in order to
ship any release with the newer version of CsWinRT, we need to embed the
WinRT.Runtime in our assemblies.

## Change
Move to use embedded CsWinRT, specifically in
`Microsoft.WinGet.Client.Engine` and
`Microsoft.Management.Configuration.Processor`. The dependent projects
are updated to no longer reference CsWinRT, but requires some special
handling to prevent errors.

Since the configuration code was leveraging a projection assembly, it is
being replaced by the processor. The projected types are shared out to
those dependent binaries via `InternalsVisibleTo`.

Also fixes an annoying behavior where an exception escaping from a
command execution would prevent the `--logs` option from opening the log
location (error cases being more likely to be the time one would want
the logs of course).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants