Skip to content

Repository files navigation

PortLens

A lens on every port.

PortLens is a native Windows app that tells you, in plain English, what every USB port, cable, charger and monitor on your machine is actually doing — and why something is slower than you expected.

License: MIT .NET 10 WinUI 3 Windows 10/11 Tests

The Ports page — every physical socket, grouped by the box that owns it


Why this exists

You plug a 40 Gbps SSD into a 40 Gbps port with the cable that came in the box, and it copies at 5 Gbps. Device Manager says the device is working properly. Task Manager says the disk is barely busy. Nothing is broken, so nothing tells you anything.

macOS users have whatcable, which reads IOKit and answers the question. Windows has had no equivalent. PortLens is that equivalent, and then some — because the place Windows hurts most is not one cable, it is a dock with three monitors, eleven devices and one shared bandwidth budget that nobody will show you.

Left 2 · USB-C   (USB 3.2 Gen 2)  5 of 10 Gbps          ⚠ Cable is the limit
Samsung T7 Shield SSD — supports 10 Gbps
████████████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░  50%
⚠ The port does 40 Gbps and the drive reports 10 Gbps, but the link came up at
  5 Gbps — that pattern points at the cable.                        [inferred]

What it does

🔌 A physical map of every socket

Every connector on the machine, grouped and colour-coded by the box that owns it — this PC, the dock, the hub hiding behind the dock — showing what is plugged in, how fast it negotiated, and what it draws.

Windows reports the USB 2 and USB 3 halves of a single Type-C socket as two unrelated ports. PortLens puts them back together, then chases each device down the tree so a webcam three hops deep still says which socket on which box it ultimately hangs off.

Each port gets a verdict: negotiated speed versus what the device says it can do, with the bottleneck attributed to the cable, the port, the hub or the device. Real faults Windows reports — overcurrent, insufficient power, insufficient bandwidth, failed enumeration — are surfaced as facts, not buried in an error code.


🖥️ Displays, and the socket each one really uses

The Displays page

Every monitor with its connection type, the physical socket it hangs off, its current mode against the maximum in its EDID, and an explanation of the gap. DisplayLink monitors — the ones that are not wired to your GPU at all — are traced back through the dock to the port the dock is plugged into, and labelled as compressed-over-USB so you stop blaming the graphics driver.


🔗 Chain planner — model a daisy chain before you buy it

The Chain planner, showing a chain that runs out of bandwidth and what would fix it

This is the part no other Windows tool does.

Pick a link rate, a cable mode and whether DSC is allowed, and PortLens computes the real DisplayPort MST budget hop by hop: pixel clock, bits per component, CVT-RB2 blanking, compression, the lane trade-off a USB-C port makes when it carries USB 3 alongside video.

When the chain does not fit, it does not just say so — it tells you exactly what to change, ranked:

Remedy Effect
Enable DSC Visually lossless compression, roughly 3×
Drop to 8-bit colour Frees ~25 % of the budget
Step the heaviest monitor down 144 → 120 → 100 → 90 → 75 → 60 Hz
Switch the port to 4-lane DisplayPort Doubles video, drops USB to 480 Mbps
Give a monitor its own port Full link each, no compression needed

Start from "Load my current setup" to model what you own, or change the pickers to model the dock you are about to order.


⚡ Power, measured rather than guessed

The Power page

Real charge and discharge wattage, voltage, current, pack health, and a per-hub USB current budget grouped by the box each bus belongs to — so you can see the unpowered hub that is quietly rationing 500 mA between four devices.


🧠 A device database that knows what your hardware is rated for

The Devices page

Knowing a dock negotiated 5 Gbps is only half an answer. PortLens ships a database of docks, hubs, display adapters and controllers, so it can compare what a device is doing against what it is for. Anything it does not recognise is still reported honestly — and listed with its ID so you can teach PortLens about it in your own devices.json.


🔔 Lives in the tray and watches while you work

The tray flyout

Plug something in and PortLens rescans. If the device came up below its own capability, you get a notification while the cable is still in your hand — which is the only moment the information is actually useful.

