Skip to content

andrewpono/Devices.Win32

Repository files navigation

Devices.Win32

Managed Windows device libraries for .NET Framework 4.8, .NET Standard 2.0, and .NET 8+. Focused on basic communication with custom USB devices that use Windows "driverless" I/O — Arduino boards, bootloaders, digital scales, lab instruments, LED controllers, and similar embedded hardware.

Two independent NuGet packages — Serial (COM port) and HID (Human Interface Device) — built on a shared core of Win32 P/Invoke, device discovery, and async I/O infrastructure.

Why not System.IO.Ports.SerialPort?

SerialPort was designed for fixed RS232 ports in the .NET 1.1 era and has well-known problems with modern USB devices:

Problem System.IO.Ports.SerialPort Anp.Serial.Win32
Device disconnect Throws on a background thread, often crashes the process or leaves the port in an unrecoverable state Raises SerialDisconnectedException on the calling thread; device transitions cleanly to closed
Discovery GetPortNames() returns bare COM names with no metadata, no VID/PID, and stale entries for disconnected devices SerialDiscovery.GetDevices() with VID/PID filtering, FriendlyName, and stable device paths
Hot-plug None SerialWatcher with arrival/removal events (Windows 8+)
Async I/O BaseStream only; no per-call timeout, no cancellation ReadAsync / WriteAsync with TimeSpan? timeout on every call
Device identity COM port number (unstable across re-plug) Device interface path (stable, unique, assigned by OS)
Event model DataReceived fires on a threadpool thread with no backpressure; easy to miss data or deadlock Pull-based async reads — caller controls the read loop and timing
USB CDC Requires manual baud/parity configuration even though CDC ignores it Works out of the box — no PortSettings needed for CDC devices

Why not raw CreateFile + ReadFile for HID?

Windows HID devices don't need a kernel driver — the OS provides one automatically — but communicating with them still requires juggling hid.dll for metadata, kernel32.dll for I/O, overlapped structures for async, CancelIoEx for timeouts, and careful handle lifecycle. Anp.Usb.Hid.Win32 wraps all of that into a simple open/read/write/close API with:

  • Device disconnect handled cleanly — HidDisconnectedException on the calling thread, not a corrupted handle
  • Per-call timeouts — no need to manage your own OVERLAPPED + WaitForSingleObject + CancelIoEx dance
  • Discovery and hot-plug — find devices by VID/PID, get notified on arrival/removal
  • Report abstraction — work with HidReport (report ID + data) or raw byte arrays

Libraries

Package Transport Typical Devices
NuGet Anp.Serial.Win32 COM port (USB CDC, USB-to-serial, RS232, Bluetooth) Arduino (CDC), GPS modules, barcode scanners, FTDI/CH340/CP2102 adapters
NuGet Anp.Usb.Hid.Win32 USB HID Custom HID devices, bootloaders, digital scales, LED controllers, relay boards

Both libraries share a common design:

  • Discovery — enumerate devices by interface GUID, optionally filtered by USB VID/PID
  • Hot-plug monitoring — real-time device arrival/removal notifications (Windows 8+)
  • Async I/O with per-call timeouts — every read/write accepts a TimeSpan? timeout (default 2 s)
  • Graceful disconnect handling — mid-I/O device removal throws a typed exception, not corruption
  • SubclassableOnOpened() / OnClosing() hooks for protocol init and teardown
  • Diagnostics — subscribe to library-level error events for logging and troubleshooting
  • Zero mandatory NuGet dependencies on .NET Framework 4.8 — only Windows system DLLs via P/Invoke

Quick Start

Serial (USB CDC — no port settings needed)

using Anp.Serial.Win32;

var devices = SerialDiscovery.GetDevices(vendorId: 0x2341); // Arduino
using (var device = devices[0])
{
    device.Open();

    await device.WriteLineAsync("HELLO");
    string response = await device.ReadLineAsync(timeout: TimeSpan.FromSeconds(5));
    Console.WriteLine(response);

    device.Close();
}

HID

using Anp.Usb.Hid.Win32;

var devices = HidDiscovery.GetDevices(vendorId: 0x1234, productId: 0x0001);
using (var device = devices[0])
{
    device.Open();

    await device.WriteOutputReportAsync(new HidReport(0x00, new byte[] { 0x01, 0x02 }));
    HidReport report = await device.ReadInputReportAsync(timeout: TimeSpan.FromSeconds(5));
    Console.WriteLine(BitConverter.ToString(report.Data));

    device.Close();
}

Samples

Runnable sample projects are in the samples/ directory:

Sample Description
Serial.BasicDiscovery Enumerate devices, print metadata
Serial.ReadWrite Open, write, read (CDC and adapter patterns)
Serial.HotPlug Monitor device arrival/removal in real time
Serial.LiveMonitor Continuous read loop with infinite timeout
Serial.DerivedDevice Subclass with protocol hooks
Hid.BasicDiscovery Enumerate HID devices, print metadata
Hid.ReadWrite Open, write/read reports
Hid.HotPlug Monitor HID device arrival/removal
Hid.LiveMonitor Continuous report read loop
Hid.DerivedDevice Subclass with protocol hooks

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 (hot-plug monitoring)

Project Structure

src/
  Serial.Win32/           COM port serial device library
  Usb.Hid.Win32/          USB HID device library
  Devices.Win32.Core/     Shared project (compiled into both libraries)
samples/
  Serial.*/               Serial sample projects
  Hid.*/                  HID sample projects

Related

  • PnpDeviceToolkit — external device discovery and enumeration tool; device paths from PnpDeviceToolkit can be used directly with SerialDevice and HidDevice constructors.

License

MIT

About

Managed Windows device libraries for .NET Framework 4.8, .NET Standard 2.0, and .NET 8+. Focused on basic communication with custom USB devices that use Windows "driverless" I/O — Arduino boards, bootloaders, digital scales, lab instruments, LED controllers, and similar embedded hardware.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors