# -*- coding: utf-8 -*- """开机自启(当前用户级,不需要管理员)。 写 HKCU\\...\\Run,不碰 HKLM —— 不需要 UAC,也不会影响别的用户。 """ from __future__ import annotations import subprocess import sys from pathlib import Path RUN_KEY = r"HKCU\Software\Microsoft\Windows\CurrentVersion\Run" # 任务管理器「启动」标签页里显示的就是这个名字。 # 用 ykxmz 而不是冒充 "Windows xxx" —— 用的人应该看得出这是什么。 VALUE_NAME = "ykxmz" # 早期版本用过的名字。开着自启的老用户升级后要把它清掉,别留两条。 LEGACY_VALUE_NAMES = ("LanRemoteAgent",) _NO_WINDOW = getattr(subprocess, "CREATE_NO_WINDOW", 0) def executable_path() -> str: """要写进注册表的可执行文件路径。""" if getattr(sys, "frozen", False): return str(Path(sys.executable).resolve()) # 源码模式:用 pythonw + run_agent.py,这样不会有黑窗 script = Path(__file__).resolve().parent.parent / "run_agent.py" pythonw = Path(sys.executable).with_name("pythonw.exe") exe = pythonw if pythonw.is_file() else Path(sys.executable) return f'"{exe}" "{script}"' def _run(args: list[str]) -> tuple[int, str]: try: proc = subprocess.run(args, capture_output=True, timeout=20, creationflags=_NO_WINDOW, encoding="mbcs", errors="replace") return proc.returncode, ((proc.stdout or "") + (proc.stderr or "")).strip() except Exception as exc: # noqa: BLE001 return -1, str(exc) def command_line() -> str: """注册表里存的完整命令行。""" if getattr(sys, "frozen", False): return f'"{Path(sys.executable).resolve()}" --silent' script = Path(__file__).resolve().parent.parent / "run_agent.py" pythonw = Path(sys.executable).with_name("pythonw.exe") exe = pythonw if pythonw.is_file() else Path(sys.executable) return f'"{exe}" "{script}" --silent' def is_enabled() -> bool: code, out = _run(["reg", "query", RUN_KEY, "/v", VALUE_NAME]) return code == 0 and VALUE_NAME in out def current_value() -> str: code, out = _run(["reg", "query", RUN_KEY, "/v", VALUE_NAME]) if code != 0: return "" for line in out.splitlines(): if VALUE_NAME in line and "REG_SZ" in line: return line.split("REG_SZ", 1)[-1].strip() return "" def cleanup_legacy() -> list: """清掉早期版本留下的自启项(换名字了,别留两条)。""" removed = [] for name in LEGACY_VALUE_NAMES: code, _out = _run(["reg", "delete", RUN_KEY, "/v", name, "/f"]) if code == 0: removed.append(name) return removed def enable() -> str: value = command_line() code, out = _run(["reg", "add", RUN_KEY, "/v", VALUE_NAME, "/t", "REG_SZ", "/d", value, "/f"]) if code != 0: raise ValueError(f"写入开机自启失败: {out[:120]}") gone = cleanup_legacy() extra = f"(顺手清掉旧自启项 {', '.join(gone)})" if gone else "" return f"已开启开机自启(静默模式){extra}\n{value}" def disable() -> str: code, out = _run(["reg", "delete", RUN_KEY, "/v", VALUE_NAME, "/f"]) if code != 0 and "找不到" not in out and "unable" not in out.lower(): # 本来就没开,也算成功 pass return "已关闭开机自启" def toggle(on: bool) -> str: return enable() if on else disable() def status() -> dict: enabled = is_enabled() return { "enabled": enabled, "command": current_value() if enabled else "", "expected": command_line(), }