Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Size of WinForms with PublishAot #9911

Open
Tracked by #4649
MichalStrehovsky opened this issue Sep 14, 2023 · 47 comments
Open
Tracked by #4649

Size of WinForms with PublishAot #9911

MichalStrehovsky opened this issue Sep 14, 2023 · 47 comments

Comments

@MichalStrehovsky
Copy link
Member

I had a look at the size of WinForms app with PublishAot. I think there's a potential to make the size of AOT-compiled self-contained WinForms apps very attractive (single-digit MB range, fully self-contained).

If I add <PublishAot>true</PublishAot> and <_SuppressWinFormsTrimError>true</_SuppressWinFormsTrimError> to a WinForms default template and dotnet publish it I get a 54 MB executable. One might say “still better than Electron” and call it good, but it can actually be much better.

Drilling into the size with Sizoscope one can immediately see something that should not be there: PresentationFramework.

image

Half of WPF gets dragged in because WinForms started using a WPF interface: ICommand. And ICommand has a bunch of custom attributes on it that drag in WPF.

We can avoid dragging in WPF if we can make sure the string within the custom attribute doesn't actually resolve. One can hack around it by putting this in the csproj for example:

  <Target Name="RemoveWPFReference" BeforeTargets="WriteIlcRspFileForCompilation">
    <ItemGroup>
      <IlcReference Remove="@(IlcReference)" Condition="'%(Filename)' == 'PresentationFramework'" />
    </ItemGroup>
  </Target>

We cannot ship this hack. The only way I can currently think of to fix this would be to divorce WPF and WinForms and stop putting them into the same NuGet/SDK.

With this out of the way, the size of the executable drops to 25.5 MB. We can do better. One thing that Sizoscope will point out is networking stack. It gets brought in from two places – PictureBox and XML. We already have feature switches in place for both PictureBox and XML.
Add this to the csproj to disable these:

<PropertyGroup>
  <XmlResolverIsNetworkingEnabledByDefault>false</XmlResolverIsNetworkingEnabledByDefault>
</PropertyGroup>
  <ItemGroup>
  <!-- We really should introduce a first class property for this -->
    <RuntimeHostConfigurationOption Include="System.Windows.Forms.PictureBox.UseWebRequest" Value="false" Trim="true" />
  </ItemGroup>

With this, the size drops to 21.7 MB.

Looking at what's left in Sizoscope, designers stand out. There's many types that come out of DesignerAttribute and EditorAttribute. Can we make a feature switch to strip them? I experimentally stripped them out and the size drops to 15.2 MB. (Tracked in dotnet/runtime#92043)

A thing that Sizoscope currently doesn't show is manifest resources. Out of the 15.2 MB above, 7.5 MB are embedded resources. They currently cannot be trimmed automatically. Clicking through the manifest resources in WinForms assemblies that are part of the application based on Sizoscope, I see that most (all?) are designer related. Can we get rid of them? If so, the size would drop to ~7 MB.

I think there would be potential to shrink this further (e.g. the TypeConverter on TableLayoutSetting+StyleConverter brings in the entire XML stack with DtdParser and everything), but 7 MB for a fully self-contained WinForms app is already quite encouraging.

@hamarb123
Copy link
Contributor

Also worth noting that including PresentationCore (see here), causes a visible behaviour change, since the DPI to be forced to SystemAware for all WinForms apps which don't declare it in the manifest, even if they set it in Main to something else, if they don't declare [DisableDpiAwareness] (this happens since the module initialiser runs earlier on NAOT, this sort of stuff shouldn't even be in a module initialiser imo). This is not ideal, so we should avoid including Wpf for any WinForms apps, that don't actually use Wpf.

@elachlan
Copy link
Contributor

@JeremyKuhne and @lonitra are the wizards focusing on Winforms AOT for .NET9

Is the issue with the design attributes because they live in the design assembly? So it pulls in the whole thing?

@JeremyKuhne JeremyKuhne self-assigned this Sep 14, 2023
@JeremyKuhne JeremyKuhne added this to the .NET 9.0 milestone Sep 14, 2023
@JeremyKuhne
Copy link
Member

Thanks for the details @MichalStrehovsky!

ICommand has a bunch of custom attributes on it that drag in WPF.

I had no idea the strings would resolve during compilation. Do you know precisely how this happens? Maybe we could get a feature that is more granular that would allow us to ignore specific usages wherever this is being figured out?

Note that we're currently focused on getting the key COM pieces converted to ComWrappers so that Accessibility and OLE (clipboard/dragdrop in particular) will work in this scenario. All and any suggestions and help in the AOT/Trimming space are very much appreciated.

@lonitra

@MichalStrehovsky
Copy link
Member Author

I had no idea the strings would resolve during compilation. Do you know precisely how this happens? Maybe we could get a feature that is more granular that would allow us to ignore specific usages wherever this is being figured out?

The reason why this happens is that the attributes have an annotation instructing trimming to keep things on the types:

[TypeConverter("System.Windows.Input.CommandConverter, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, Custom=null")]
public interface ICommand
{
}

public sealed class TypeConverterAttribute : Attribute
{
    public TypeConverterAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] string typeName) { }
}

The annotation on the TypeConverterAttribute constructor instructs trimming to resolve the string and keep the constructor. The constructor brings a massive closure of WPF with it.

We could ask trimming to drop all TypeConverters, but I assume WinForms actually use type converter and do require this trimming behavior. It just might not be desirable here.

Cc @vitek-karas @sbomer for ideas

@MichalStrehovsky
Copy link
Member Author

Is the issue with the design attributes because they live in the design assembly? So it pulls in the whole thing?

We don't pull in whole assemblies (unless something forces it, like setting <TrimMode>partial</TrimMode> in the project, or something like that). The designers get pulled in for each individual control that has designer attributes on it.

The Sizoscope tool is really good at showing the paths to roots for these, try it - it's a WinForms app.

@vitek-karas
Copy link
Member

For the designer related attributes: If we can make an assumption that these attributes are not useful when running the app, then there are ways to remove all of those attributes (and what they depend on along with them).
Similarly for the resources - if there are embedded resources which are there solely for designers and we can say with confidence that they're not going to be needed by the app, we can also remove them. It's just that currently none of this is automatic because the trimmer tools are not smart enough to figure out if these things are actually used by the app or not (for some of these it's really difficult to do even in theory).

For the type converter: I can't think of a way to solve this super cleanly. There is a direct dependency all the way to all of the WPF functionality. Typically, the only way we can break such a chain is through feature switches - similar to what Michal used to get rid of the networking stack. But that depends on usability - if the code dependency is there, but in reality almost no WinForms app actually runs that code, then maybe a feature switch would be a solution.

I fear TypeConverter as I know that it's used a lot in WinForms - and it's not very trimming friendly.

@elachlan
Copy link
Contributor

There are some apps that use the designers at runtime. So it would have to be behind a switch, unless the team decided that designers are not supported in AOT scenarios.

Could CommandConverter live in a different assembly that would avoid PresentationFramework? I know its a breaking change, but it seems like structuring dependencies properly helps with trimming? Please correct me if I am wrong, I am still learning AOT/Trimming.

@MichalStrehovsky
Copy link
Member Author

Could CommandConverter live in a different assembly that would avoid PresentationFramework?

It's not about what assembly CommandConverter lives in. It's about what it's constructor depends on.

Clicking through the dependency graph in Sizoscope might help building intuition around this. Here I double clicked why the Grid constructor (WPF Grid) is included in an empty WinForms app template.

It doesn't even fit in a single screen so this is just a part.

But basically CommandConverter implements ConvertFrom virtual method and since that one is called, we need ConvertFromHelper, that calls GetKnownCommand,... then suddenly we need XamlReader and that has built in support for a bunch of controls.

image

@KlausLoeffelmann
Copy link
Member

KlausLoeffelmann commented Sep 16, 2023

ICommand doesn't live in presentationframework, and we're not using presentationframework currently from the runtime in the context of DataBinding.

image

But yes, the attributes are there, but we're actually not using them in the runtime context (and also not in the Designer context, because those would not work, anyway.)

But apart from that: We will be enabling the ElemenHost at one point, and then at the latest, we would need to have the reference to presentationframework, anyway.

@KlausLoeffelmann
Copy link
Member

I am wondering, how the other UI stacks do that. I mean, the whole ViewModel concept is based on ICommand, and they all use that ICommand, don't they? ICommand is even .NET Standard 1.0 compatible, is it not?

image

@MichalStrehovsky
Copy link
Member Author

I am wondering, how the other UI stacks do that

The custom attribute is harmless if the project being built doesn't provide a way to resolve the PresentationFramework assembly. WinForms is inflicting it on itself by being packaged together with WPF. Granted it would be better if the ICommand interface wasn't annotated in a way that violated layering/separation of concerns but I don't know if anything can be done about that

@elachlan
Copy link
Contributor

If we look at it from the other direction, WPF would require these attributes so that trimming/AOT works for them right? So we can't remove the annotations without breaking AOT for them?

@MichalStrehovsky
Copy link
Member Author

If we look at it from the other direction, WPF would require these attributes so that trimming/AOT works for them right? So we can't remove the annotations without breaking AOT for them?

The attribute is in the official docs: https://learn.microsoft.com/en-us/dotnet/api/system.windows.input.icommand?view=net-7.0. The official docs make it seem this is a WPF-specific interface with some special behavior in WinRT with no mention of any other use for this interface.

@elachlan
Copy link
Contributor

The dependency on it was added in #4895 to enable modernized model binding. No work was done to decouple it from PresentationFramework, which wouldn't have been obvious at the time. I imagine they would need to be moved to a different assembly and removes the dependencies on WPF. Or WPF writes its own ICommand wrapper and uses that internally with the TypeConverter?

@RussKie
Copy link
Member

RussKie commented Sep 19, 2023

I distinctly remember chatting with @KlausLoeffelmann mentioning that we're bringing the WPF stuff in... :)

WRT: WPFt-rimming - why not make an assumption here - if no <UseWpf>true</UseWpf> present, then purge it all away?

WRT: attributes/typeconverter trimming - the PropertyGrid makes heavy use of those, so care has to be taken here.

@RussKie
Copy link
Member

RussKie commented Sep 19, 2023

/cc: @kant2002

@KlausLoeffelmann
Copy link
Member

WRT: WPFt-rimming - why not make an assumption here - if no true present, then purge it all away?

That sounds reasonable. As soon as someone drags ElementHost into the game, we need to change the vbproj (SCNR) and enable <UseWpf>true</UseWpf>. Should work in those cases!

@JeremyKuhne
Copy link
Member

@vitek-karas How difficult would it be to add configuration to ignore resolution of specified types? Say, if we indicated that we don't want ICommand parsed?

@MichalStrehovsky
Copy link
Member Author

@vitek-karas How difficult would it be to add configuration to ignore resolution of specified types? Say, if we indicated that we don't want ICommand parsed?

Can we fix the Sdk to not pass the WPF assembly references if UseWpf is not specified? This would fix the problem too and work exactly the same between publish and F5 debug.

Trimming flags that cause differences in behavior between F5 debug and a published app (without generating a warning about it at publish time) are typically reserved as last ditch effort when nothing else can work. Users don't like when that happens.

@MichalStrehovsky
Copy link
Member Author

Can we fix the Sdk to not pass the WPF assembly references if UseWpf is not specified?

This could likely also fix another issue I didn't mention: an empty WinForms app published with PublishAot also dumps 5 native DLLs into publish output (D3DCompiler_47_cor3.dll, PenImc_cor3.dll, PresentationNative_cor3.dll, vcruntime140_cor3.dll, wpfgfx_cor3.dll). I assume these are all WPF. They are doubling the size of a NativeAOT publish (provided we can fix all the issues discussed in top-post) - while the WinForms app can be 7 MB in size in theory, all of these together cost another 8 MB.

@elachlan
Copy link
Contributor

So there are winforms changes and SDK changes required here? Do we have an SDK tracking issue?

@MichalStrehovsky
Copy link
Member Author

So there are winforms changes and SDK changes required here? Do we have an SDK tracking issue?

I didn't file one because the SDK repo backlog is filled with untriaged issues from years ago that nobody is looking at. In my experience the way to get work done in the SDK repo is to submit a pull request. The actual fix might also be in https://github.com/dotnet/windowsdesktop - I'm not quite sure what component decides the assembly references and native files we need.

@AustinWise
Copy link
Contributor

I was looking into this and it looks like there is already a mechanism for targeting packs to only reference a subset of the WindowsDesktop files depending on the values of UseWPF and UseWindowsForms . Perhaps something similar could be done for the runtime packs.

Existing mechanism for targeting packs

In the .NET 8 RC2 SDK's Sdks/Microsoft.NET.Sdk.WindowsDesktop/targets/Microsoft.NET.Sdk.WindowsDesktop.props file there is:

    <FrameworkReference Include="Microsoft.WindowsDesktop.App" IsImplicitlyDefined="true"
                        Condition="('$(UseWPF)' == 'true') And ('$(UseWindowsForms)' == 'true')"/>

    <FrameworkReference Include="Microsoft.WindowsDesktop.App.WPF" IsImplicitlyDefined="true"
                        Condition="('$(UseWPF)' == 'true') And ('$(UseWindowsForms)' != 'true')"/>

    <FrameworkReference Include="Microsoft.WindowsDesktop.App.WindowsForms" IsImplicitlyDefined="true"
                        Condition="('$(UseWPF)' != 'true') And ('$(UseWindowsForms)' == 'true')"/>

So there are different ReferenceReferences depending on what combination of UseWPF and UseWindowsForms properties are set.

In the .NET 8 RC2 SDK's Microsoft.NETCoreSdk.BundledVersions.props file you can see that these difference KnownFrameworkReference share the same targeting packages and runtime packs, but have different settings for Profile:

    <KnownFrameworkReference Include="Microsoft.WindowsDesktop.App"
                              TargetFramework="net8.0"
                              RuntimeFrameworkName="Microsoft.WindowsDesktop.App"
                              DefaultRuntimeFrameworkVersion="8.0.0-rc.2.23479.10"
                              LatestRuntimeFrameworkVersion="8.0.0-rc.2.23479.10"
                              TargetingPackName="Microsoft.WindowsDesktop.App.Ref"
                              TargetingPackVersion="8.0.0-rc.2.23479.10"
                              RuntimePackNamePatterns="Microsoft.WindowsDesktop.App.Runtime.**RID**"
                              RuntimePackRuntimeIdentifiers="win-x64;win-x86;win-arm64"
                              IsWindowsOnly="true"
                              />

    <KnownFrameworkReference Include="Microsoft.WindowsDesktop.App.WPF"
                              TargetFramework="net8.0"
                              RuntimeFrameworkName="Microsoft.WindowsDesktop.App"
                              DefaultRuntimeFrameworkVersion="8.0.0-rc.2.23479.10"
                              LatestRuntimeFrameworkVersion="8.0.0-rc.2.23479.10"
                              TargetingPackName="Microsoft.WindowsDesktop.App.Ref"
                              TargetingPackVersion="8.0.0-rc.2.23479.10"
                              RuntimePackNamePatterns="Microsoft.WindowsDesktop.App.Runtime.**RID**"
                              RuntimePackRuntimeIdentifiers="win-x64;win-x86;win-arm64"
                              IsWindowsOnly="true"
                              Profile="WPF"
                              />

    <KnownFrameworkReference Include="Microsoft.WindowsDesktop.App.WindowsForms"
                              TargetFramework="net8.0"
                              RuntimeFrameworkName="Microsoft.WindowsDesktop.App"
                              DefaultRuntimeFrameworkVersion="8.0.0-rc.2.23479.10"
                              LatestRuntimeFrameworkVersion="8.0.0-rc.2.23479.10"
                              TargetingPackName="Microsoft.WindowsDesktop.App.Ref"
                              TargetingPackVersion="8.0.0-rc.2.23479.10"
                              RuntimePackNamePatterns="Microsoft.WindowsDesktop.App.Runtime.**RID**"
                              RuntimePackRuntimeIdentifiers="win-x64;win-x86;win-arm64"
                              IsWindowsOnly="true"
                              Profile="WindowsForms"
                              />

In the targeting pack's data/FrameworkList.xml file, the <File /> tags have a Profile attribute. The ResolveTargetingPack task considers the profile when adding references to assemblies.

Idea for runtime pack

The runtime pack Nuget package has a file called data/RuntimeList.xml. If the <File /> element had a Profile attribute one them like the ones in the targeting pack's FrameworkList.xml file, the ResolveRuntimePack task could be changed to consider the Profile attribute when choosing which files to publish.

To add the Profile element to the RuntimeList.xml file, I think you need to add FrameworkListFileClass elements to https://github.com/dotnet/windowsdesktop/blob/main/src/windowsdesktop/src/sfx/Microsoft.WindowsDesktop.App.Runtime.sfxproj . I believe this is how the targeting pack sets them in https://github.com/dotnet/windowsdesktop/blob/main/src/windowsdesktop/src/sfx/Microsoft.WindowsDesktop.App.Ref.sfxproj .

@elachlan
Copy link
Contributor

Similar issue raised in 2020: #3723

@MichalStrehovsky
Copy link
Member Author

A thing that Sizoscope currently doesn't show is manifest resources. Out of the 15.2 MB above, 7.5 MB are embedded resources.

Just an update that when using .NET 9 and latest Sizoscope, manifest resources now do show up so this can now be visualized properly.

image

@elachlan
Copy link
Contributor

elachlan commented Dec 1, 2023

@MichalStrehovsky any standouts? I think we have quite a few icons and even more string localizations.

@MichalStrehovsky
Copy link
Member Author

quite a few icons and even more string localizations.

Yes, that :).

