.NET Core 3.0 will be RTM soon and it supports WPF and Winforms APIs.

In my last post I’ve been exploring .NET Core 3.0 new APIs by comparing compiled bits with NDepend, of .NET Core 3.0 against .NET Core 2.2.

In this post I will compare .NET Core 3.0 Windows Forms (Winforms) and WPF APIs with .NET Framework 4.x.

I won’t make the suspense last: .NET Core 3.0 support for Winforms and WPF APIs is almost complete, I found very few breaking changes.

I will now explain what I’ve done with NDepend to explore this API diff, and then dig into the results. If you are wondering How to port desktop applications to .NET Core 3.0 see Microsoft explanations here.

Comparing .NET Core 3.0 Winforms and WPF APIs vs. NET Framework 4.x with NDepend

From the NDepend Start Page select Compare 2 versions of a code base menu. Then use Add assemblies in Folder buttons to add .NET Framework assemblies from folder C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319 and Microsoft.WindowsDesktop.app nuget package assemblies (from folder C:\Users\psmac\.nuget\packages\microsoft.windowsdesktop.app\3.0.0-preview-27325-3\ref

etcoreapp3.0 on my machine).

A minor difficulty was to isolate the exact set of assemblies to focus on. Here is the list of concerned assemblies I came up with:

Accessibility PresentationCore PresentationCore-CommonResources PresentationFramework PresentationFramework.Aero PresentationFramework.Aero2 PresentationFramework.AeroLite PresentationFramework.Classic PresentationFramework.Luna PresentationFramework.Royale PresentationUI ReachFramework System.Drawing System.Drawing.Design System.Drawing.Primitives System.Printing System.Security.AccessControl System.Security.Cryptography.Cng System.Security.Cryptography.Pkcs System.Security.Cryptography.Xml System.Security.Permissions System.Security.Principal.Windows System.Windows.Controls.Ribbon System.Windows.Forms System.Windows.Input.Manipulations System.Windows.Presentation System.Xaml UIAutomationClient UIAutomationClientSideProviders UIAutomationProvider UIAutomationTypes WindowsBase WindowsFormsIntegration 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 Accessibility PresentationCore PresentationCore - CommonResources PresentationFramework PresentationFramework . Aero PresentationFramework . Aero2 PresentationFramework . AeroLite PresentationFramework . Classic PresentationFramework . Luna PresentationFramework . Royale PresentationUI ReachFramework System . Drawing System . Drawing . Design System . Drawing . Primitives System . Printing System . Security . AccessControl System . Security . Cryptography . Cng System . Security . Cryptography . Pkcs System . Security . Cryptography . Xml System . Security . Permissions System . Security . Principal . Windows System . Windows . Controls . Ribbon System . Windows . Forms System . Windows . Input . Manipulations System . Windows . Presentation System . Xaml UIAutomationClient UIAutomationClientSideProviders UIAutomationProvider UIAutomationTypes WindowsBase WindowsFormsIntegration

Notice that to preserve the correspondance between APIs and assemblies packaging, the attribute TypeForwardedToAttribute is massively used to delegate implementations.

The few breaking changes

With the default NDepend rules about API breaking changes, I’ve only found 16 public types and 52 public methods missing. Here are the types:

16 types missing on a total of 4.095 public types, well done!

The 52 public methods missing are: (on a total of 42.645 public methods)

Parent Assembly Name Parent Type Name Method name System.Security.Principal.Windows System.Security.Principal.WindowsIdentity .ctor(String,String) System.Security.Principal.Windows System.Security.Principal.WindowsIdentity Impersonate() System.Security.Principal.Windows System.Security.Principal.WindowsIdentity Impersonate(IntPtr) System.Security.Principal.Windows System.Security.Principal.IdentityReferenceCollection get_IsReadOnly() System.Security.Permissions System.Net.EndpointPermission ToString() System.Security.Permissions System.Security.HostProtectionException GetObjectData(SerializationInfo,StreamingContext) System.Security.Permissions System.Security.Policy.ApplicationDirectory Clone() System.Security.Permissions System.Security.Policy.ApplicationTrust Clone() System.Security.Permissions System.Security.Policy.PermissionRequestEvidence Clone() System.Security.Permissions System.Security.Policy.Site Clone() System.Security.Permissions System.Security.Policy.StrongName Clone() System.Security.Permissions System.Security.Policy.Url Clone() System.Security.Permissions System.Security.Policy.Zone Clone() System.Security.Permissions System.Security.Policy.GacInstalled Clone() System.Security.Permissions System.Security.Policy.Hash Clone() System.Security.Permissions System.Security.Policy.Publisher Clone() System.Security.Cryptography.Pkcs System.Security.Cryptography.Pkcs.EnvelopedCms .ctor(SubjectIdentifierType,ContentInfo) System.Security.Cryptography.Pkcs System.Security.Cryptography.Pkcs.EnvelopedCms .ctor(SubjectIdentifierType,ContentInfo,AlgorithmIdentifier) System.Security.Cryptography.Pkcs System.Security.Cryptography.Pkcs.EnvelopedCms Encrypt() System.Security.Cryptography.Pkcs System.Security.Cryptography.Pkcs.ContentInfo Finalize() System.Security.Cryptography.Cng System.Security.Cryptography.ECDiffieHellmanCng FromXmlString(String) System.Security.Cryptography.Cng System.Security.Cryptography.ECDiffieHellmanCng ToXmlString(Boolean) System.Security.Cryptography.Cng System.Security.Cryptography.ECDsaCng FromXmlString(String) System.Security.Cryptography.Cng System.Security.Cryptography.ECDsaCng ToXmlString(Boolean) System.Security.Cryptography.Cng System.Security.Cryptography.RSACng DecryptValue(Byte[]) System.Security.Cryptography.Cng System.Security.Cryptography.RSACng EncryptValue(Byte[]) System.Security.Cryptography.Cng System.Security.Cryptography.RSACng get_KeyExchangeAlgorithm() System.Security.Cryptography.Cng System.Security.Cryptography.RSACng get_SignatureAlgorithm() System.Printing System.Printing.PrintQueue set_Name(String) System.Printing System.Printing.IndexedProperties.PrintInt32Property op_Implicit(PrintInt32Property) System.Printing System.Printing.IndexedProperties.PrintStringProperty op_Implicit(PrintStringProperty) System.Printing System.Printing.IndexedProperties.PrintStreamProperty op_Implicit(PrintStreamProperty) System.Printing System.Printing.IndexedProperties.PrintQueueAttributeProperty op_Implicit(PrintQueueAttributeProperty) System.Printing System.Printing.IndexedProperties.PrintQueueStatusProperty op_Implicit(PrintQueueStatusProperty) System.Printing System.Printing.IndexedProperties.PrintBooleanProperty op_Implicit(PrintBooleanProperty) System.Printing System.Printing.IndexedProperties.PrintThreadPriorityProperty op_Implicit(PrintThreadPriorityProperty) System.Printing System.Printing.IndexedProperties.PrintServerLoggingProperty op_Implicit(PrintServerLoggingProperty) System.Printing System.Printing.IndexedProperties.PrintDriverProperty op_Implicit(PrintDriverProperty) System.Printing System.Printing.IndexedProperties.PrintPortProperty op_Implicit(PrintPortProperty) System.Printing System.Printing.IndexedProperties.PrintServerProperty op_Implicit(PrintServerProperty) System.Printing System.Printing.IndexedProperties.PrintTicketProperty op_Implicit(PrintTicketProperty) System.Printing System.Printing.IndexedProperties.PrintByteArrayProperty op_Implicit(PrintByteArrayProperty) System.Printing System.Printing.IndexedProperties.PrintProcessorProperty op_Implicit(PrintProcessorProperty) System.Printing System.Printing.IndexedProperties.PrintQueueProperty op_Implicit(PrintQueueProperty) System.Printing System.Printing.IndexedProperties.PrintJobPriorityProperty op_Implicit(PrintJobPriorityProperty) System.Printing System.Printing.IndexedProperties.PrintJobStatusProperty op_Implicit(PrintJobStatusProperty) System.Printing System.Printing.IndexedProperties.PrintDateTimeProperty op_Implicit(PrintDateTimeProperty) System.Printing System.Printing.IndexedProperties.PrintSystemTypeProperty op_Implicit(PrintSystemTypeProperty) System.Printing System.Windows.Xps.XpsDocumentWriter raise__WritingProgressChanged(Object,WritingProgressChangedEventArgs) System.Printing System.Windows.Xps.XpsDocumentWriter raise__WritingCompleted(Object,WritingCompletedEventArgs) System.Printing System.Windows.Xps.XpsDocumentWriter raise__WritingCancelled(Object,WritingCancelledEventArgs) System.Drawing System.Drawing.FontConverter Finalize()

Portability to .NET Core 3.0 analysis

Microsoft offers a Portability Analyzer tool to analyze changes in desktop API that will break your desktop app. I’ve tested it on NDepend but I just got very coarse results. Did I miss something? At least it is mostly green 🙂

I wrote last year a post named Quickly assess your .NET code compliance with .NET Standard let me know in comment if it is worth revisiting this post for desktop APIs. Btw, my guess is that desktop APIs won’t be part of .NET Standard vNext (since there is no plan to support it on all platforms) but I haven’t found any related info on the web.

Why migrate your desktop app to .NET Core 3.0?

This is a great news that Microsoft embeds good-old desktop APIs in .NET Core 3.0 with such an outstanding compatibility. It is worth noting that so far (February 2019) there is no plan to port Windows Forms and WPF on other platforms than Windows. So, what are the benefits of porting an existing application to .NET Core 3.0?

I found answers in this recent How to Port Desktop Applications to .NET Core 3.0 Channel9 30 minutes video at 5:12. Basically you’ll get more deployment flexibility, Core Runtime and API improvements and also more performances.

Microsoft promises to not urge anyone to port existing Winforms and WPF application to .NET Core 3.0. However for a Visual Studio extension shop like us if it is decided that VS will run on .NET Core 3.0 in the future, we hope to be notified many months ahead. We discussed that on twitter with Amanda Silver in January 2019. It looks like this spring 2019 they will take a decision. As a consequence to support both Visual Studio past versions running on .NET fx and new versions running on .NET Core 3, an extension will need to support both .NET Fx and .NET Core 3 desktop APIs.