fix(lite): hide tun network command windows

This commit is contained in:
RoyirinRosmontis
2026-09-19 04:36:54 +08:00
parent e406d614b7
commit d0e739eff1
+23 -13
View File
@@ -27,6 +27,16 @@ class TunBackendError(RuntimeError):
"""TUN 后端无法启动或停止。""" """TUN 后端无法启动或停止。"""
def _run_hidden(command, **kwargs):
"""运行 Windows 网络命令时隐藏控制台窗口。
源码通过 test.bat 启动时这些子进程复用已有控制台;Nuitka GUI 没有父控制台,
未设置 CREATE_NO_WINDOW 就会为每次 PowerShell 查询弹出一个窗口。
"""
kwargs.setdefault("creationflags", getattr(subprocess, "CREATE_NO_WINDOW", 0))
return subprocess.run(command, **kwargs)
class TunBackend: class TunBackend:
def __init__(self, user_dir: str | Path, engine_path: str | Path | None = None, def __init__(self, user_dir: str | Path, engine_path: str | Path | None = None,
log: Callable[[str], None] | None = None, log: Callable[[str], None] | None = None,
@@ -110,7 +120,7 @@ class TunBackend:
"Where-Object InterfaceAlias -ne 'RRPP-TUN' | " "Where-Object InterfaceAlias -ne 'RRPP-TUN' | "
"Sort-Object RouteMetric | Select-Object -First 1 -ExpandProperty InterfaceAlias)"] "Sort-Object RouteMetric | Select-Object -First 1 -ExpandProperty InterfaceAlias)"]
try: try:
result = subprocess.run(command, capture_output=True, text=True, timeout=5) result = _run_hidden(command, capture_output=True, text=True, timeout=5)
value = (result.stdout or "").strip().splitlines() value = (result.stdout or "").strip().splitlines()
if value and value[0].strip(): if value and value[0].strip():
return value[0].strip() return value[0].strip()
@@ -127,7 +137,7 @@ class TunBackend:
f"(Get-NetIPAddress -InterfaceAlias '{escaped}' -AddressFamily IPv4 " f"(Get-NetIPAddress -InterfaceAlias '{escaped}' -AddressFamily IPv4 "
"-AddressState Preferred | Select-Object -First 1 -ExpandProperty IPAddress)"] "-AddressState Preferred | Select-Object -First 1 -ExpandProperty IPAddress)"]
try: try:
result = subprocess.run(command, capture_output=True, text=True, timeout=5) result = _run_hidden(command, capture_output=True, text=True, timeout=5)
value = (result.stdout or "").strip().splitlines() value = (result.stdout or "").strip().splitlines()
if value and value[0].strip(): if value and value[0].strip():
return value[0].strip() return value[0].strip()
@@ -144,7 +154,7 @@ class TunBackend:
"Sort-Object RouteMetric | Select-Object -First 1 " "Sort-Object RouteMetric | Select-Object -First 1 "
"NextHop,InterfaceIndex | ConvertTo-Json -Compress"] "NextHop,InterfaceIndex | ConvertTo-Json -Compress"]
try: try:
result = subprocess.run(command, capture_output=True, text=True, timeout=5) result = _run_hidden(command, capture_output=True, text=True, timeout=5)
data = json.loads((result.stdout or "").strip()) data = json.loads((result.stdout or "").strip())
if isinstance(data, dict): if isinstance(data, dict):
return str(data.get("NextHop") or ""), str(data.get("InterfaceIndex") or "") return str(data.get("NextHop") or ""), str(data.get("InterfaceIndex") or "")
@@ -161,7 +171,7 @@ class TunBackend:
"Sort-Object RouteMetric | Select-Object -First 1 " "Sort-Object RouteMetric | Select-Object -First 1 "
"NextHop,InterfaceIndex | ConvertTo-Json -Compress"] "NextHop,InterfaceIndex | ConvertTo-Json -Compress"]
try: try:
result = subprocess.run(command, capture_output=True, text=True, timeout=5) result = _run_hidden(command, capture_output=True, text=True, timeout=5)
data = json.loads((result.stdout or "").strip()) data = json.loads((result.stdout or "").strip())
if isinstance(data, dict): if isinstance(data, dict):
return str(data.get("NextHop") or ""), str(data.get("InterfaceIndex") or "") return str(data.get("NextHop") or ""), str(data.get("InterfaceIndex") or "")
@@ -182,7 +192,7 @@ class TunBackend:
"$_.IPAddress -notlike 'fd*' }).IPAddress | ConvertTo-Json -Compress", "$_.IPAddress -notlike 'fd*' }).IPAddress | ConvertTo-Json -Compress",
] ]
try: try:
result = subprocess.run(command, capture_output=True, text=True, timeout=5) result = _run_hidden(command, capture_output=True, text=True, timeout=5)
if result.returncode: if result.returncode:
return None return None
value = json.loads((result.stdout or "").strip() or "[]") value = json.loads((result.stdout or "").strip() or "[]")
@@ -242,7 +252,7 @@ class TunBackend:
if self._physical_gateway: if self._physical_gateway:
command.append(self._physical_gateway) command.append(self._physical_gateway)
command.extend(["METRIC", "1", "IF", self._physical_ifindex]) command.extend(["METRIC", "1", "IF", self._physical_ifindex])
result = subprocess.run(command, capture_output=True, text=True, result = _run_hidden(command, capture_output=True, text=True,
encoding="mbcs", errors="replace", timeout=15) encoding="mbcs", errors="replace", timeout=15)
if result.returncode: if result.returncode:
detail = (result.stderr or result.stdout or "").strip() detail = (result.stderr or result.stdout or "").strip()
@@ -275,7 +285,7 @@ class TunBackend:
if self._physical_ipv6_gateway and self._physical_ipv6_gateway != "::": if self._physical_ipv6_gateway and self._physical_ipv6_gateway != "::":
command.append(f"nexthop={self._physical_ipv6_gateway}") command.append(f"nexthop={self._physical_ipv6_gateway}")
command.append("metric=1") command.append("metric=1")
result = subprocess.run(["netsh", *command], capture_output=True, text=True, result = _run_hidden(["netsh", *command], capture_output=True, text=True,
encoding="mbcs", errors="replace", timeout=15) encoding="mbcs", errors="replace", timeout=15)
if result.returncode: if result.returncode:
detail = (result.stderr or result.stdout or "").strip() detail = (result.stderr or result.stdout or "").strip()
@@ -310,7 +320,7 @@ class TunBackend:
if self._physical_gateway: if self._physical_gateway:
command.append(self._physical_gateway) command.append(self._physical_gateway)
command.extend(["METRIC", "1", "IF", self._physical_ifindex]) command.extend(["METRIC", "1", "IF", self._physical_ifindex])
result = subprocess.run(command, capture_output=True, text=True, result = _run_hidden(command, capture_output=True, text=True,
encoding="mbcs", errors="replace", timeout=15) encoding="mbcs", errors="replace", timeout=15)
detail = (result.stderr or result.stdout or "").strip() detail = (result.stderr or result.stdout or "").strip()
if result.returncode: if result.returncode:
@@ -333,7 +343,7 @@ class TunBackend:
def _cleanup_netsh(self, args: list[str]) -> bool: def _cleanup_netsh(self, args: list[str]) -> bool:
try: try:
result = subprocess.run(["netsh", *args], capture_output=True, text=True, result = _run_hidden(["netsh", *args], capture_output=True, text=True,
encoding="mbcs", errors="replace", timeout=15) encoding="mbcs", errors="replace", timeout=15)
except Exception as exc: except Exception as exc:
self.detail_log(f"[TUN] 清理 netsh 失败: {exc}") self.detail_log(f"[TUN] 清理 netsh 失败: {exc}")
@@ -348,7 +358,7 @@ class TunBackend:
remaining: list[str] = [] remaining: list[str] = []
for address in self._node_routes: for address in self._node_routes:
try: try:
result = subprocess.run(["route", "DELETE", address], capture_output=True, result = _run_hidden(["route", "DELETE", address], capture_output=True,
text=True, encoding="mbcs", errors="replace", timeout=15) text=True, encoding="mbcs", errors="replace", timeout=15)
except Exception as exc: except Exception as exc:
self.detail_log(f"[TUN] 清理节点路由失败 ({address}): {exc}") self.detail_log(f"[TUN] 清理节点路由失败 ({address}): {exc}")
@@ -365,7 +375,7 @@ class TunBackend:
if self._physical_ipv6_ifindex: if self._physical_ipv6_ifindex:
command.append(f"interface={self._physical_ipv6_ifindex}") command.append(f"interface={self._physical_ipv6_ifindex}")
try: try:
result = subprocess.run(["netsh", *command], capture_output=True, text=True, result = _run_hidden(["netsh", *command], capture_output=True, text=True,
encoding="mbcs", errors="replace", timeout=15) encoding="mbcs", errors="replace", timeout=15)
except Exception as exc: except Exception as exc:
self.detail_log(f"[TUN] 清理 IPv6 节点路由失败 ({address}): {exc}") self.detail_log(f"[TUN] 清理 IPv6 节点路由失败 ({address}): {exc}")
@@ -379,7 +389,7 @@ class TunBackend:
return not remaining and not remaining_v6 return not remaining and not remaining_v6
def _netsh(self, args: list[str], *, ignore_error: bool = False) -> None: def _netsh(self, args: list[str], *, ignore_error: bool = False) -> None:
result = subprocess.run(["netsh", *args], capture_output=True, text=True, result = _run_hidden(["netsh", *args], capture_output=True, text=True,
encoding="mbcs", errors="replace", timeout=15) encoding="mbcs", errors="replace", timeout=15)
if result.returncode and not ignore_error: if result.returncode and not ignore_error:
detail = (result.stderr or result.stdout or "").strip() detail = (result.stderr or result.stdout or "").strip()
@@ -388,7 +398,7 @@ class TunBackend:
def _configure_routes(self) -> None: def _configure_routes(self) -> None:
for _ in range(20): for _ in range(20):
try: try:
probe = subprocess.run( probe = _run_hidden(
["powershell", "-NoProfile", "-Command", ["powershell", "-NoProfile", "-Command",
f"Get-NetAdapter -Name '{self._device}' -ErrorAction SilentlyContinue"], f"Get-NetAdapter -Name '{self._device}' -ErrorAction SilentlyContinue"],
capture_output=True, text=True, timeout=3, capture_output=True, text=True, timeout=3,