Single click the tray icon for the flyout above: the sockets that need attention first, everything healthy and empty folded into one quiet line, then the displays — each row a shortcut into the matching page. Double click opens the window; right click gives the menu. Closing the window hides to the tray rather than exiting, because the point of the icon is to keep watching.


📄 One-click report for the support ticket

The Report page

HTML or JSON covering every port, device, display, budget and verdict — for a support ticket, an RMA, or a forum thread where someone insists the cable cannot possibly be the problem. The CLI produces the same thing for scripts.

Record a fixture captures the whole machine to a file. portlens --replay recomputes everything from that capture — which is how hardware you do not own gets debugged, and how the test suite runs with no exotic hardware attached.


What it cannot do, and says so

Windows does not expose USB Power Delivery contracts or cable e-marker data to user-mode applications. UCSI lives in the kernel and the OS surfaces only cooked results. There is no API that will tell you a cable is rated for 40 Gbps.

So PortLens infers what it can from what Windows does expose, and labels every derived conclusion [inferred] and every direct reading [measured]. It will never invent a cable rating, and a blank answer from the deep hardware probe is reported as a normal result rather than dressed up as a finding.

If you want a tool that confidently makes things up, this is not it.


Running it

dotnet build src\PortLens.App\PortLens.App.csproj -r win-x64
.\src\PortLens.App\bin\Debug\net10.0-windows10.0.19041.0\win-x64\PortLens.App.exe

No admin rights, no driver, no service, no telemetry. Pass --tray to start quietly in the notification area.

Building on Arm64? Swap win-x64 for win-arm64. Build the individual .csproj files rather than the solution — the SDK rejects a RID-specific build of the solution as a whole.

CLI

portlens --ports --displays --power    # the same engine, in a terminal
portlens --chain                       # daisy-chain budget and remedies
portlens --devices                     # what the device database recognised
portlens --deep                        # what this machine will and will not tell us
portlens --html --out report.html      # shareable report
portlens --record capture.json         # capture this machine
portlens --replay capture.json         # recompute everything from a capture

MSIX

.\tools\build-msix.ps1                 # needs the Windows SDK; test-signs by default

The unpackaged build is the default, because a tool you reach for when a dock is misbehaving should be a folder you can copy onto a USB stick. The package exists for the Store, for winget, and for identity-backed toast notifications.


How it is built

Project What lives there
PortLens.Core Domain model, providers, inference rules, bandwidth maths, chain planner, device database, fixtures
PortLens.App WinUI 3 shell: seven pages, tray icon, tray flyout, notifications
PortLens.Cli Console front-end over the same engine
PortLens.Tests xunit — 79 tests, all headless, all fixture-driven
tools make-icon.ps1 draws the icon and tile assets; build-msix.ps1 packages
docs ui-mockup.html — the clickable mockup the XAML was built from

Interop is hand-written DllImport rather than CsWin32, deliberately: the USB IOCTL structs need predictable layout, and two of them behave in ways a generator would not know about.

Notes from the hardware

  • IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 fails silently unless the caller sets the Usb300 protocol bit on the input buffer.
  • The Speed field is unreliable on some xHCI drivers, so the EX_V2 capability flags win.
  • Dock firmware often fills in companion-port data for port 1 only, or not at all, so socket identity has to be recovered from the shape of the device tree.
  • Physical port location comes from ACPI _PLD and is not consistently surfaced, so the port map degrades to a logical grouping rather than inventing a chassis diagram it cannot verify.

Contributing

The most valuable contribution is a fixture from hardware nobody here owns. Run portlens --record capture.json, check that you are happy with what it contains, and open an issue with it attached — Thunderbolt docks, UHBR monitors, USB4 enclosures and anything with a surprising topology are all welcome.

Second most valuable: entries for devices.json, so more hardware gets compared against its real rating instead of only being described.


Licence

MIT.

Inspired by whatcable by Darryl Morley, which answered this question on macOS first.

About

A lens on every port. Native Windows app that explains what every USB port, cable, charger and monitor is actually doing - with a DisplayPort daisy-chain planner that tells you what to change when a chain won't fit.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages