Skip to content

Error when using the System.IO.Ports nuget package #1750

Description

@alnlarsen

Using a simple netstandard project using the System.IO.Ports package, and trying to use any SerialPort function in a CLI action gives me the following exception:

00:00:00.346 : CLI           : Debug       : Executing CLI action: SerialTest
00:00:00.346 : tap           : Error       : System.IO.Ports is currently only supported on Windows.
00:00:00.346 : tap           : Debug       : PlatformNotSupportedException: System.IO.Ports is currently only supported on Windows.
00:00:00.362 : tap           : Debug       :     at System.IO.Ports.SerialPort..ctor()
00:00:00.362 : tap           : Debug       :     at SerialTest.Execute(CancellationToken cancellationToken) in C:\Users\allarsen\RiderProjects\ConsoleApp2\ConsoleApp2\Program.cs:line 11
00:00:00.362 : tap           : Debug       :     at OpenTap.Cli.ICliActionExtensions.PerformExecute(ICliAction action, String[] parameters)
00:00:00.362 : tap           : Debug       :     at OpenTap.Cli.CliActionExecutor.Execute(String[] args)
00:00:00.362 : tap           : Debug       : Exception caught at:
00:00:00.362 : tap           : Debug       :     at Void Execute()
00:00:00.362 : tap           : Debug       :     at Void wrapGoInProcess()
00:00:00.362 : tap           : Debug       :     at Void goInProcess()
00:00:00.362 : tap           : Debug       :     at System.Object InvokeMethod(System.Object, System.Object[], System.Signature, Boolean)

System.IO.Ports is currently only supported on Windows.

This is a pretty strange error to get because I am building and running this project on Windows.

The error happens because System.IO.Ports ships a common stub dll containing only the public interfaces and documentation, which throws PlatformNotSupportedException in all implementations. It also ships platform-dependent implementations in the runtimes directory.

UPDATE

After some investigation, I now have a good understanding of the behavior we are seeing.

The .NET resolver checks the .deps.json file of the entry assembly (e.g. tap.exe) to figure out which runtime variant to load. tap.deps.json does not exist when building MyPlugin.dll. Instead, there will be a filed called MyPlugin.deps.json in the output folder. This is the file we would like the .NET resolver to consult when loading DLLs in a debug build. Renaming MyPlugin.deps.json fixes the exception mentioned above.

A few key takeaways from my investigation:

  • In .NET Framework, the OpenTAP resolver gets to resolve a DLL before the default resolver. This behavior has changed in .NET, where several other strategies are tried before "falling back" to the AssemblyResolve hook OpenTAP uses. To ensure the OpenTAP resolver runs first, OpenTAP must override the default load context. See: https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/loading-managed#algorithm
  • OpenTAP cannot practically use AssemblyLoadContext without targetting .NET 9, which is not planned for a few releases.
  • This is mostly a problem in debug builds. In practice, a package author will package the correct dll for the target runtime.
  • In debug builds, the workaround is to copy .deps.json to tap.deps.json to help the default resolver
  • When packaging files for release, the solution is to explicitly package the required runtime DLLs

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentation

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions