Anp.Serial.Win32
1.0.0
dotnet add package Anp.Serial.Win32 --version 1.0.0
NuGet\Install-Package Anp.Serial.Win32 -Version 1.0.0
<PackageReference Include="Anp.Serial.Win32" Version="1.0.0" />
<PackageVersion Include="Anp.Serial.Win32" Version="1.0.0" />
<PackageReference Include="Anp.Serial.Win32" />
paket add Anp.Serial.Win32 --version 1.0.0
#r "nuget: Anp.Serial.Win32, 1.0.0"
#:package Anp.Serial.Win32@1.0.0
#addin nuget:?package=Anp.Serial.Win32&version=1.0.0
#tool nuget:?package=Anp.Serial.Win32&version=1.0.0
Anp.Serial.Win32
Managed Windows serial (COM port) library for .NET Framework 4.8, .NET Standard 2.0, and .NET 8+. Focused on USB CDC devices and USB-to-serial adapters, with full support for Bluetooth virtual COM ports and physical RS232 ports. All via Win32 P/Invoke.
Devices are identified by their device interface path — the stable, unique symbolic link assigned by the OS — rather than a COM port number. PortName (e.g. COM3) is populated on a best-effort basis from the PnP registry for display and interop with legacy APIs, but the path is the primary identifier used for discovery, open, and hot-plug matching.
Features
- Discovery — enumerate all COM port devices, optionally filtered by USB VID/PID
- Hot-plug monitoring — get notified when devices arrive or are removed (Windows 8+)
- Async I/O —
ReadAsync,WriteAsync,ReadExactAsync,ReadToAsync,ReadLineAsync,WriteLineAsyncwith meaningful per-call timeouts (default 2 s, configurable per operation) - Graceful disconnect handling — mid-I/O device removal raises
SerialDisconnectedExceptioninstead of corrupting state; the device transitions cleanly to closed - Sync wrappers —
Write,ReadText,ReadLine,ReadTofor simple use cases - Serial control — baud rate, data bits, parity, stop bits, DTR/RTS, modem status, purge
- Device metadata —
VendorId/ProductIdparsed from the device path;FriendlyNameandPortName(best-effort, e.g.COM3) read from the PnP registry - Subclassable —
OnOpened()/OnClosing()hooks for protocol initialization and teardown - Diagnostics — subscribe to
SerialDiag.Errorfor library-level error reporting
Quick Start
A SerialDevice can be created via the built-in discovery or directly from a device interface path obtained from any external source — your own enumeration, a config file, a saved user preference, or a tool like PnpDeviceToolkit.
Discover devices
using Anp.Serial.Win32;
// All COM port devices
var devices = SerialDiscovery.GetDevices();
// Filter by USB VID/PID
var filtered = SerialDiscovery.GetDevices(vendorId: 0x1234, productId: 0x5678);
foreach (var device in devices)
{
Console.WriteLine($"{device.PortName} — {device.FriendlyName} (VID={device.VendorId}, PID={device.ProductId})");
device.Dispose();
}
Open, write, read (USB CDC)
USB CDC devices typically don't require PortSettings — the actual line rate is negotiated over the USB control interface, and the default 8N1 configuration works out of the box:
using Anp.Serial.Win32;
// From discovery, config file, or any other source
var device = new SerialDevice(@"\\?\USB#VID_1234&PID_5678#serial#{...}");
device.Open();
// No PortSettings needed for USB CDC — just start talking
await device.WriteLineAsync("HELLO");
string response = await device.ReadLineAsync(timeout: TimeSpan.FromSeconds(5));
device.Close();
device.Dispose();
Open, write, read (USB-to-serial adapter)
For USB-to-serial adapters and physical RS232 ports, configure PortSettings to match the target device:
var device = new SerialDevice(@"\\?\FTDIBUS#VID_0403+PID_6001+...#{...}");
device.Open();
device.PortSettings = new PortSettings
{
BaudRate = 115200,
DataBits = 8,
Parity = Parity.None,
StopBits = StopBits.One
};
await device.WriteLineAsync("HELLO");
string response = await device.ReadLineAsync(timeout: TimeSpan.FromSeconds(5));
device.Close();
device.Dispose();
Monitor hot-plug events
using Anp.Serial.Win32;
using (var watcher = new SerialWatcher(vendorId: 0x1234))
{
watcher.DeviceArrived += (s, e) => Console.WriteLine($"Connected: {e.DevicePath}");
watcher.DeviceRemoved += (s, e) => Console.WriteLine($"Disconnected: {e.DevicePath}");
watcher.StartWatching();
Console.ReadLine(); // keep alive
}
Subclass for protocol initialization
public class MyDevice : SerialDevice
{
public MyDevice(string path) : base(path) { }
protected override void OnOpened()
{
// Device is open — send handshake, configure settings, etc.
PortSettings = new PortSettings { BaudRate = 9600, DataBits = 8 };
WriteLineAsync("INIT").GetAwaiter().GetResult();
}
protected override void OnClosing()
{
// Device is about to close — send goodbye, flush, etc.
WriteLineAsync("BYE").GetAwaiter().GetResult();
}
}
// Use with discovery
var devices = SerialDiscovery.GetDevices<MyDevice>(path => new MyDevice(path));
API Overview
SerialDevice
| Member | Description |
|---|---|
Open() / Close() |
Open and close the device for I/O |
IsOpen |
Whether the device is currently open |
IsOpenChanged |
Event raised after open/close transitions |
DevicePath |
Device interface path (symbolic link) |
PortName |
COM port name (e.g. COM3), or null |
FriendlyName |
Windows device name (e.g. USB Serial Device (COM3)), or null |
VendorId / ProductId |
USB IDs parsed from the device path, or null |
PortSettings |
Get/set baud rate, data bits, parity, stop bits |
PortTimeouts |
(Advanced) Get/set driver-level COM port timeouts — reserve for edge cases where the default return-immediately behavior doesn't suit the driver |
DtrEnable / RtsEnable |
DTR and RTS signal control |
Encoding |
Byte encoding for text I/O (default: ASCII) |
NewLine |
Delimiter for ReadLineAsync / WriteLineAsync (default: \n) |
ReadAsync(byte[], int, int) |
Read bytes into a buffer |
ReadAsync(int) |
Read up to N bytes, return as byte[] |
ReadExactAsync(int) |
Read exactly N bytes (loops until full) |
WriteAsync(byte[], int, int) |
Write bytes from a buffer |
WriteAsync(byte[]) |
Write an entire byte array |
ReadTextAsync(int) |
Read bytes and decode as string |
ReadToAsync(string) |
Read until delimiter is found |
ReadLineAsync() |
Read until NewLine delimiter |
WriteTextAsync(string) |
Encode and write a string |
WriteLineAsync(string) |
Write a string followed by NewLine |
GetModemStatus() |
Read CTS, DSR, Ring, RLSD signals |
Purge(bool, bool) |
(Advanced) Clear receive and/or transmit buffers |
OnOpened() / OnClosing() |
Virtual hooks for subclass initialization |
All async methods accept an optional TimeSpan? timeout parameter (default: 2 seconds).
SerialDiscovery
| Member | Description |
|---|---|
GetDevices(vendorId?, productId?) |
Discover and return SerialDevice instances |
GetDevices<T>(factory, vendorId?, productId?) |
Discover with custom factory for derived types |
GetDevicePaths(vendorId?, productId?) |
Return device interface paths only |
SerialWatcher
| Member | Description |
|---|---|
DeviceArrived |
Event: a matching COM port device was connected |
DeviceRemoved |
Event: a matching COM port device was disconnected |
StartWatching() / StopWatching() |
Begin/end PnP monitoring (Windows 8+) |
Diagnostics
SerialDiag.Error += (sender, error) =>
{
Console.WriteLine($"[{error.Member}] {error.Message} {error.Exception}");
};
Requirements
- .NET Framework 4.8, .NET Standard 2.0, or .NET 8+ (Windows)
- Windows Vista or later (discovery and I/O)
- Windows 8 or later (SerialWatcher hot-plug monitoring)
License
See LICENSE in the repository root.
Links
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net8.0-windows7.0 is compatible. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 is compatible. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.8
- No dependencies.
-
.NETStandard 2.0
- Microsoft.Win32.Registry (>= 5.0.0)
-
net8.0-windows7.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 97 | 3/29/2026 |