You can crossreference what Sizoscope reports with ILSpy on the original managed assembly (Sizoscope doesn't see the contents).

@LakshanF
Copy link
Contributor

LakshanF commented Feb 2, 2024

TrimTest project, which we are tracking as part of WinForms trimming, gives a similar view as above from sizoscope.

@NCLnclNCL
Copy link

.

@hez2010
Copy link

hez2010 commented May 11, 2024

3a11734ba30d5a161b3f966c4586e3c5

With the latest nightly build, the size of nativeaot winforms app comes to 19mb.

Note that the published winforms app comes with unnecessary native dependency dll from wpf, while you can delete those dll manually.

@NCLnclNCL
Copy link

3a11734ba30d5a161b3f966c4586e3c5 With the latest nightly build, the size of nativeaot winforms app comes to 19mb.

Note that the published winforms app comes with unnecessary native dependency dll from wpf, while you can delete those dll manually.

I think the included dlls have been deleted to only have 19mb left

@hez2010
Copy link

hez2010 commented May 11, 2024

I think the included dlls have been deleted to only have 19mb left

You are right. The 19mb is the size after those dlls being deleted.

@MichalStrehovsky
Copy link
Member Author

MichalStrehovsky commented May 13, 2024

By adding a couple supported switches to the project file, it can shrink down to 9.5 MB (~2.3 MB compressed, since this compresses well). This is on x64; x86 is even smaller. Very cool!

  <PropertyGroup>
    <XmlResolverIsNetworkingEnabledByDefault>false</XmlResolverIsNetworkingEnabledByDefault>
    <SatelliteResourceLanguages>en-US</SatelliteResourceLanguages>
    <OptimizationPreference>Size</OptimizationPreference>
    <InvariantGlobalization>true</InvariantGlobalization>
    <StackTraceSupport>false</StackTraceSupport>
    <UseSystemResourceKeys>true</UseSystemResourceKeys>
  </PropertyGroup>

  <ItemGroup>
    <!-- We really should introduce a first class property for this -->
    <RuntimeHostConfigurationOption Include="System.Windows.Forms.PictureBox.UseWebRequest" Value="false" Trim="true" />
  </ItemGroup>

@elachlan
Copy link
Contributor

elachlan commented May 13, 2024

@MichalStrehovsky Is the remaining size the WPF reference?

Edit:
<UseWpf>false</UseWpf> should work properly now.

@MichalStrehovsky
Copy link
Member Author

@MichalStrehovsky Is the remaining size the WPF reference?

There's no WPF anymore. This is size of WinForms, XML parser, garbage collector, etc. This is a fully standalone WinForms app that doesn't need a .NET runtime installed on the system.

There's still ways to go down with size. One thing that immediately jumps out are various embedded resources. There's almost 3 MB of them (out of the 9.5 MB total):

image

Many ways to go about this - could this be made trimmable (embedded resources are not trimmed, but embedded resources are not the only way to get data into the executable)? Could this be compressed (these are all uncompressed ICO files, but they compress ridiculously well and ICO has built-in support for PNG since Vista)?

Then there's probably a bunch of other opportunities - I see a lot of WinForms code related to MDI, but many apps don't use MDI so it looks unnecessary. Etc. Sizoscope would let you see all that.

@elachlan
Copy link
Contributor

wow, I just checked, its like a 90% reduction in file size by using PNG and still having multiple icons. Down to 2.59kb from 53kb on alignment.icon.

Something we should probably investigate.

@RussKie
Copy link
Member

RussKie commented May 13, 2024

Then there's probably a bunch of other opportunities - I see a lot of WinForms code related to MDI, but many apps don't use MDI so it looks unnecessary. Etc. Sizoscope would let you see all that.

You'd be surprised how many LOB apps do actually use MDI.

@elachlan
Copy link
Contributor

All the apps I am maintaining are MDI.

@hamarb123
Copy link
Contributor

I have never used MDI, so it would be a waste of space for me :)

