Migrating to .NET 9
With the release of OpenTAP 9.29, the runtime has been upgraded from .NET Framework 4.7.2 to .NET 9.
This change brings significant performance improvements, better cross-platform support, and many new APIs. Unfortunately, it also introduces subtle behavior changes, and the removal of legacy APIs.
This guide highlights common problems and workarounds specifically related to OpenTAP, and is not meant to be comprehensive. For a comprehensive list of breaking changes, see the official documentation.
To know which version of .NET OpenTAP is using, you can check the log file. It might say:
; Microsoft Windows 10.0.19045X64
; .NET 9.0.7
; OpenTAP Engine 9.29.0+f68daa39 X64
2
3
Here it says, OpenTAP 9.29.0 running on .NET 9(.0.7) on Microsoft Windows 10, 64-bit.
Using OpenTAP 9.28, it might have said:
; Microsoft Windows 10.0.19045 X64
; .NET Framework 4.8.4790.0
; OpenTAP Engine 9.28.2+504225fd X64
2
3
Here it says, OpenTAP 9.28.2, running on .NET Framework 4.8, on Microsoft Windows 10, 64-bit.
Even though OpenTAP depends on a given .NET version, it might actually be running on a newer, but compatible runtime. For example, in 9.28, it specifies 4.7.2, but can run on 4.8. And in the same way, it could be running on .NET 10, even though it requires .NET 9.
ProcessStartInfo Defaults Changed
Symptoms
ProcessStartInfo behaves differently when starting processes, causing unexpected behavior in some applications.
Cause
Several default properties of ProcessStartInfo
have changed in .NET 9 compared to .NET Framework 4.7.2. For example, UseShellExecute
now defaults to false
, which affects how processes are launched.
Solution
Explicitly set important properties like UseShellExecute
, RedirectStandardOutput
, or CreateNoWindow
to ensure consistent behavior across .NET versions.
Example
var startInfo = new ProcessStartInfo
{
UseShellExecute = true, // Explicitly set to match .NET Framework behavior
};
Process.Start(startInfo);
2
3
4
5
Missing System.IO.Ports API
Symptoms
The type or namespace name 'IO' does not exist in the namespace 'System' (are you missing an assembly reference?)
or
Could not load file or assembly 'System.IO.Ports, Version=0.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified.
Cause
The System.IO.Ports
namespace has been removed from .NET 9 and is no longer included by default.
Solution
Install the OpenTAP Serial Ports package, which provides serial port functionality compatible with .NET 9:
<ItemGroup>
<OpenTapPackageReference Include="Serial Ports" Version="9.0.7" />
</ItemGroup>
2
3
This package restores the
System.IO.Ports
API functionality for OpenTAP users on .NET 9.
Other Missing .NET Framework APIs
Symptoms
The type or namespace name 'Drawing' does not exist in the namespace 'System' (are you missing an assembly reference?)
or
Could not load for or assembly 'name, Version=x.y.z.w, ...
Cause
Many APIs that were part of .NET Framework are not included by default in .NET 9.
Solution
Check if Microsoft provides a compatibility package. This is the case for most removed APIs. There is a meta package called Microsoft.Windows.Compatibility which restores many removed APIs, but there are also more targetted compatibility packages such as System.Drawing.Common which restores specific APIs. The specific packages should be preferred if possible.
Hint: Check the Dependencies tab of the meta package for a list of all compatibility packages provided by Microsoft.
<ItemGroup>
<PackageReference Include="Microsoft.Windows.Compatibility" Version="9.0.7" />
</ItemGroup>
2
3
This enables use of legacy APIs such as
System.Drawing
,System.ServiceModel
, and others.
Assembly Side-Loading Not Supported
Symptoms
Unexpected behavior of certain assemblies.
Cause
.NET Framework supports automatic side-by-side execution of strong named assemblies. This is a powerful feature, but can lead to subtle bugs and dependency issues, and has been removed in .NET 9.
In .NET Framework, attempting to load two assemblies with the same name but different versions results in two different instances of the same assembly. In .NET 9, the second load call will return a handle to the first assembly.
Solution
Side-loading is still possible, but must be manually managed using AssemblyLoadContext
.
AppDomain Changes
Symptoms
Some AppDomain
APIs are missing or behave differently in .NET 9, causing compatibility issues.
Cause
Several AppDomain
APIs related to assembly isolation and unloading have been removed or altered in .NET 9. The runtime now uses AssemblyLoadContext
for dynamic assembly loading and unloading.
Solution
Migrate code depending on AppDomain
to use AssemblyLoadContext
instead. AssemblyLoadContext
is too big a topic to cover in this document. Please refer to Microsoft's documentation about AssemblyLoadContext
Global Assembly Cache (GAC) Not Supported
Symptoms
FileNotFoundException or TypeLoadException exceptions raised from code previously working in .NET Framework.
In some cases, this is because the assembly is located in the GAC (Global Assembly Cache) and that is no longer supported in .NET 9.
Cause
.NET 9 does not support GAC.
Solution
Load the assembly directly or create a GAC assembly resolver.
Usually those assemblies are located inside a folder in C:\Windows\assembly\GAC_MSIL\[assembly-name]\[version]\[assembly-name].dll
.
The simplest solution is to add a custom assembly resolver for those assemblies. This can be done via AppDomain.Current.ResolveAssembly
. That event will be called if it was otherwise not possible to resolve the assembly.
If that solves the issue, instruct the users to install relevant software for the assembly to be located there. For example, the assembly may follow an IVI driver.
Alternatively, you can use the DLL as part of your solution by simply copying it into the OpenTAP installation folder. Check if the license permits that before distributing it.
CET Enabled by Default
Symptoms
Application crashes when calling functions from shared libraries.
Cause
.NET 9 enables CET by default. This is a security feature which sets some limitations to how a low level library can manipulate return addresses on the stack, and can cause issues in low level libraries using custom exception handling. For more information, see CET supported by default and Shadow Stack.
Solution
For compatibility reasons, OpenTAP disables CET, so if you are starting the process with tap.exe
or Editor.exe
, no steps are needed.
If you are in control of the shared library, you can update it to ensure it does not violate shadow stack protection rules. This is the preferred solution since it is the most secure solution, and it solves the problem regardless of how the library is used.
If you are in control of the executable with the problem, you can disable CET by setting a build property:
<PropertyGroup>
<!-- Disable Control-flow Enforcement Technology -->
<CETCompat>false</CETCompat>
</PropertyGroup>
2
3
4
If you are not in control of either the library or the binary, you can disable stack protections for your system or for a particular process.
BinaryFormatter Removed
Symptoms
Code using BinaryFormatter
for serialization fails to compile or throws runtime errors.
CauseBinaryFormatter
has been fully removed in .NET 9 due to long-standing security concerns.
Solution
Migrate to safer serialization alternatives such as System.Text.Json
.
Assembly.LoadWithPartialName Removed
Symptoms
Code using Assembly.LoadWithPartialName
fails to compile or throws runtime errors.
CauseAssembly.LoadWithPartialName
was deprecated and is removed in .NET 9 due to its unreliable behavior.
Solution
Use Assembly.Load
or AssemblyLoadContext
with the full assembly name or path instead.
Thread.Suspend and Thread.Resume Removed
Symptoms
Calls to Thread.Suspend
or Thread.Resume
result in compilation errors or runtime exceptions in .NET 9.
Cause
These APIs have been removed because there is no way to use them safely.
Solution
Refactor code to use safer synchronization primitives such as the lock
keyword, Monitor
, Mutex
, Semaphore
or Event
for synchronization.
Thread.Abort Removed
Symptoms
Calls to Thread.Abort
cause compilation errors or runtime failures in .NET 9.
CauseThread.Abort
has been removed due to its unsafe nature and potential to leave application state inconsistent.
Solution
Use cooperative cancellation patterns with CancellationToken
to gracefully stop threads.
Registry API Changes
Symptoms
Code using certain Windows-specific Registry APIs fails to compile or run in .NET 9.
Cause
Some Registry APIs have been removed or now require additional NuGet packages (like Microsoft.Win32.Registry
) to be referenced explicitly.
Solution
Add the Microsoft.Win32.Registry
package to your project and update your code to use the supported APIs.
<ItemGroup>
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
</ItemGroup>
2
3
System.IO.FileInfo.Length Exception on Missing File
Symptoms
Accessing FileInfo.Length
throws a FileNotFoundException
if the file does not exist.
Cause
In .NET 9, FileInfo.Length
no longer returns 0 for nonexistent files and instead throws an exception.
Solution
Check FileInfo.Exists
before accessing FileInfo.Length
.
TLS 1.0/1.1 Disabled by Default
Symptoms
Connections using TLS 1.0 or 1.1 fail to establish, causing communication errors.
Cause
.NET 9 disables TLS 1.0 and 1.1 by default for improved security. Only TLS 1.2 and higher are enabled.
Solution
Update your applications and servers to use TLS 1.2 or higher. Explicitly configure SslProtocols
if needed.
Example
var handler = new HttpClientHandler
{
SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
};
using var client = new HttpClient(handler);
2
3
4
5
6
HttpWebRequest & WebClient Deprecated
Symptoms
Code using HttpWebRequest
or WebClient
issues warnings or may not behave optimally in .NET 9.
CauseHttpWebRequest
and WebClient
are deprecated in favor of the newer, more flexible HttpClient
API.
Solution
Migrate to using HttpClient
for HTTP operations.
CodeDomProvider Removed for Dynamic Compilation
Symptoms
Code using CodeDomProvider
or CSharpCodeProvider
to compile code at runtime fails to compile or run in .NET 9.
CauseCodeDomProvider
and related runtime compilation APIs have been removed in .NET 9.
Solution
Migrate to the Roslyn compiler APIs (Microsoft.CodeAnalysis.CSharp
) for runtime code compilation.
Remoting Removed
Symptoms
Code using .NET Remoting APIs fails to compile or run after upgrading to .NET 9. This affects the following APIs:
- MarshalByRefObject.GetLifetimeService
- MarshalByRefObject.InitializeLifetimeService
Cause
.NET Remoting has been completely removed in .NET 9. These APIs are no longer supported or available.
Solution
There is no direct replacement. You must migrate to a different communication technology.