diff --git a/DLPilot/release/DLPilot.exe b/DLPilot/release/DLPilot.exe index df0ea12..af3ed88 100644 Binary files a/DLPilot/release/DLPilot.exe and b/DLPilot/release/DLPilot.exe differ diff --git a/DLPilot/src/app_gui.py b/DLPilot/src/app_gui.py index 49b8980..3bfa930 100644 --- a/DLPilot/src/app_gui.py +++ b/DLPilot/src/app_gui.py @@ -63,10 +63,30 @@ def _apply_dropdown_theme(): ctk.ThemeManager.theme['DropdownMenu']['text_color'] = ('#1A1A1A', '#DADADA') +def _ensure_single_instance(): + """单实例锁(Windows 命名互斥体): 已有实例在运行时返回 False。 + + 双击 exe 开多个实例时, 只有先启动的能连串口, 后续实例连接报"拒绝访问", + 且用户往往只看到后开的窗口 —— 表现为"连不上/断不开"。互斥体句柄随 + 进程存活, 进程退出自动释放。""" + ERROR_ALREADY_EXISTS = 183 + ctypes.windll.kernel32.CreateMutexW(None, False, 'hulk.DLPilot.single') + return ctypes.windll.kernel32.GetLastError() != ERROR_ALREADY_EXISTS + + def run_gui(auto_close=0): """入口: 配置外观并启动主窗口""" ctk.set_appearance_mode('System') _apply_dropdown_theme() + if not _ensure_single_instance(): + from tkinter import messagebox + import tkinter as tk + r = tk.Tk() + r.withdraw() + messagebox.showinfo('DLPilot 已在运行', + '检测到另一个 DLPilot 实例正在运行。\n' + '请使用已打开的窗口(串口同时只能被一个实例占用)。') + return App(auto_close).mainloop() diff --git a/DLPilot/src/dlpc_rs485.py b/DLPilot/src/dlpc_rs485.py index 72fe4d1..437e38f 100644 --- a/DLPilot/src/dlpc_rs485.py +++ b/DLPilot/src/dlpc_rs485.py @@ -301,7 +301,11 @@ class DLPC485: self.ser = None with self._lock: self._target = None - self.on_status(f'连接失败: {e}') + et = str(e) + if 'PermissionError' in repr(e) or '拒绝访问' in et \ + or 'Access is denied' in et: + et += '(串口被占用: 检查串口助手/另一个本工具实例)' + self.on_status(f'连接失败: {et}') if self.ser is not None: try: item = None