OK
This commit is contained in:
@@ -2,8 +2,8 @@ import concurrent.futures
|
||||
import csv
|
||||
import ipaddress
|
||||
import json
|
||||
import unicodedata
|
||||
import re
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
from dataclasses import asdict, dataclass, field
|
||||
@@ -75,7 +75,6 @@ class DeviceDiscoveryOptions:
|
||||
class DeviceInfo:
|
||||
ip: str
|
||||
mac: str
|
||||
hostname: str
|
||||
vendor: str
|
||||
adapter: str
|
||||
interface_index: str
|
||||
@@ -112,6 +111,12 @@ class DeviceDiscovery:
|
||||
adapters = self._active_adapters(self.network.get_network_info())
|
||||
return [ALL_ADAPTERS] + [adapter["name"] for adapter in adapters]
|
||||
|
||||
def get_adapter_choices_and_default_range(self) -> tuple[list[str], str, str]:
|
||||
adapters = self._active_adapters(self.network.get_network_info())
|
||||
choices = [ALL_ADAPTERS] + [adapter["name"] for adapter in adapters]
|
||||
default_adapter = preferred_discovery_adapter(adapters)
|
||||
return choices, adapter_to_safe_range(default_adapter) if default_adapter else "", default_adapter.get("name", "") if default_adapter else ""
|
||||
|
||||
def default_scan_range(self, adapter_name: str = ALL_ADAPTERS) -> str:
|
||||
adapters = self._select_adapters(adapter_name)
|
||||
if not adapters:
|
||||
@@ -136,7 +141,7 @@ class DeviceDiscovery:
|
||||
self.last_summary = ""
|
||||
self.output(
|
||||
f"开始局域网设备发现: {adapter_name or ALL_ADAPTERS},"
|
||||
f"目标 {len(targets)} 个,并发 {discovery_options.workers},超时 {discovery_options.timeout_ms}ms\n",
|
||||
f"目标 {len(targets)} 个,并发 {min(discovery_options.workers, 24, max(1, len(targets)))},超时 {discovery_options.timeout_ms}ms\n",
|
||||
"muted",
|
||||
)
|
||||
self.status(self._status("扫描中", adapter_name or ALL_ADAPTERS, describe_targets(targets), len(targets), 0, 0, 0))
|
||||
@@ -196,13 +201,14 @@ class DeviceDiscovery:
|
||||
def scan_targets(self, targets: list[str], options: DeviceDiscoveryOptions, started: float) -> dict[str, dict]:
|
||||
results = {}
|
||||
completed = 0
|
||||
workers = min(options.workers, 24, max(1, len(targets)))
|
||||
|
||||
def task(ip: str) -> dict:
|
||||
if self.stop_event.is_set():
|
||||
return {"ip": ip, "ok": False, "rtt": 0.0}
|
||||
return ping_once(ip, options.timeout_ms)
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=options.workers) as executor:
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as executor:
|
||||
futures = {executor.submit(task, ip): ip for ip in targets}
|
||||
for future in concurrent.futures.as_completed(futures):
|
||||
completed += 1
|
||||
@@ -241,7 +247,6 @@ class DeviceDiscovery:
|
||||
devices[local_ip] = DeviceInfo(
|
||||
ip=local_ip,
|
||||
mac=normalize_mac(adapter.get("mac", "")),
|
||||
hostname=resolve_hostname(local_ip),
|
||||
vendor=vendor_name(adapter.get("mac", "")),
|
||||
adapter=adapter.get("name", ""),
|
||||
interface_index=str(adapter.get("interface_index", "")),
|
||||
@@ -270,7 +275,6 @@ class DeviceDiscovery:
|
||||
devices[ip] = DeviceInfo(
|
||||
ip=ip,
|
||||
mac=mac,
|
||||
hostname=resolve_hostname(ip),
|
||||
vendor=vendor_name(mac),
|
||||
adapter=adapter.get("name", ""),
|
||||
interface_index=str(adapter.get("interface_index", "")),
|
||||
@@ -285,20 +289,15 @@ class DeviceDiscovery:
|
||||
adapter = adapter_for_ip(ip, adapters)
|
||||
if not adapter:
|
||||
continue
|
||||
# Ping succeeded but no valid ARP MAC was visible; keep it out of
|
||||
# the asset list unless it is a known local/gateway address.
|
||||
if ip not in gateways and ip not in local_ips:
|
||||
continue
|
||||
devices[ip] = DeviceInfo(
|
||||
ip=ip,
|
||||
mac="",
|
||||
hostname=resolve_hostname(ip),
|
||||
vendor="未知",
|
||||
adapter=adapter.get("name", ""),
|
||||
interface_index=str(adapter.get("interface_index", "")),
|
||||
latency_ms=ping.get("rtt", 0.0),
|
||||
method="在线",
|
||||
note="网关" if ip in gateways else "本机",
|
||||
note="网关" if ip in gateways else "本机" if ip in local_ips else "未读取到 MAC",
|
||||
)
|
||||
|
||||
return list(devices.values())
|
||||
@@ -346,7 +345,7 @@ Get-NetNeighbor -AddressFamily IPv4 | Select-Object ifIndex,IPAddress,LinkLayerA
|
||||
|
||||
def build_scan_targets(self, adapters: list[dict], options: DeviceDiscoveryOptions) -> list[str]:
|
||||
if options.scan_range:
|
||||
return parse_target_range(options.scan_range, options.max_hosts)
|
||||
return filter_targets_for_adapters(parse_target_range(options.scan_range, options.max_hosts), adapters)
|
||||
targets = []
|
||||
for adapter in adapters:
|
||||
targets.extend(parse_target_range(adapter_to_safe_range(adapter), options.max_hosts))
|
||||
@@ -361,7 +360,7 @@ Get-NetNeighbor -AddressFamily IPv4 | Select-Object ifIndex,IPAddress,LinkLayerA
|
||||
def export_results(self, path: str) -> None:
|
||||
if not self.last_results:
|
||||
raise RuntimeError("还没有可导出的设备发现结果")
|
||||
fields = ["ip", "mac", "hostname", "vendor", "adapter", "interface_index", "latency_ms", "method", "note", "checked_at"]
|
||||
fields = ["ip", "mac", "vendor", "adapter", "interface_index", "latency_ms", "method", "note", "checked_at"]
|
||||
with open(path, "w", newline="", encoding="utf-8-sig") as file:
|
||||
writer = csv.DictWriter(file, fieldnames=fields)
|
||||
writer.writeheader()
|
||||
@@ -372,7 +371,7 @@ Get-NetNeighbor -AddressFamily IPv4 | Select-Object ifIndex,IPAddress,LinkLayerA
|
||||
if not self.last_results:
|
||||
return self.last_summary
|
||||
return "\n".join(
|
||||
f"{row.get('ip', '')}\t{row.get('mac', '')}\t{row.get('hostname', '')}\t{row.get('vendor', '')}\t{row.get('method', '')}"
|
||||
f"{row.get('ip', '')}\t{row.get('mac', '')}\t{row.get('vendor', '')}\t{row.get('method', '')}"
|
||||
for row in sorted(self.last_results, key=lambda item: ip_sort_key(item.get("ip", "")))
|
||||
)
|
||||
|
||||
@@ -389,7 +388,8 @@ Get-NetNeighbor -AddressFamily IPv4 | Select-Object ifIndex,IPAddress,LinkLayerA
|
||||
adapters = self._active_adapters(self.network.get_network_info())
|
||||
if not adapter_name or adapter_name == ALL_ADAPTERS:
|
||||
return adapters
|
||||
return [adapter for adapter in adapters if adapter.get("name") == adapter_name]
|
||||
selected = normalize_adapter_name(adapter_name)
|
||||
return [adapter for adapter in adapters if normalize_adapter_name(adapter.get("name", "")) == selected]
|
||||
|
||||
def _active_adapters(self, adapters: list[dict]) -> list[dict]:
|
||||
active = []
|
||||
@@ -397,7 +397,7 @@ Get-NetNeighbor -AddressFamily IPv4 | Select-Object ifIndex,IPAddress,LinkLayerA
|
||||
if not adapter.get("ipv4"):
|
||||
continue
|
||||
status = str(adapter.get("status", "")).lower()
|
||||
if "disconnect" in status or "断开" in status:
|
||||
if any(value in status for value in ("disconnect", "disabled", "not present", "断开", "禁用")):
|
||||
continue
|
||||
active.append(adapter)
|
||||
return active
|
||||
@@ -419,7 +419,7 @@ Get-NetNeighbor -AddressFamily IPv4 | Select-Object ifIndex,IPAddress,LinkLayerA
|
||||
|
||||
def ping_once(ip: str, timeout_ms: int) -> dict:
|
||||
try:
|
||||
result = run_hidden(["ping", ip, "-n", "1", "-w", str(timeout_ms)], timeout=max(2, timeout_ms / 1000 + 2))
|
||||
result = run_hidden(["ping", ip, "-n", "1", "-w", str(timeout_ms)], timeout=max(1.2, timeout_ms / 1000 + 0.8))
|
||||
output = result.stdout
|
||||
if re.search(r"\bTTL=", output, re.IGNORECASE):
|
||||
match = re.search(r"(?:time|时间)[=<]?\s*(\d+(?:\.\d+)?)\s*(?:ms|毫秒)", output, re.IGNORECASE)
|
||||
@@ -480,6 +480,49 @@ def adapter_to_safe_range(adapter: dict) -> str:
|
||||
return ".".join(parts[:3]) + ".0/24" if len(parts) == 4 else ""
|
||||
|
||||
|
||||
def preferred_discovery_adapter(adapters: list[dict]) -> Optional[dict]:
|
||||
if not adapters:
|
||||
return None
|
||||
for adapter in adapters:
|
||||
if adapter.get("gateway"):
|
||||
return adapter
|
||||
for adapter in adapters:
|
||||
prefix = adapter.get("prefix_length")
|
||||
try:
|
||||
if prefix not in ("", None) and int(prefix) <= 24:
|
||||
return adapter
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
return adapters[0]
|
||||
|
||||
|
||||
def filter_targets_for_adapters(targets: list[str], adapters: list[dict]) -> list[str]:
|
||||
return [target for target in targets if target_in_adapter_network(target, adapters)]
|
||||
|
||||
|
||||
def target_in_adapter_network(target: str, adapters: list[dict]) -> bool:
|
||||
try:
|
||||
address = ipaddress.ip_address(target)
|
||||
except ValueError:
|
||||
return False
|
||||
for adapter in adapters:
|
||||
local_ip = adapter.get("ipv4", "")
|
||||
if not local_ip:
|
||||
continue
|
||||
prefix = adapter.get("prefix_length") or netmask_to_prefix(adapter.get("netmask", "")) or 24
|
||||
try:
|
||||
if address in ipaddress.ip_network(f"{local_ip}/{prefix}", strict=False):
|
||||
return True
|
||||
except Exception:
|
||||
continue
|
||||
return False
|
||||
|
||||
|
||||
def normalize_adapter_name(value: str) -> str:
|
||||
normalized = unicodedata.normalize("NFKC", str(value or ""))
|
||||
return re.sub(r"[\s\u200b-\u200d\ufeff]+", " ", normalized).strip().casefold()
|
||||
|
||||
|
||||
def netmask_to_prefix(netmask: str) -> Optional[int]:
|
||||
if not netmask:
|
||||
return None
|
||||
@@ -499,27 +542,6 @@ def normalize_neighbor(item: dict) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def resolve_hostname(ip: str) -> str:
|
||||
try:
|
||||
old_timeout = socket.getdefaulttimeout()
|
||||
socket.setdefaulttimeout(0.8)
|
||||
try:
|
||||
return socket.gethostbyaddr(ip)[0]
|
||||
finally:
|
||||
socket.setdefaulttimeout(old_timeout)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
result = run_hidden(["nbtstat", "-A", ip], timeout=2)
|
||||
for line in result.stdout.splitlines():
|
||||
match = re.match(r"\s*([^\s<]+)\s+<00>\s+UNIQUE", line, re.IGNORECASE)
|
||||
if match:
|
||||
return match.group(1).strip()
|
||||
except Exception:
|
||||
pass
|
||||
return ""
|
||||
|
||||
|
||||
def vendor_name(mac: str) -> str:
|
||||
normalized = normalize_mac(mac)
|
||||
if len(normalized) < 8:
|
||||
|
||||
@@ -68,7 +68,7 @@ class DnsDiagnostic:
|
||||
self.done = done or (lambda: None)
|
||||
self.status = status or (lambda _stats: None)
|
||||
self.result = result or (lambda _row: None)
|
||||
self.network = NetworkManager(lambda _text, _tag=None: None)
|
||||
self.network = NetworkManager(output)
|
||||
self.stop_event = threading.Event()
|
||||
self.worker = None
|
||||
self.last_results: list[dict] = []
|
||||
@@ -76,14 +76,35 @@ class DnsDiagnostic:
|
||||
self.local_dns_servers: set[str] = set()
|
||||
|
||||
def get_adapter_choices(self) -> list[str]:
|
||||
adapters = self._active_adapters(self.network.get_network_info())
|
||||
adapters = self.network.get_network_info()
|
||||
return [ALL_ADAPTERS] + [adapter["name"] for adapter in adapters]
|
||||
|
||||
def get_adapter_choices_and_default_dns(self) -> tuple[list[str], str]:
|
||||
adapters = self.network.get_network_info()
|
||||
choices = [ALL_ADAPTERS] + [adapter["name"] for adapter in adapters]
|
||||
servers = collect_adapter_dns(self._active_adapters(adapters))
|
||||
default_dns = ",".join(list(dict.fromkeys(servers + PUBLIC_DNS_SERVERS)))
|
||||
return choices, default_dns
|
||||
|
||||
def default_dns_servers(self, adapter_name: str = ALL_ADAPTERS) -> str:
|
||||
adapters = self._select_adapters(adapter_name)
|
||||
adapters = self._select_dns_source_adapters(adapter_name)
|
||||
servers = collect_adapter_dns(adapters)
|
||||
return ",".join(list(dict.fromkeys(servers + PUBLIC_DNS_SERVERS)))
|
||||
|
||||
def repair_abnormal_dns(self, adapter_name: str = ALL_ADAPTERS) -> dict:
|
||||
if self.is_running():
|
||||
raise RuntimeError("DNS 诊断正在运行,请先停止当前任务")
|
||||
|
||||
adapters = self._select_repair_adapters(adapter_name)
|
||||
if not adapters:
|
||||
raise ValueError("没有找到可修复的活动网卡")
|
||||
|
||||
servers = PUBLIC_DNS_SERVERS[:2]
|
||||
for adapter in adapters:
|
||||
self.network.set_dns_servers(adapter["name"], servers)
|
||||
self.network.flush_dns_cache()
|
||||
return {"adapters": [adapter["name"] for adapter in adapters], "servers": servers}
|
||||
|
||||
def start_diagnosis(self, adapter_name: str = ALL_ADAPTERS, options: Optional[dict] = None) -> None:
|
||||
if self.is_running():
|
||||
raise RuntimeError("DNS 诊断正在运行,请先停止当前任务")
|
||||
@@ -302,13 +323,25 @@ class DnsDiagnostic:
|
||||
return adapters
|
||||
return [adapter for adapter in adapters if adapter.get("name") == adapter_name]
|
||||
|
||||
def _select_dns_source_adapters(self, adapter_name: str) -> list[dict]:
|
||||
adapters = self.network.get_network_info()
|
||||
if not adapter_name or adapter_name == ALL_ADAPTERS:
|
||||
return self._active_adapters(adapters)
|
||||
return [adapter for adapter in adapters if adapter.get("name") == adapter_name]
|
||||
|
||||
def _select_repair_adapters(self, adapter_name: str) -> list[dict]:
|
||||
adapters = self._select_adapters(adapter_name)
|
||||
if adapter_name and adapter_name != ALL_ADAPTERS:
|
||||
return adapters
|
||||
return [adapter for adapter in adapters if adapter.get("gateway") or adapter.get("dns1") or adapter.get("dns2")]
|
||||
|
||||
def _active_adapters(self, adapters: list[dict]) -> list[dict]:
|
||||
active = []
|
||||
for adapter in adapters:
|
||||
if not adapter.get("ipv4"):
|
||||
continue
|
||||
status = str(adapter.get("status", "")).lower()
|
||||
if "disconnect" in status or "断开" in status:
|
||||
if any(value in status for value in ("disconnect", "disabled", "not present", "断开", "禁用")):
|
||||
continue
|
||||
active.append(adapter)
|
||||
return active
|
||||
|
||||
@@ -487,7 +487,7 @@ Get-NetNeighbor -AddressFamily IPv4 | Select-Object ifIndex,IPAddress,LinkLayerA
|
||||
if not adapter.get("ipv4"):
|
||||
continue
|
||||
status = str(adapter.get("status", "")).lower()
|
||||
if "disconnect" in status or "断开" in status:
|
||||
if any(value in status for value in ("disconnect", "disabled", "not present", "断开", "禁用")):
|
||||
continue
|
||||
active.append(adapter)
|
||||
return active
|
||||
|
||||
@@ -353,7 +353,7 @@ Get-NetNeighbor -AddressFamily IPv4 | Select-Object ifIndex,IPAddress,LinkLayerA
|
||||
if not adapter.get("ipv4"):
|
||||
continue
|
||||
status = str(adapter.get("status", "")).lower()
|
||||
if "disconnect" in status or "断开" in status:
|
||||
if any(value in status for value in ("disconnect", "disabled", "not present", "断开", "禁用")):
|
||||
continue
|
||||
active.append(adapter)
|
||||
return active
|
||||
|
||||
@@ -24,33 +24,55 @@ class NetworkManager:
|
||||
def get_network_info(self) -> list[dict]:
|
||||
script = r"""
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
$items = Get-NetIPConfiguration | ForEach-Object {
|
||||
$alias = $_.InterfaceAlias
|
||||
$index = $_.InterfaceIndex
|
||||
$adapter = Get-NetAdapter -InterfaceAlias $alias -ErrorAction SilentlyContinue
|
||||
$ipif = Get-NetIPInterface -InterfaceAlias $alias -AddressFamily IPv4 -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
$cim = Get-CimInstance Win32_NetworkAdapterConfiguration -Filter "IPEnabled=True" -ErrorAction SilentlyContinue | Where-Object { $_.InterfaceIndex -eq $index } | Select-Object -First 1
|
||||
$ipconfigs = @{}
|
||||
Get-NetIPConfiguration | ForEach-Object { $ipconfigs[[string]$_.InterfaceIndex] = $_ }
|
||||
|
||||
$ipifs = @{}
|
||||
Get-NetIPInterface -AddressFamily IPv4 | ForEach-Object {
|
||||
$key = [string]$_.InterfaceIndex
|
||||
if (-not $ipifs.ContainsKey($key)) { $ipifs[$key] = $_ }
|
||||
}
|
||||
|
||||
$cims = @{}
|
||||
Get-CimInstance Win32_NetworkAdapterConfiguration | ForEach-Object {
|
||||
if ($_.InterfaceIndex -ne $null) { $cims[[string]$_.InterfaceIndex] = $_ }
|
||||
}
|
||||
|
||||
$dnsMap = @{}
|
||||
Get-DnsClientServerAddress -AddressFamily IPv4 | ForEach-Object {
|
||||
$dnsMap[[string]$_.InterfaceIndex] = @($_.ServerAddresses)
|
||||
}
|
||||
|
||||
$items = Get-NetAdapter | Sort-Object Name | ForEach-Object {
|
||||
$adapter = $_
|
||||
$alias = $adapter.Name
|
||||
$index = $adapter.ifIndex
|
||||
$key = [string]$index
|
||||
$ipconfig = $ipconfigs[$key]
|
||||
$ipif = $ipifs[$key]
|
||||
$cim = $cims[$key]
|
||||
$dns = @($dnsMap[$key])
|
||||
[PSCustomObject]@{
|
||||
name = $alias
|
||||
description = $_.InterfaceDescription
|
||||
mac = if ($adapter) { $adapter.MacAddress } else { "" }
|
||||
status = if ($adapter) { [string]$adapter.Status } else { "" }
|
||||
link_speed = if ($adapter) { [string]$adapter.LinkSpeed } else { "" }
|
||||
description = $adapter.InterfaceDescription
|
||||
mac = $adapter.MacAddress
|
||||
status = [string]$adapter.Status
|
||||
link_speed = [string]$adapter.LinkSpeed
|
||||
interface_index = $index
|
||||
ipv4 = @($_.IPv4Address | Select-Object -ExpandProperty IPAddress)[0]
|
||||
ipv6 = @($_.IPv6Address | Select-Object -ExpandProperty IPAddress)
|
||||
prefix_length = @($_.IPv4Address | Select-Object -ExpandProperty PrefixLength)[0]
|
||||
gateway = @($_.IPv4DefaultGateway | Select-Object -ExpandProperty NextHop)[0]
|
||||
dns = @($_.DNSServer.ServerAddresses)
|
||||
ipv4 = @($ipconfig.IPv4Address | Select-Object -ExpandProperty IPAddress)[0]
|
||||
ipv6 = @($ipconfig.IPv6Address | Select-Object -ExpandProperty IPAddress)
|
||||
prefix_length = @($ipconfig.IPv4Address | Select-Object -ExpandProperty PrefixLength)[0]
|
||||
gateway = @($ipconfig.IPv4DefaultGateway | Select-Object -ExpandProperty NextHop)[0]
|
||||
dns = $dns
|
||||
dhcp_server = if ($cim) { $cim.DHCPServer } else { "" }
|
||||
dhcp_lease_obtained = if ($cim) { [string]$cim.DHCPLeaseObtained } else { "" }
|
||||
dhcp_lease_expires = if ($cim) { [string]$cim.DHCPLeaseExpires } else { "" }
|
||||
dhcp_enabled = if ($ipif) { [string]$ipif.Dhcp -eq "Enabled" } else { $false }
|
||||
dhcp_enabled = if ($ipif) { [string]$ipif.Dhcp -eq "Enabled" } elseif ($cim) { [bool]$cim.DHCPEnabled } else { $false }
|
||||
}
|
||||
}
|
||||
$items | ConvertTo-Json -Depth 5 -Compress
|
||||
"""
|
||||
result = run_hidden(["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script], timeout=15)
|
||||
result = run_hidden(["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script], timeout=30)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(result.stdout.strip() or "PowerShell 获取网卡信息失败")
|
||||
|
||||
@@ -60,6 +82,8 @@ $items | ConvertTo-Json -Depth 5 -Compress
|
||||
return self._get_network_info_from_ipconfig()
|
||||
|
||||
data = json.loads(output[json_start:])
|
||||
if data is None:
|
||||
return []
|
||||
if isinstance(data, dict):
|
||||
data = [data]
|
||||
|
||||
@@ -263,6 +287,54 @@ $items | ConvertTo-Json -Depth 5 -Compress
|
||||
|
||||
self.output("静态 IPv4 配置已应用\n", "success")
|
||||
|
||||
def set_dns_servers(self, name: str, dns_servers: list[str]) -> None:
|
||||
adapter_name = name.strip()
|
||||
if not adapter_name:
|
||||
raise ValueError("请选择网卡")
|
||||
|
||||
servers = [validate_ip(server) for server in dns_servers if str(server).strip()]
|
||||
if not servers:
|
||||
raise ValueError("请输入至少一个 DNS 服务器")
|
||||
|
||||
self._run_netsh(
|
||||
[
|
||||
"interface",
|
||||
"ip",
|
||||
"set",
|
||||
"dnsservers",
|
||||
f"name={adapter_name}",
|
||||
"source=static",
|
||||
f"address={servers[0]}",
|
||||
"index=1",
|
||||
]
|
||||
)
|
||||
for index, server in enumerate(servers[1:], start=2):
|
||||
self._run_netsh(["interface", "ip", "add", "dnsservers", f"name={adapter_name}", f"address={server}", f"index={index}"])
|
||||
self.output(f"已设置 DNS: {adapter_name} -> {', '.join(servers)}\n", "success")
|
||||
|
||||
def flush_dns_cache(self) -> None:
|
||||
result = run_hidden(["ipconfig", "/flushdns"], timeout=10)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(result.stdout.strip() or "刷新 DNS 缓存失败")
|
||||
self.output("已刷新 DNS 缓存\n", "success")
|
||||
|
||||
def set_adapter_enabled(self, name: str, enabled: bool) -> None:
|
||||
adapter_name = name.strip()
|
||||
if not adapter_name:
|
||||
raise ValueError("请选择网卡")
|
||||
|
||||
action = "启用" if enabled else "禁用"
|
||||
self.output(f"准备{action}网卡: {adapter_name}\n", "warning")
|
||||
command = "Enable-NetAdapter" if enabled else "Disable-NetAdapter"
|
||||
script = f"""
|
||||
$ErrorActionPreference = "Stop"
|
||||
{command} -Name {self._ps_quote(adapter_name)} -Confirm:$false
|
||||
"""
|
||||
result = run_hidden(["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script], timeout=20)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(result.stdout.strip() or f"{action}网卡失败,请确认已用管理员权限运行")
|
||||
self.output(f"网卡已{action}: {adapter_name}\n", "success")
|
||||
|
||||
def load_profiles(self) -> dict:
|
||||
if not os.path.exists(self.profiles_file):
|
||||
return {}
|
||||
@@ -301,3 +373,6 @@ $items | ConvertTo-Json -Depth 5 -Compress
|
||||
if result.returncode != 0:
|
||||
message = (result.stdout + result.stderr).strip()
|
||||
raise RuntimeError(message or "netsh 命令执行失败,请确认已用管理员权限运行")
|
||||
|
||||
def _ps_quote(self, value: str) -> str:
|
||||
return "'" + value.replace("'", "''") + "'"
|
||||
|
||||
Reference in New Issue
Block a user