rs2322tcp/internal/client/virtual_port_validation_windows.go

132 lines
3 KiB
Go

//go:build windows
/*
* ============================================================================
* Projekt.....: rs2322tcp
* Datei.......: internal/client/virtual_port_validation_windows.go
* Copyright (C) 2026 Dieter Lang
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Beschreibung:
* Windows-spezifische Ermittlung der lokal vorhandenen seriellen
* Schnittstellen für rs2322tcp.
*
* Unter Windows werden virtuelle Ports als COMxxx-Schnittstellen
* bereitgestellt.
*
* Diese Implementierung prüft den konfigurierten Portbereich und liefert
* die tatsächlich vorhandenen COM-Schnittstellen zurück.
*
* Die Funktion verändert keine Geräte und installiert keine Treiber.
* ============================================================================
*/
package client
import (
"fmt"
"golang.org/x/sys/windows/registry"
"git.lang-dieter.de/rs2322tcp/internal/config"
)
///////////////////////////////////////////////////////////////////////////////
// Existing virtual ports
///////////////////////////////////////////////////////////////////////////////
// existingVirtualPortLinks returns all COM ports in the configured range
// that are currently registered as serial ports in Windows.
//
// The Windows serial-port information is obtained from the Plug-and-Play
// device registry. No device is created, modified, or removed.
func existingVirtualPortLinks(
cfg config.VirtualPortRangeConfig,
) ([]string, error) {
key, err := registry.OpenKey(
registry.LOCAL_MACHINE,
`HARDWARE\DEVICEMAP\SERIALCOMM`,
registry.READ,
)
if err != nil {
return nil, fmt.Errorf(
"open Windows serial-port registry: %w",
err,
)
}
defer key.Close()
values, err := key.ReadValueNames(0)
if err != nil {
return nil, fmt.Errorf(
"read Windows serial-port registry: %w",
err,
)
}
available := make([]string, 0)
for _, valueName := range values {
portName, _, err := key.GetStringValue(valueName)
if err != nil {
continue
}
number, ok := windowsCOMPortNumber(portName)
if !ok {
continue
}
if number < cfg.First || number > cfg.Last {
continue
}
available = append(
available,
portName,
)
}
return available, nil
}
///////////////////////////////////////////////////////////////////////////////
// COM port number
///////////////////////////////////////////////////////////////////////////////
// windowsCOMPortNumber extracts the numeric part of a Windows COM port name.
//
// Examples:
//
// COM1 -> 1
// COM10 -> 10
//
// The comparison is case-insensitive.
func windowsCOMPortNumber(portName string) (int, bool) {
if len(portName) < 4 {
return 0, false
}
if (portName[0] != 'C' && portName[0] != 'c') ||
(portName[1] != 'O' && portName[1] != 'o') ||
(portName[2] != 'M' && portName[2] != 'm') {
return 0, false
}
number := 0
for _, char := range portName[3:] {
if char < '0' || char > '9' {
return 0, false
}
number = number*10 + int(char-'0')
}
if number == 0 {
return 0, false
}
return number, true
}