@hamarb123
Copy link
Contributor

hamarb123 commented May 13, 2024

wow, I just checked, its like a 90% reduction in file size by using PNG and still having multiple icons. Down to 2.59kb from 53kb on alignment.icon.

Yeah, if we run all the icons through gimp (iirc I used gimp anyway) we could probably get them smaller. I got an icon down from 416kB to 3kB by using it (which ended up saving me ~1.6MB overall as it was used in multiple places), and still had all the scales I had.

@MichalStrehovsky
Copy link
Member Author

Literally all I said is "many apps don't use MDI so it looks unnecessary". Especially in a WinForms hello world. It amounts to 300 kB of garbage in the WinForms hello world app. It would also be garbage in tools like Sizoscope that also use WinForms, but don't use MDI.

A quick hack I tried:

Add a file named ILLink.Substitutions.xml next to the csproj. This is the contents:

<linker>
  <assembly fullname="System.Windows.Forms">
    <type fullname="System.Windows.Forms.Form">
      <method signature="System.Void UpdateMdiWindowListStrip()" body="stub" />
    </type>
  </assembly>
</linker>

Then add this to the csproj:

  <ItemGroup>
    <IlcArg Include="--substitution:ILLink.Substitutions.xml" />
  </ItemGroup>

Et voilà, 300 kB of garbage removed.

