A modern .NET port of Windows Forms has been available for some time. The fact that it only runs on Windows is a legacy of the original Framework version, which is tightly coupled with older Windows technologies like the COM interface.
To be a candidate for Native AOT, a library must be "trimmable", which in turn depends on avoiding dynamic type resolution, or reflection, as much as possible. Another obstacle to trimming is COM marshalling, which Windows Forms are "heavily reliant" upon.1
Microsoft's documentation suggests COM wrappers as a strategy to provide the static type guarantees that COM marshalling ordinarily precludes. Third-party attempts to bring trimmable COM marshalling to Windows Forms have already appeared, but support is still lacking for a long-term-support framework (currently .NET 8). Fortunately, it looks like Microsoft intends to eventually refactor Windows Forms for Native AOT.
To make this technical discussion more concrete (and, hopefully, to invite contributions), this repository includes a broken demo project using Windows Forms. A Native AOT build should complete successfully, but with several warnings from the IL compiler ("ILC"), e.g.,
Publishing a WinForms project with Native AOT
C:\git\Npp.DotNet.Plugin>dotnet publish examples\gui -r win-x64 -f net9.0-windows -c Release npp.dotnet.plugin net9.0-windows succeeded (5.3s) → lib\bin\Release\net9.0-windows\win-x64\npp.dotnet.plugin.dll Npp.DotNet.Plugin.Gui.Demo net9.0-windows succeeded with 5 warning(s) (41.5s) → examples\gui\bin\Release\net9.0-windows\win-x64\publish\ C:\Users\Admin\.nuget\packages\microsoft.windowsdesktop.app.runtime.win-x64\9.0.0\runtimes\win-x64\lib\net9.0\System.Windows.Forms.Primitives.dll : warning IL3053: Assembly 'System.Windows.Forms.Primitives' produced AOT analysis warnings. ILC : warning IL3000: System.Windows.Forms.ThreadExceptionDialog.ThreadExceptionDialog(Exception): 'System.Reflection.Assembly.Location.get' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'. C:\Users\Admin\.nuget\packages\microsoft.windowsdesktop.app.runtime.win-x64\9.0.0\runtimes\win-x64\lib\net9.0\System.Windows.Forms.dll : warning IL3053: Assembly 'System.Windows.Forms' produced AOT analysis warnings. ILC : warning IL3000: System.Windows.Forms.Control.ControlVersionInfo.OwnerIsInMemoryAssembly.get: 'System.Reflection.Assembly.Location.get' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'. C:\Users\Admin\.nuget\packages\microsoft.windowsdesktop.app.runtime.win-x64\9.0.0\runtimes\win-x64\lib\net9.0\System.Formats.Nrbf.dll : warning IL3053: Assembly 'System.Formats.Nrbf' produced AOT analysis warnings.
Build succeeded with 5 warning(s) in 47.5s
Publishing a WinForms project with Native AOT (net8.0-windows)
C:\git\Npp.DotNet.Plugin>dotnet publish examples\gui -r win-x64 -f net8.0-windows -c Release
MSBuild version 17.9.6+a4ecab324 for .NET
Determining projects to restore...
All projects are up-to-date for restore.
Npp.DotNet.Plugin -> C:\git\npp.dotnet.plugin\lib\bin\Release\net8.0-windows\Npp.DotNet.Plugin.dll
Npp.DotNet.Plugin.Gui.Demo -> C:\git\npp.dotnet.plugin\examples\gui\bin\Release\net8.0-windows\win-x64\Npp.DotNet.Plugin.Gui.Demo.dll
Optimizing assemblies for size may change the behavior of the app. Be sure to test after publishing. See: https://aka.ms/dotnet-illink
Generating native code
ILC: Method '[PresentationFramework]System.Windows.Documents.MsSpellCheckLib.RCW+SpellCheckerFactoryCoClass..ctor()' will always throw because: Invalid IL or CLR metadata in 'Void SpellCheckerFactoryCoClass..ctor()'
ILC: Method '[PresentationFramework]System.Windows.Documents.MsSpellCheckLib.RCW+SpellCheckerFactoryCoClass.UnregisterUserDictionary(string,string)' will always throw because: Invalid
IL or CLR metadata in 'Void SpellCheckerFactoryCoClass.UnregisterUserDictionary(System.String, System.String)'
ILC: Method '[PresentationFramework]System.Windows.Documents.MsSpellCheckLib.RCW+SpellCheckerFactoryCoClass.RegisterUserDictionary(string,string)' will always throw because: Invalid IL
or CLR metadata in 'Void SpellCheckerFactoryCoClass.RegisterUserDictionary(System.String, System.String)'
ILC: Method '[PresentationFramework]System.Windows.Documents.MsSpellCheckLib.RCW+SpellCheckerFactoryCoClass.CreateSpellChecker(string)' will always throw because: Invalid IL or CLR met
adata in 'ISpellChecker SpellCheckerFactoryCoClass.CreateSpellChecker(System.String)'
ILC: Method '[WindowsBase]MS.Internal.Security.AttachmentService+AttachmentServices..ctor()' will always throw because: Invalid IL or CLR metadata in 'Void AttachmentServices..ctor()'
ILC: Method '[PresentationCore]MS.Internal.AppModel.CustomCredentialPolicy+InternetSecurityManager..ctor()' will always throw because: Invalid IL or CLR metadata in 'Void InternetSecur
ityManager..ctor()'
ILC: Method '[System.DirectoryServices]System.DirectoryServices.UnsafeNativeMethods+PropertyEntry..ctor()' will always throw because: Invalid IL or CLR metadata in 'Void PropertyEntry.
.ctor()'
ILC: Method '[System.DirectoryServices]System.DirectoryServices.UnsafeNativeMethods+PropertyValue..ctor()' will always throw because: Invalid IL or CLR metadata in 'Void PropertyValue.
.ctor()'
Creating library bin\Release\net8.0-windows\win-x64\native\Npp.DotNet.Plugin.Gui.Demo.lib and object bin\Release\net8.0-windows\win-x64\native\Npp.DotNet.Plugin.Gui.Demo.exp
Npp.DotNet.Plugin.Gui.Demo -> C:\git\npp.dotnet.plugin\examples\gui\bin\Release\net8.0-windows\win-x64\publish\
The warnings are significant. A module that is not properly trimmed is basically a .NET assembly. If placed in the plugin path, it may throw an unhandled exception at any time, crashing Notepad++.
Many .NET plugins for Notepad++ depend on libraries. The IL compiler has no fault tolerance for .NET assemblies intended to run on .NET Framework, and each dependency increases the chances of a build failure. The status of third-party libraries compatible with modern .NET is completely unknown, as no .NET plugin has ever been successfully ported to a recent framework. Also keep in mind that simply targeting modern .NET does not guarantee AOT compatibility. The particular requirements for trimming compatibility may not be achievable for some libraries.
Native AOT generates self-contained applications. Even after being trimmed, the remaining portion of the .NET runtime required to execute the app will be at least a few megabytes in size, and this will be statically linked with the binary. That's a substantial difference from a typical .NET Framework assembly, which usually weighs in at less than 1MB, with all of its runtime code residing in core system libraries.