132 lines
3 KiB
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
|
|
}
|