@paul1956
Copy link
Contributor

I have very old apps that use MDI and newer ones that don’t. I would love to just see a switch somewhere where I could specify if I need it. My biggest concern is is WebView 2 which is larger than my whole application just to display a Captcha.

@MichalStrehovsky
Copy link
Member Author

I have very old apps that use MDI and newer ones that don’t. I would love to just see a switch somewhere where I could specify if I need it. My biggest concern is is WebView 2 which is larger than my whole application just to display a Captcha.

I think it's feasible to just restructure code within WinForms so that this can be trimmed naturally. It just currently can't because UpdateMdiWindowListStrip is statically called from Form.OnVisibleChanged and that one is obviously needed. So UpdateMdiWindowListStrip cannot be trimmed and neither can anything that it calls to. The low tech "fix" for situations like this is to realize that for something to become part of MDI, some other method needs to be called first (setter for MdiParentInternal?). So one could replace the static call to UpdateMdiWindowListStrip in OnVisibleChanged to an indirect call (e.g. delegate invocation) and only create the delegate in the other method that actually deals with MDI. That way if nobody calls the method to make the form an MDI form, the delegate will always be null and whatever it would point to gets trimmed. But if MDI is actually used, the delegate will be created and the callback called. There are probably other ways to achieve this, but this is the gist - get rid of the direct calls that cannot be trimmed by setting up an indirection.

