Modernize Desktop Apps On Windows With NET
Modernize Desktop Apps On Windows With NET
NET 6
PUBLISHED BY
All rights reserved. No part of the contents of this book may be reproduced or transmitted in any
form or by any means without the written permission of the publisher.
This book is provided “as-is” and expresses the author’s views and opinions. The views, opinions, and
information expressed in this book, including URL and other Internet website references, may change
without notice.
Some examples depicted herein are provided for illustration only and are fictitious. No real association
or connection is intended or should be inferred.
Microsoft and the trademarks listed at https://www.microsoft.com on the “Trademarks” webpage are
trademarks of the Microsoft group of companies.
All other marks and logos are property of their respective owners.
Co-Authors:
Miguel Ramos, Senior Program Manager, Windows Developer Platform team, Microsoft
Adam Braden, Principal Program Manager, Windows Developer Platform team, Microsoft
Ricardo Minguez Pablos, Senior Program Manager, Azure IoT team, Microsoft
Introduction
This book is about strategies you can adopt to move your existing desktop applications through the
path of modernization and incorporate the latest runtime, language, and platform features. You’ll
discover that there’s no unique recipe as each application is different, and so are your requirements
and preferences. The good news is that there are common approaches you can apply to add new
features and capabilities to your applications. Some of them won’t even require major modifications
of your code. In this book, we’ll reveal how all those features work behind the scenes and explain the
mechanics of their implementations. Moreover, you’ll find some common scenarios for modernizing
existing desktop applications shown in detail so you can find inspiration for evolving your projects.
Microsoft’s approach to modernizing existing applications is to give you the flexibility to create your
own customized path. All the modernization strategies described in this book are mostly independent.
You can choose ones that are relevant for your application and skip others that aren’t important for
you. In other words, you can mix and match the strategies to best address your application needs.
You might also find this book useful if you’re a technical decision maker, such as an enterprise
architect or a development lead or director who wants an overview of the benefits of updating
existing desktop applications.
Along the different chapters, sample implementation code snippets and screenshots are provided,
with chapter 5 devoted to showcase a complete migration process for sample applications.
Also, on the GitHub repository for this book, you’ll find the results of the process, which you can
consult with if you decide to follow the step-by-step tutorial.
WPF ........................................................................................................................................................................................... 4
UWP .......................................................................................................................................................................................... 4
Deployment ........................................................................................................................................................................... 8
Installation.............................................................................................................................................................................. 8
XAML Islands...................................................................................................................................................................... 15
Performance ....................................................................................................................................................................... 16
i Contents
Configuration files ................................................................................................................................................................ 17
ODBC ..................................................................................................................................................................................... 20
OLE DB .................................................................................................................................................................................. 20
ADO.NET .............................................................................................................................................................................. 20
AppDomains ...................................................................................................................................................................... 22
Remoting ............................................................................................................................................................................. 22
Preparation ......................................................................................................................................................................... 43
ii Contents
Fix the code and build .................................................................................................................................................... 45
Tools ...................................................................................................................................................................................... 62
iii Contents
CHAPTER 1
Why modern desktop
applications
Introduction
A story of one company
Back in the early 2000s, one multinational company started developing a distributed desktop solution
to exchange information between different branches of the company and execute optimized
operations on centralized units. They have chosen a brand-new framework called Windows Forms
(also known as WinForms) for their application development. Over the years, the project evolved into
a mature, well tested, and time-proven application with hundreds of thousands of lines of code. Time
passed and .NET Framework 2.0 is no longer the hot new technology. The developers who are
working on this application are facing a dilemma. They’d like to use the latest stack of technologies in
their development and have their application look and “feel” modern. At the same time, they don’t
want to throw away the great product they have built over 15 years and rewrite the entire application
from scratch.
Your story
You might find yourself in the same boat, where you have mature Windows Forms or Windows
Presentation Foundation (WPF) applications that have proved their reliability over the years. You
probably want to keep using these applications for many more years. At the same time, since those
applications were written some time ago, they might be missing capabilities like modern look,
performance, integration with new devices and platform features, and so on, which gives them a feel
of “old tech”. There’s another problem that might concern you as a developer. While working on the
older .NET Framework versions and maintaining applications that were written a while ago, you might
feel like you aren’t learning new technologies and missing on building modern technical skills. If that
is your story – this book is for you!
Then, Internet technologies started shocking the development world and winning over more
engineers with advantages like easy deployment and simplified distribution processes. The fact that
once a web application was deployed to production all users got automatic updates made a huge
impact on the software agility.
However, the Internet infrastructure, underlying protocols, and standards like HTTP and HTML weren’t
designed for building complex applications. In fact, the major development effort back then was
aiming just one goal: to give web applications the same capabilities that desktop applications have,
such as fast data input and state management.
Even though web and mobile applications have grown at an incredible pace, for certain tasks desktop
applications still hold the number one place in terms of efficiency and performance. That explains why
there are millions of developers who are building their projects with WPF and WinForms and the
amount of those applications is constantly growing.
Here are some reasons for choosing desktop applications in your development:
Microsoft offered many UI desktop technologies throughout the years from Win32 introduced in 1995
to Universal Windows Platform (UWP) released in 2016.
You can develop in any of them using C# and Visual Basic, but let’s take a closer look.
Windows Forms
First released in 2002, Windows Forms is a managed framework and is the oldest, most used, desktop
technology built on the Windows graphics device interface (GDI) engine. It offers a smooth drag-and-
drop experience for developing user interfaces in Visual Studio. At the same time, Windows Forms
relies on the Visual Studio Designer as the main way you develop your UI, so creating visual
components from code isn’t trivial.
• Mature technology.
• Designer is available, but developers usually prefer to create the design from code using
declarative XAML.
• The learning curve is steeper than Windows Forms.
• Supported on any Windows version.
• Supported on .NET Core 3.0 and later versions.
UWP
UWP isn’t only a presentation framework like WPF and Windows Forms, but it’s also a platform itself.
This platform has:
• Applications are executed in app containers. App containers control what resources a UWP app
can access.
• Supported only on Windows 10.
• Apps can be deployed through Microsoft Store for easier deployment.
• Designed as part of the Windows Runtime API.
• Contains an extensive set of rich built-in controls and additional controls available through the
Microsoft UI Library NuGet packages (WinUI library) updated every few months.
The following table describes the differences between the two concepts:
Aspect of
comparison Modern Application Desktop Application
Security Contained execution & Great Fundamentals. User & Admin level of security. You
Designed from the ground up to respect have native access to the registry and
user’s privacy, manage battery life, and hard drive folders.
focus to keep the device safe.
Deployment Installation and updates are managed by MSI, Custom installers & Updates.
the platform. Traditionally a source of headaches
for developers and IT managers.
Distribution Trusted Distribution & Signed Packages. Web, SCCM & Custom distribution.
Distribution is performed from a trusted No control over what is installed,
source and never from the web. affects the whole machine.
UI Modern UI. Different input mechanisms, ink, Windows Forms, WPF, MFC. Designed
touch, gamepad, keyboard, mouse, etc. for the mouse and keyboard for a
dense UI and to get the most
productivity from the desktop.
Data Cloud First Data with Insights. Source of Local Data. Traditional desktop
truth in the cloud. Insights to know what applications usually need some local
happens with your app and how it’s data.
performing.
Design Designed for reuse. Reuse in mind between Designed for Windows Desktop only
different platforms, front end, and back end,
running assets in many places as possible.
As a part of the commitment to provide developers with the best tools to build applications, Microsoft
put a great effort to bring these concepts, or we can even say platforms, closer together to empower
developers with the best of both worlds. To do that, Microsoft has performed a bidirectional effort
between the two platforms.
1. Move Modern Application features into Desktop Applications. For existing desktop apps that
need a way to leverage modern capabilities without rewriting from scratch, features from the
Modern Application platform are pushed into the Desktop Application.
In this book, we’ll focus on the second part and show how you can modernize your existing desktop
applications.
Modern features
Say you have a working Windows Forms application that a sales representative of your company uses
to fill in a customer order. A new requirement comes in to enable the customer to sign the order
using a tablet pen. Inking is native in today’s operating systems and technologies, but it wasn’t
available when the app was developed.
This path will show you how you can leverage modern desktop features into your existing desktop
development.
Deployment
Modern development cycles have stressed out to provide agility on how new versions of applications
are deployed to every single user. Since Windows Forms and WPF applications are based on a
particular version of the .NET Framework that must be present on the machine, they can’t take
advantage of new .NET Framework version features without the intervention of the IT people with the
risk of having side effects for other apps running on the same machine. It has limited the innovation
pace for developers forcing them to stay on outdated versions of the .NET Framework.
Since the launch of .NET Core 3.0, you can leverage a new approach of deploying multiple versions of
.NET side by side and specifying which version of .NET each application should target. This way, you
can use the newest features in one application while being confident you aren’t going to break any
other applications.
Installation
Desktop applications always rely on some sort of installation process before the user can start using
them. This fact brought into the game a set of technologies, from MSI and ClickOnce to custom
installers or even XCOPY deployment. Any of these methods deals with delicate problems because
applications need a way to access shared resources on the machine. Sometimes installation needs to
access the Registry to insert or update new Key Values, sometimes to update shared DLLs referenced
by the main application. This behavior causes a continuous headache for users, creating this
perception that once you install some application, your computer will never be the same, even if you
uninstall it afterwards.
In this book, we’ll introduce a new way of installing applications with MSIX that solves the problem
described earlier. You’ll learn how you can easily set up packaging, installation, and updates for your
application.
If you have been developing Windows Forms or WPF applications for a long time, you are definitely
familiar with .NET Framework. A few years ago, a new .NET Core platform (specifically its 3.0 version)
started supporting desktop applications. The next version of .NET Core was rebranded to .NET 5, and
now we are releasing its successor, .NET 6. Just as there are different versions of .NET Framework, for
example, 4.6, 4.7, and 4.8, there are also different versions of .NET Core: .NET Core 3, .NET 5, and .NET
6. Our recommendation is to stay on the latest version, .NET 6, especially because it is a long-term
support version that Microsoft will support for three years.
And now let’s look into the history of each platform to understand the differences and benefits of
each.
If you’re targeting only one of these platforms, you can use this model. However, in many cases you
might need more than one target platform in the same solution. For example, your application may
have a desktop admin part, a customer-facing web site that shares the back-end logic running on a
server, and even a mobile client. In this case, you need a unified coding experience that can span all
this .NET verticals.
By the time Windows 8 was released, the concept of Portable Class Libraries (PCLs) was born.
Originally, the .NET Framework was designed around the assumption that it would always be
This leads to reasoning about the API differences between verticals at the assembly level, as opposed
to the individual API level that we had before. This aspect enabled a class library experience that can
target multiple verticals, also known as portable class libraries.
With PCL, the experience of development is unified across verticals based on the API shape. And the
most pressing need to create libraries running on different verticals is also addressed. But there’s a
great challenge: APIs are only portable when the implementation is moved forward across all the
verticals.
Another large challenge has to do with how the .NET Framework is deployed. The .NET Framework is a
machine-wide framework. Any changes made to it affect all applications taking a dependency on it.
Although this deployment model has many advantages, such as reducing disk space and centralized
access to services, it presents some pitfalls.
To start with, it’s difficult for application developers to take a dependency on a recently released
framework. They either have to take a dependency on the latest OS or provide an application installer
that installs the .NET Framework along with the application. If you are a web developer, you might not
even have this option as the IT department establishes the server supported version.
Even if you’re willing to go through the trouble of providing an installer to chain in the .NET
Framework setup, you may find that upgrading the .NET Framework can break other applications.
Despite the efforts to provide backward compatible versions of the framework, there are compatible
changes that can break applications. For example, adding an interface to an existing type can change
how this type is serialized and cause breaking problems depending on the existing code. Because the
NET Framework installed base is huge, fighting against these breaking scenarios slows down the pace
of innovations inside the .NET Framework.
To solve all these issues, Microsoft has developed .NET Core to approach the evolution of the .NET
Platform.
The purpose of .NET Core is to provide a unified platform for all types of applications, which includes
Windows, cross-platform, and mobile applications. .NET Standard enables this by providing shared
base APIs, which every application model needs, and excluding any application model-specific API.
This framework gives applications many benefits in terms of efficiency and performance, simplifying
the packaging and deployment in the different supported platforms.
• Produce a single .NET runtime and framework that can be used everywhere and that has uniform
runtime behaviors and developer experiences.
• Expand the capabilities of .NET by taking the best of .NET Core, .NET Framework, Xamarin, and
Mono.
• Build that product out of a single code-base that developers (Microsoft and the community) can
work on and expand together and that improves all scenarios.
This new release and direction are a game-changer for .NET. With .NET 5, your code and project files
will look and feel the same no matter which type of app you’re building. You’ll have access to the
same runtime, APIs, and language capabilities with each app. This includes new performance
improvements that get committed to the runtime, practically daily. For more details, see What’s new
in .NET 5.
In .NET 6 we’ve made numerous improvements in reliability, performance, new APIs, and language
features. It is a newer, better, and more stable version for desktop applications and it’s going to stick
around for a while. So we recommend updating your application to this version, and in the next
chapter, you can learn how to do that.
In 2019 the last version of the .NET Framework - 4.8 was released. It included three major
improvements for desktop applications:
• Modern browser and media controls: Today, .NET desktop applications use Internet Explorer
and Windows Media Player for showing HTML and playing media files. Since these legacy
controls don’t show the latest HTML or play the latest media files, new controls were added that
take advantage of Microsoft Edge and newer media players that support the latest standards.
• Access to UWP controls: UWP contains new controls that take advantage of the latest Windows
features and touch displays. You don’t have to rewrite your applications to use these new
features and controls, so you can use these new features in your existing WPF or Windows Forms
code.
.NET 5 is the open-source, cross-platform, and fast-moving version of .NET family. Because of its side-
by-side nature, it can take changes without the fear of breaking any application. This means that .NET
will get new APIs and language features over time that .NET Framework won’t. Also, .NET already has
features that were impossible for .NET Framework, such as:
• Side-by-side versions of .NET supporting Windows Forms and WPF: This solves the problem
of side effects when updating the machine’s framework version. Multiple versions of .NET can be
installed on the same machine and each application specifies which version of .NET it should use.
Even more, now you can develop and run Windows Forms and WPF on top of .NET.
• Embed .NET directly into an application: You can deploy .NET as part of your application
package. This enables you to take advantage of the latest version, features, and APIs without
having to wait for a specific version to be installed on the machine.
• Take advantage of .NET features: .NET Core is the fast-moving, open-source version of .NET.
Its side-by-side nature enables fast introduction of new innovative APIs and Base Class Libraries
(BCL) improvements without the risk of breaking compatibility. Now Windows Forms and WPF
applications can take advantage of the latest .NET features, which also includes more
fundamental fixes for runtime performance, high-DPI support, and so on.
An essential part of the roadmap for Microsoft was to ease developers to move applications to .NET
Core and in later to .NET 5. But if you have existing .NET Framework applications, you shouldn’t feel
pressured to move to .NET 5. .NET Framework will be fully supported and will always be a part of
Windows. However, if you want to use the newest language features and APIs in the future, you’ll
need to move your applications to .NET.
For your brand-new desktop applications, we recommend starting directly on .NET 5. It’s lightweight
and cross platform, runs side by side, has high performance, and fits perfectly on containers and
microservices architectures.
• Defines uniform set of base class libraries APIs for all .NET implementations to implement,
independent of the workload.
• Enables developers to produce portable libraries that are usable across .NET implementations,
using this same set of APIs.
.NET Standard is the evolution of PCLs and the following list shows the fundamental differences
between .NET Standard and PCLs:
A new version of .NET Standard, version 2.1, was released at the same time as .NET Core 3.0. As
expected, .NET Core 3.x versions support .NET Standard 2.1 and earlier versions.
Also, it’s important to notice that both Windows Forms and WPF implementations for .NET Core are
open source.
XAML Islands
XAML Islands is a set of components for developers to use the new Windows 10 controls (UWP XAML
controls) in their current WPF, Windows Forms, and native Win32 apps (like MFC). You can have your
“islands” of UWP XAML controls wherever you want inside your Win32 apps.
These XAML Islands are possible because Windows 10, version 1903 introduces a set of APIs that
allows hosting UWP XAML content in Win32 windows using windows handlers (HWnds). Notice that
only apps running on Windows 10 1903 and above can use XAML Islands.
To make it easier to create XAML Islands for Windows Forms and WPF developers, the Windows
Community Toolkit introduces a set of .NET wrappers in several NuGet packages. Those wrappers are
the wrapped and hosting controls:
When a new .NET Core version is released, you can update each app on a machine as needed without
any concern of affecting other applications. New .NET Core versions are installed in their own
directories and exist “side-by-side” with each other.
If you need to deploy with isolation, you can deploy .NET Core with your application. .NET Core will
bundle your app with the .NET Core runtime as in a single executable.
Performance
Since its start, targeting the web and cloud workloads, .NET Core has had performance plugged into
its DNA. Server-side code must be performant enough to fulfill high-concurrency scenarios and .NET
Core-family platforms including .NET 6 scores today as the best performance web platform in the
market.
In .NET 5 and .NET 6 we’ve refactored old Windows Forms code wich resulted in reducing memory
allocations for drawing Forms and Controls, so by simply upgrading from .NET Framework to .NET 6
your applications become much faster. You can take advantage of these performance improvements
when you use .NET to build your next generation of desktop applications.
This is a key success factor of .NET Core that will continue to speed up the roadmap previously
mentioned: To be the single .NET platform that any developer will ever need to build any application.
If you just want to update your application to the latest .NET version using a tool and not get into the
details of what’s happening behind the scenes, feel free to skip this chapter and find step-by-step
instructions in the Example of migrating to .NET chapter.
A complex desktop application doesn’t work in isolation and needs some kind of interaction with
subsystems that may reside on the local machine or on a remote server. It will probably need some
kind of database to connect as a persistence storage either locally or remotely. With the raise of
Internet and service-oriented architectures, it’s common to have your application connected to some
sort of service residing on a remote server or in the cloud. You may need to access the machine file
system to implement some functionality. Alternatively, maybe you’re using a piece of functionality
that resides inside a COM object outside your application, which is a common scenario if, for example,
you’re integrating Office assemblies in your app.
Besides, there are differences in the API surface that is exposed by .NET Framework and .NET, and
some features that are available on .NET Framework aren’t available on .NET. So, it’s important for you
to know and take them into account when planning a migration.
Configuration files
Configuration files offer the possibility to store sets of properties that are read at run time and affect
the behavior of our apps, such as where to locate a database or how many times to execute a loop.
The beauty of this technique is that you can modify some aspects of the application without the need
to recode and recompile. This comes in handy when, for example, the same app code runs on a
development environment with a certain set of configuration values and in production with a different
one.
Configuration on .NET
In the .NET world, there’s no machine.config file. And even though you can continue to use the old
fashioned System.Configuration namespace, you may consider switching to the modern
Microsoft.Extensions.Configuration, which offers a good number of enhancements.
The configuration API supports the concept of configuration provider, which defines the data source
to be used to load the configuration. There are different kinds of built-in providers, such as:
The new configuration allows a list of name-value pairs that can be grouped into a multi-level
hierarchy. Any stored value maps to a string, and there’s built-in binding support that allows you to
deserialize settings into a custom plain old CLR object (POCO).
The ConfigurationBuilder object lets you add as many configuration providers you may need for your
application, using a precedence rule to resolve preference. So, the last provider you add in your code
will override the others. This is a great feature for managing different environments for execution
since you can define different configurations for development, testing and production environments,
and manage them on a single function inside your code.
To migrate from an old-style app.config to a new configuration file, you should choose between an
XML format and a JSON format.
If you choose XML, the conversion is straightforward. Since the content is the same, just save the
app.config file with XML as type. Then, change the code that references AppSettings to use the
ConfigurationBuilder class. This change should be easy.
If you want to use a JSON format and you don’t want to migrate by hand, there’s a tool called dotnet-
config2json available on .NET that can convert an app.config file to a JSON configuration file.
You may also come across some issues when using configuration sections that were defined in the
machine.config file. For example, consider the following configuration:
18 CHAPTER 3 | Migrating Modern Desktop applications
<configuration>
<system.diagnostics>
<switches>
<add name="General" value="4" />
</switches>
<trace autoflush="true" indentsize="2">
<listeners>
<add name="myListener"
type="System.Diagnostics.TextWriterTraceListener,
System, Version=1.0.3300.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
initializeData="MyListener.log"
traceOutputOptions="ProcessId, LogicalOperationStack, Timestamp,
ThreadId, Callstack, DateTime" />
</listeners>
</trace>
</system.diagnostics>
</configuration>
This exception occurs because that section and the assembly responsible for handling that section
was defined in the machine.config file, which now doesn’t exist.
To easily fix the issue, you can copy the section definition from your old machine.config to your new
configuration file:
<configSections>
<section name="system.diagnostics"
type="System.Diagnostics.SystemDiagnosticsSection,
System, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"/>
</configSections>
Accessing databases
Almost every desktop application needs some kind of database. For desktop, it’s common to find
client-server architectures with a direct connection between the desktop app and the database
engine. These databases can be local or remote depending on the need to share information between
different users.
From the code perspective, there have been many technologies and frameworks to give the developer
the possibility to connect, query, and update a database.
The most common examples of database you can find when talking about Windows Desktop
application are Microsoft Access and Microsoft SQL Server. If you have more than 20 years of
experience programming for the desktop, names like ODBC, OLEDB, RDO, ADO, ADO.NET, LINQ, and
Entity Framework will sound familiar.
OLE DB
OLE DB has been a great way to access various data sources in a uniform manner. But it was based on
COM, which is a Windows-only technology, and as such wasn’t the best fit for a cross-platform
technology such as .NET. It’s also unsupported in SQL Server versions 2014 and later. For those
reasons, OLE DB won’t be supported by .NET.
ADO.NET
You can still use ADO.NET from your existing desktop code on .NET. You just need to update some
NuGet packages.
The latest technology released as part of the .NET Framework world is Entity Framework, with 6.4
being the latest version. With the launch of .NET Core, Microsoft also released a new data access stack
based on Entity Framework and called Entity Framework Core.
You can use EF 6.4 and EF Core from both .NET Framework and .NET. So, what are the decision drivers
to help to decide between the two?
EF 6.3 is the first version of EF6 that can run on .NET and work cross-platform. In fact, the main goal of
this release was to make it easier to migrate existing applications that use EF6 to .NET.
EF Core was designed to provide a developer experience similar to EF6. Most of the top-level APIs
remain the same, so EF Core will feel familiar to developers who have used EF6.
Although compatible, there are differences on the implementation you should check before making a
decision. For more information, see Compare EF Core & EF6.
• The app will run on Windows and .NET Framework 4.0 or later.
• EF6 supports all of the features that the app requires.
SQL Server
SQL Server has been one of the databases of choice if you were developing for the desktop some
years ago. With the use of System.Data.SqlClient in .NET Framework, you could access versions of SQL
Server, which encapsulates database-specific protocols.
In .NET, you can find a new SqlClient class, fully compatible with the one existing in the .NET
Framework but located in the Microsoft.Data.SqlClient library. You just have to add a reference to the
Microsoft.Data.SqlClient NuGet package and do some renaming for the namespaces and everything
should work as expected.
Microsoft Access
Microsoft Access has been used for years when the sophisticated and more scalable SQL Server wasn’t
needed. You can still connect to Microsoft Access using the System.Data.Odbc library.
Consuming services
With the rise of service-oriented architectures, desktop applications began to evolve from a client-
server model to the three-layer approach. In the client-server approach, a direct database connection
is established from the client holding the business logic, usually inside a single EXE file. On the other
hand, the three-layer approach establishes an intermediate service layer implementing business logic
and database access, allowing for better security, scalability, and reusability. Instead of working
directly with underlying data, the layered approach relies on a set of services implementing contracts
and typed objects for data transfer.
If you have a desktop application using a WCF service and you want to migrate it to .NET, there are
some things to consider.
The first thing is how to resolve the configuration to access the service. Because the configuration is
different on .NET, you’ll need to make some updates in your configuration file.
Second, you’ll need to regenerate the service client with the new tools present on Visual Studio 2019.
In this step, you must consider activating the generation of the synchronous operations to make the
client compatible with your existing code.
After the migration, if you find that there are libraries you need that aren’t present on .NET, you can
add a reference to the Microsoft.Windows.Compatibility NuGet package and see if the missing
functions are there.
If you’re using the WebRequest class to perform web service calls, you may find some differences on
.NET. The recommendation is to use the System.Net.Http.HttpClient instead.
Insert a COMReference structure inside the project file like in the following example:
<ItemGroup>
<COMReference Include="MSHTML">
<Guid>{3050F1C5-98B5-11CF-BB82-00AA00BDCE0B}\</Guid>
<VersionMajor>4</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>primary</WrapperTool>
<Isolated>false</Isolated>
</COMReference>
</ItemGroup>
The Windows Compatibility Pack provides access to APIs that were previously available only for .NET
Framework. It can be used on .NET Core and .NET Standard projects.
For more information on API compatibility, you can find documentation about breaking changes and
deprecated/legacy APIs at https://docs.microsoft.com/dotnet/core/compatibility/fx-core.
AppDomains
Application domains (AppDomains) isolate apps from one another. AppDomains require runtime
support and are expensive. Creating additional app domains isn’t supported. For code isolation, we
recommend separate processes or using containers as an alternative. For the dynamic loading of
assemblies, we recommend the new AssemblyLoadContext class.
To make code migration from .NET Framework easier, .NET exposes some of the AppDomain API
surface. Some of the APIs function normally (for example, AppDomain.UnhandledException), some
members do nothing (for example, SetCachePath), and some of them throw
PlatformNotSupportedException (for example, CreateDomain).
Remoting
.NET Remoting was used for cross-AppDomain communication, which is no longer supported. Also,
Remoting requires runtime support, which is expensive to maintain. For these reasons, .NET Remoting
isn’t supported on .NET.
Use security boundaries that are provided by the operating system, such as virtualization, containers,
or user accounts for running processes with the minimum set of privileges.
Security Transparency
Similar to CAS, Security Transparency separates sandboxed code from security critical code in a
declarative fashion but is no longer supported as a security boundary.
Use security boundaries that are provided by the operating system, such as virtualization, containers,
or user accounts for running processes with the least set of privileges.
With the release of Windows 10, Microsoft introduced many innovations to support scenarios like
tablets and touch devices and to provide the best experience for users for a Microsoft operating
system ever. For example, you can:
One important thing to note here is that you don’t need to abandon .NET Framework technology to
follow this modernization path. You can safely stay on there and have all the benefits of Windows 10
without the pressure to migrate to .NET. So, you get both the power and the flexibility to choose your
modernization path.
WinRT APIs
WinRT APIs are object-oriented, well-structured application programming interfaces (APIs) that give
Windows 10 developers access to everything the operating system has to offer. Through WinRT APIs,
you can integrate functionalities like Push Notifications, Device APIs, Microsoft Ink, and WinML,
among others on your desktop apps.
In general, WinRT APIs can be called from a classic desktop app. However, two main areas present an
exception to this rule:
Some WinRT APIs require this package identity to work as expected. However, classic desktop apps
like native C++ or .NET apps, use different deployment systems that don’t require a package identity.
If you want to use these WinRT APIs in your desktop application, you need to provide them a package
identity.
One way to proceed is to build an additional packaging project. Inside the packaging project, you
point to the original source code project and specify the Identity information you want to provide. If
you install the package and run the installed app, it will automatically get an identify enabling your
code to call all WinRT APIs requiring Identity.
You can check which APIs need a packaged application identity by inspecting if the type that contains
the API is marked with the DualApiPartition attribute. If it is, you can call if from an unpackaged
traditional desktop app. Otherwise, you must convert your classic desktop app to a UWP with the help
of a packaging project.
https://docs.microsoft.com/windows/desktop/apiindex/uwp-apis-callable-from-a-classic-desktop-app
Benefits of packaging
Besides giving you access to these APIs, you get some additional benefits by creating a Windows App
package for your desktop application including:
• Streamlined deployment. Apps have a great deployment experience ensuring that users can
confidently install an application and update it. If a user chooses to uninstall the app, it’s
removed completely with no trace left behind preventing the Windows rot problem.
• Automatic updates and licensing. Your application can participate in the Microsoft Store’s
built-in licensing and automatic update facilities. Automatic update is a highly reliable and
efficient mechanism, because only the changed parts of files are downloaded.
• Increased reach and simplified monetization. Maybe not your case but if you choose to
distribute your application through the Microsoft Store you reach millions of Windows 10 users.
The design goal for packaging is to separate the application state from system state while maintaining
compatibility with other apps. Windows 10 accomplishes this goal by placing the application inside a
UWP package. It detects and redirects some changes to the file system and registry at run time to
fulfill the promise of a trusted and clean install and uninstall behavior of an application provided by
packaging.
Packages that you create for your desktop application are desktop-only, full-trust applications that
aren’t sandboxed, although there’s lightweight virtualization applied to the app for writes to HKCU and
AppData. This virtualization allows them to interact with other apps the same way classic desktop
applications do.
Installation
App packages are installed under %ProgramFiles%\WindowsApps\package_name, with the executable
titled app_name.exe. Each package folder contains a manifest (named AppxManifest.xml) that
contains a special XML namespace for packaged apps. Inside that manifest file is an <EntryPoint>
element, which references the full-trust app. When that application is launched, it doesn’t run inside
an app container, but instead it runs as the user as it normally would.
After deployment, package files are marked read-only and heavily locked down by the operating
system. Windows prevents apps from launching if these files are tampered with.
File system
The OS supports different levels of file system operations for packaged desktop applications,
depending on the folder location.
When trying to access the user’s AppData folder, the system creates a private per-user, per-app
location behind the scenes. This creates the illusion that the packaged application is editing the real
AppData when it’s actually modifying a private copy. By redirecting writes this way, the system can
track all file modifications made by the app. It can then clean all those files when uninstalling reducing
system “rot” and providing a better application removal experience for the user.
Registry
App packages contain a registry.dat file, which serves as the logical equivalent of HKLM\Software in
the real registry. At run time, this virtual registry merges the contents of this hive into the native
system hive to provide a singular view of both.
All writes are kept during package upgrade and only deleted when the application is uninstalled.
For details about how a packaged application handles installation, file access, registry, and
uninstallation, see https://docs.microsoft.com/windows/msix/desktop/desktop-to-uwp-behind-the-
scenes.
Let’s take an existing WPF sample app that reads files and shows its contents on the screen. The goal
is to display a Toast Notification when the application starts.
First, you should check in the following link whether the Windows 10 API that you’ll use requires a
Package Identity:
Our sample will use the Windows.UI.Notifications.Notification API that requires a packaged identity:
To access the WinRT API, add a reference to the Microsoft.Windows.SDK.Contracts NuGet package
and this package will do the magic behind the scenes (see details at
https://blogs.windows.com/windowsdeveloper/2019/04/30/calling-windows-10-apis-from-a-desktop-
application-just-got-easier/).
Create a ShowToastNotification method that will be called on application startup. It just builds a
toast notification from an XML pattern:
Although the project builds, there are errors because the Notifications API requires a Package Identity
and you didn’t provide it. Adding a Windows Packaging Project to the solution will fix the issue:
Next step is to add the WPF application to the Windows Packaging Project by adding a project
reference:
Let’s generate the package so you can install your app. Right click on Store > Create App Packages.
XAML Islands
XAML Islands are a set of components that enable Windows desktop developers to use UWP XAML
controls on their existing Win32 applications, including Windows Forms and WPF.
Besides adding functionality from the Windows 10 APIs, you can add pieces of UWP XAML inside of
your app using XAML Islands.
Windows 10 1903 update introduces a set of APIs that allows hosting UWP XAML content in Win32
windows. Only apps running on Windows 10 1903 can use XAML Islands.
In 2015, along with Windows 10, UWP was born. UWP allows you to create apps that work across
Windows devices like XBox, Mobile, and Desktop. One year later, Microsoft announced Desktop
Bridge (formerly known as Project Centennial). Desktop Bridge is a set of tools that allowed
developers to bring their existing Win32 apps to the Microsoft Store. More capabilities were added in
2017, allowing developers to enhance their Win32 apps leveraging some of the new Windows 10 APIs,
like live tiles and notifications on the action center. But still, no new UI controls.
At Build 2018, Microsoft announced a way for developers to use the new Windows 10 XAML controls
into their current Win32 apps, without fully migrating their apps to UWP. It was branded as UWP
XAML Islands.
How it works
The Windows 10 1903 update introduces several XAML hosting APIs. Two of them are
WindowsXamlManager and DesktopWindowXamlSource.
The DesktopWindowXamlSource is the instance of your XAML Island content. It has the Content
property, which you’re responsible for instantiating and setting. The DesktopWindowXamlSource
renders and gets its input from an HWND. It needs to know to which other HWND it will attach the
XAML Island’s one, and you’re responsible for sizing and positioning the parent’s HWND.
WPF or Windows Forms developers don’t usually deal with HWND inside their code, so it may be hard
to understand and handle HWND pointers and the underlying wiring stuff to communicate Win32 and
UWP worlds.
The Windows Community Toolkit offers two types of controls: Wrapped Controls and Hosting
Controls.
Wrapped Controls
These wrapped controls wrap some UWP controls into Windows Forms or WPF controls, hiding UWP
concepts for those developers. These controls are:
<Window
...
xmlns:uwpControls="clr-
namespace:Microsoft.Toolkit.Wpf.UI.Controls;assembly=Microsoft.Toolkit.Wpf.UI.Controls">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="\*"/>
</Grid.RowDefinitions>
<uwpControls:InkToolbar TargetInkCanvas="{x:Reference Name=inkCanvas}"/>
<uwpControls:InkCanvas Grid.Row="1" x:Name="inkCanvas" />
</Grid>
Hosting controls
The power of XAML Islands extends to most first-party controls, third-party controls, and custom
controls developed for UWP, which can be integrated into Windows Forms and WPF as “Islands” with
fully functional UI. The WindowsXamlHost control for WPF and Windows Forms allows doing this.
Once you’ve placed your WindowsXamlHost into your UI code, specify which UWP type you want to
load. You can choose to use a wrapped control like a Button or a more complex one composed by
several different controls, which are a custom UWP control.
<Window
...
xmlns:xamlhost="clr-
namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost">
<xamlhost:WindowsXamlHost x:Name="myUwpButton"
InitialTypeName="Windows.UI.Xaml.Controls.Button" />
There’s a clear recommendation on how to approach this and it’s better to have one single and bigger
XAML Island containing a custom composite control than to have several islands with one control on
each.
One of the core features of XAML is binding and it works between your Win32 code and the island.
So, you can bind, for instance, a Win32 Textbox with a UWP Textbox. One important thing to consider
is that these bindings are one-way bindings, from UWP to Win32, so if you update the Textbox inside
the XAML Island the Win32 Textbox will be updated, but not the other way around.
https://docs.microsoft.com/windows/apps/desktop/modernize/host-standard-control-with-xaml-
islands
The custom UWP XAML control can be included on this UWP app or in an independent UWP Class
Library project that you reference in the same solution as your WPF or Windows Forms project.
https://docs.microsoft.com/windows/apps/desktop/modernize/host-custom-control-with-xaml-
islands
The following article demonstrates how to host a UWP XAML control from the WinUI 2 library:
https://docs.microsoft.com/windows/apps/desktop/modernize/host-custom-control-with-xaml-
islands
If you’re developing a new Windows App, a UWP App is probably the right approach.
Starting with WinUI 2 in 2018, Microsoft started shipping some new XAML UI controls and features as
separate NuGet packages that build on top of the UWP SDK.
XAML framework will now be developed on GitHub and shipped out of band as NuGet packages.
The existing UWP XAML APIs that ship as part of the OS will no longer receive new feature updates.
They will still receive security updates and critical fixes according to the Windows 10 support lifecycle.
The Universal Windows Platform contains more than just the XAML framework (for example,
application and security model, media pipeline, Xbox and Windows 10 shell integrations, broad device
support) and will continue to evolve. All new XAML features will just be developed and ship as part of
WinUI instead.
WinUI 3 will address this critical feedback adding WinUI in desktop apps. This will allow that Win32
apps can use WinUI 3 as standalone UI Framework; no need to load Windows Forms or WPF.
Within this aggregation, WinUI 3 will let developers easily mix and match the right combination of:
Migrating by hand
The migration process consists of four sequential steps:
2. Migrate Project File: .NET projects use the new SDK-style project format. Create a new project
file with this format or update the one you have to use the SDK style.
3. Fix code and build: Build the code in .NET addressing API-level differences between .NET
Framework and .NET. If needed, update third-party packages to the ones that support .NET.
4. Run and test: There might be differences that don’t show up until run time. So, don’t forget to
run the application and test that everything works as expected.
Preparation
So, you need to transition from one format to another. You can do the update manually, taking the
dependencies contained in the packages.config file and migrating them to the project file with the
PackageReference format. Or, you can let Visual Studio do the work for you: right-click on the
packages.config file and select the Migrate packages.config to PackageReference option.
Maybe the project is referencing older package versions that don’t support .NET, but you might find
newer versions that do support it. So, updating packages to newer versions is generally a good
recommendation. However, you should consider that updating the package version can introduce
some breaking changes that would force you to update your code.
What happens if you don’t find a compatible version? What if you just don’t want to update the
version of a package because of these breaking changes? Don’t worry because it’s possible to depend
on .NET Framework packages from a .NET application. Don’t forget to test it extensively because it can
cause run-time errors if the external package calls an API that isn’t available on .NET. This is great for
when you’re using an old package that isn’t going to be updated and you can just retarget to work on
the .NET.
https://docs.microsoft.com/dotnet/standard/analyzers/portability-analyzer
An interesting aspect of this tool is that it only surfaces the differences from your own code and not
code from external packages, which you can’t change. Remember you should have updated most of
these packages to make them work with .NET.
The SDK-style project for .NET is a lot simpler than .NET Framework’s project format. Apart from the
previously mentioned PackageReference entries, you won’t need to do much more. The new project
format includes files with certain extensions by default, such as .cs and .xaml files, without the need
to explicitly include them in the project file.
AssemblyInfo considerations
Attributes are autogenerated on .NET projects. If the project contains an AssemblyInfo.cs file, the
definitions will be duplicated, which will cause compilation conflicts. You can delete the older
AssemblyInfo.cs file or disable autogeneration by adding the following entry to the .NET project file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
</Project>
Resources
Embedded resources are included automatically but resources aren’t, so you need to migrate the
resources to the new project file.
Package references
With the Migrate packages.config to PackageReference option, you can easily move your external
package references to the new format as previously mentioned.
Microsoft.Windows.Compatibility
If your application depends on APIs that aren’t available on .NET, such as Registry, ACLs, or WCF, you
have to include a reference to the Microsoft.Windows.Compatibility package to add these
Windows-specific APIs. They work on .NET but aren’t included as they aren’t cross-platform.
• AppDomains
• Remoting
• Code Access Security
• WCF Server
• Windows Workflow
That’s why you need to find a replacement for these technologies if you’re using them in your
application. For more information, see the .NET Framework technologies unavailable on .NET Core and
.NET 5+ article.
In this final step, you can find several different issues depending on the complexity of your application
and the dependencies and APIs you’re using.
Another reason for errors is the use of the BeginInvoke and EndInvoke methods because they aren’t
supported on .NET. They aren’t supported on .NET because they have a dependency on Remoting,
which doesn’t exist on .NET. To solve this issue, try to use the await keyword (when available) or the
Task.Run method.
You can use compatibility analyzers to let you identify APIs and code patterns in your code that can
potentially cause problems at run time with .NET. Go to https://github.com/dotnet/platform-compat
and use the .NET API analyzer on your project.
This application shows a product catalog and allows the user to navigate, filter, and search for
products. From an architecture point of view, the app relies on an external WCF service that serves as
a façade to a back-end database.
You can see the main application window in the following picture:
If you open the .csproj project file, you can see something like this:
In the Solution Explorer, right click on the Windows Forms project and select Unload Project > Edit.
Now you can update the .csproj file. You’ll delete the entire content and replace it with the following
code:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
</Project>
Save and reload the project. You’re now done updating the project file and the project is targeting the
.NET.
If you compile the project at this point, you’ll find some errors related to the WCF client reference.
Since this code is autogenerated, you must regenerate it to target .NET.
Right-click on Connected Services and select the Add Connected Service option.
If you have the WCF Service in the same solution as we have in this example, you can select the
Discover option instead of specifying a service URL.
If you compile the project again and execute it, you won’t see the product images. The problem is that
now the path to the files has slightly changed. To fix this issue, you need to add another level of depth
in the path, updating in the file CatalogView.cs the line:
to
After this change, you can check that the application launches and runs as expected on .NET Core.
This application uses a local SQL Server Express database to hold the product catalog information.
This database is accessed directly from the WPF application.
First, you must update the .csproj file to the new SDK style used by .NET Core applications. You’ll
follow the same steps described in the Windows Forms migration: you’ll unload the project, open the
.csproj file, update its contents, and reload the project.
In this case, delete all the content of the .csproj file and replace it with the following code:
If you reload the project and compile it, you’ll get the following error:
Since you’ve deleted all the .csproj contents, you’ve lost a project reference specification present in the
old project. You just need to add this line to the .csproj file to include the project reference back:
<ItemGroup>
<ProjectReference Include="..\\eShop.SqlProvider\\eShop.SqlProvider.csproj" />
<ItemGroup>
You can also let Visual Studio help you by right-clicking on the Dependencies node and selecting
Add Project Reference. Select the project from the solution and click OK:
These days, we’re all familiar with the DevOps concept, where developers and IT Pros work closely to
move applications to their production environments. But if you’ve been in the desktop battle for more
than 10 years, you might have seen the following story. A team of developers works together hard to
meet the project deadlines. Business people are nervous since they need the system working on many
user’s machines to run the company. On “D-Day”, the project manager checks with every developer
that their code is working well and that everything is fine, so they can ship. Then, the package team
comes in generating the setup for the app, distribute it to every user machine and a set of test users
run the application. Well, they try, because before showing any UI, the application throws an
exception that says “Method ~ of object ~ failed”. Panic starts flowing through the air and a brief
investigation points to a young and tired developer that has introduced a third-party control, that
certainly “worked on the dev machine”.
Installing desktop applications have traditionally been a nightmare for two main reasons:
In this chapter, we’ll talk about MSIX. MSIX is the brand-new technology from Microsoft that tries to
capture the best of previous technologies to provide a solid foundation for the packaging technology
of the future.
What does a packaging technology have to do with modernization? Well, it turns out that packaging
is fundamental for the enterprise IT with lots of money invested there. Modernization isn’t only related
Companies need a way to break this packaging cycle into three independent cycles:
• OS updates
• Application updates
• Customization
With the use of resource packages, you can easily create multilingual apps and the OS takes care of
installing the ones that are used.
Network optimization
MSIX detects the differences on the files at the byte block level enabling a feature called differential
updates. What this means is that only the updated byte blocks are downloaded on application
updates.
With the optional packages feature, you achieve componentization on your app deployment, so you
can download them when needed.
OS managed
The OS handles all the processes for installing, updating, and removing an application. Applications
are installed per user but downloaded only once, minimizing the disk footprint. Microsoft is working
on providing the MSIX experience also on Windows 7.
Tools
App Installer
App Installer allows Windows 10 apps to be installed by double-clicking the app package. This means
that users don’t need to use PowerShell or other developer tools to deploy Windows 10 apps. The
App Installer can also install an app from the web, optional packages, and related sets.
You’ll see the structure of the packaging project and note a special folder called Applications. Inside
this folder, you can specify which applications you want to include in the package. It can be more than
one.
If you open the code for the Package.appxmanifest file, you can see a couple of interesting things.
Right under <Package>, there’s an <Identity> node. This is where your packaged application is going
to get its identity, which will be managed by the OS.
The final stage is about how you deploy the MSIX package to another machine.
Right-click on the packaging project, select the Store menu, and then select the Create App
Packages option.
Then, you can choose between creating a package to upload to the store or creating packages for
sideloading. In most modernization scenarios, you’ll choose I want to create packages for
sideloading.
The final step is to declare where you want to deploy the final installation assets.
For a detailed step-by-step guide, see Package a desktop app from source code using Visual Studio.
Using a combination of Windows 10 features and MSIX packages, you can provide a great updating
experience for your users. In fact, the user doesn’t need to be technical at all but still benefit from a
seamless application update experience.
You can configure your updates to interact with the user in two different ways:
• Silent updates in the background. With this option, your users don’t need to be aware of the
updates.
You can also configure when you want to perform updates, when the application launches or on a
regular basis. Thanks to the side-loading features, you can even get these updates while the
application is running.
When you use this type of deployment, a special file is created called .appinstaller. This simple file
contains the following sections:
In combination with this file, Microsoft has designed a special URL protocol to launch the installation
process from a link:
This protocol works on all browsers and launches the installation process with a great user experience
on Windows 10. Since the OS manages the installation process, it’s aware of the location this
application was installed from and tracks all the files affected by the process.
MSIX creates a user interface for installation automatically showing some properties of the package.
This allows for a common installation experience for every app.