This is just one example. A person more familiar with WinForms will probably find more low hanging fruit than just MDI. This is literally a quick sample I found after messing with this for 15 minutes with little WinForms experience.

@elachlan
Copy link
Contributor

@MichalStrehovsky raised a good point in this comment:
#11375 (comment)

Which is essentially, are all icon/image embedded resources needed for an application compiled AOT?
If the images loaded by ToolboxBitmapAttribute are only used in the designer, then could we technically trim them when compiling to AOT?

The other suggestion is to move the icon resources to System.Windows.Forms.Design but again, this depends on if they are only used in the designer. It also makes it harder to just pass the type to ToolboxBitmapAttribute.

@elachlan
Copy link
Contributor

@MichalStrehovsky I had a chat to the team today and they are very interested in a way to trim out the icons. Is there someone who is more familiar with the trimmer that could help here?

They are unlikely to accept the PR for the png icons due to backwards compatibility. If we can reduce the risk of breakages then they are more likely to take it on. So adjusting System.Drawing.Bitmap to support png based ico files could help with that. Failing that, bitmap based icons would work with only small size savings.

@NCLnclNCL
Copy link

NCLnclNCL commented Jun 27, 2024

have this

    <RuntimeHostConfigurationOption Include="System.ComponentModel.Design.IDesignerHost.IsSupported"
                                    Value="false"
                                    Trim="true" />
    <RuntimeHostConfigurationOption Include="System.Windows.Forms.Binding.IsSupported"
                                    Value="false"
                                    Trim="true" />
    <RuntimeHostConfigurationOption Include="System.ComponentModel.DefaultValueAttribute.IsSupported"
                                    Value="false"
                                    Trim="true" />
    <RuntimeHostConfigurationOption Include="System.Drawing.Design.UITypeEditor.IsSupported"
                                    Value="false"
                                    Trim="true" />
    <RuntimeHostConfigurationOption Include="System.Windows.Forms.ActiveXImpl.IsSupported"
                                    Value="false"
                                    Trim="true" />
    <RuntimeHostConfigurationOption Include="System.Windows.Forms.Control.AreDesignTimeFeaturesSupported"
                                    Value="false"
                                    Trim="true" />
    <RuntimeHostConfigurationOption Include="System.Windows.Forms.MdiWindowDialog.IsSupported"
                                    Value="false"
                                    Trim="true" />
     <RuntimeHostConfigurationOption Include="System.Windows.Forms.ImageIndexConverter.IsSupported"
                                    Value="false"
                                    Trim="true" />
    <RuntimeHostConfigurationOption Include="System.Windows.Forms.Primitives.TypeConverterHelper.UseComponentModelRegisteredTypes"
                                    Value="true"
                                    Trim="true" />
    <RuntimeHostConfigurationOption Include="System.Windows.Forms.Control.UseComponentModelRegisteredTypes"
                                    Value="true"
                                    Trim="true" />
  </ItemGroup>

@NCLnclNCL
Copy link

NCLnclNCL commented Jul 1, 2024

<ItemGroup>
  <TrimmableAssembly Include="System.Windows.Forms" />
  <TrimmableAssembly Include="System.Windows.Forms.Primitives" />
</ItemGroup>

<Target Name="ConfigureTrimming" BeforeTargets="PrepareForILLink">
  <ItemGroup>
    <ManagedAssemblyToLink Condition="'%(Filename)' == 'System.Windows.Forms'">
      <IsTrimmable>true</IsTrimmable>
    </ManagedAssemblyToLink>
    <ManagedAssemblyToLink Condition="'%(Filename)' == 'System.Windows.Forms.Primitives'">
      <IsTrimmable>true</IsTrimmable>
    </ManagedAssemblyToLink>
  </ItemGroup>
</Target>

@JeremyKuhne JeremyKuhne modified the milestones: .NET 9.0, .NET 10.0 Jul 24, 2024
felixf4xu added a commit to Cute-Ct/WinFormsTrimTest.Net8 that referenced this issue Nov 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests