windows mutex-lock

This commit is contained in:
Flowseal 2026-04-14 00:27:06 +03:00
parent 25ae4b0a24
commit 4cbb9e555c
4 changed files with 46 additions and 6 deletions

View file

@ -273,7 +273,7 @@ def run_tray() -> None:
def main() -> None:
if not acquire_lock("linux.py"):
if not acquire_lock():
_show_info("Приложение уже запущено.", os.path.basename(sys.argv[0]))
return
try:

View file

@ -610,7 +610,7 @@ def run_menubar() -> None:
def main() -> None:
if not acquire_lock("macos.py"):
if not acquire_lock():
_show_info("Приложение уже запущено.")
return
try:

View file

@ -51,7 +51,7 @@ def ensure_dirs() -> None:
_lock_file_path: Optional[Path] = None
def _same_process(meta: dict, proc: psutil.Process, script_hint: str) -> bool:
def _same_process(meta: dict, proc: psutil.Process) -> bool:
try:
lock_ct = float(meta.get("create_time", 0.0))
if lock_ct > 0 and abs(lock_ct - proc.create_time()) > 1.0:
@ -63,7 +63,7 @@ def _same_process(meta: dict, proc: psutil.Process, script_hint: str) -> bool:
return False
def acquire_lock(script_hint: str = "") -> bool:
def acquire_lock() -> bool:
global _lock_file_path
ensure_dirs()
for f in list(APP_DIR.glob("*.lock")):
@ -84,7 +84,7 @@ def acquire_lock(script_hint: str = "") -> bool:
pass
is_running = False
try:
is_running = _same_process(meta, psutil.Process(pid), script_hint)
is_running = _same_process(meta, psutil.Process(pid))
except Exception:
pass
if is_running:

View file

@ -56,6 +56,39 @@ from ui.ctk_theme import (
_tray_icon: Optional[object] = None
_config: dict = {}
_exiting = False
_win_mutex_handle = None
_ERROR_ALREADY_EXISTS = 183
def _acquire_win_mutex() -> bool | None:
global _win_mutex_handle
try:
kernel32 = ctypes.windll.kernel32
kernel32.CreateMutexW.restype = ctypes.c_void_p
kernel32.CreateMutexW.argtypes = [ctypes.c_void_p, ctypes.c_bool, ctypes.c_wchar_p]
handle = kernel32.CreateMutexW(None, True, "Local\\TgWsProxy_SingleInstance")
if kernel32.GetLastError() == _ERROR_ALREADY_EXISTS:
kernel32.CloseHandle(ctypes.c_void_p(handle))
return False
if not handle:
return None
_win_mutex_handle = handle
return True
except Exception:
return None
def _release_win_mutex() -> None:
global _win_mutex_handle
if _win_mutex_handle:
try:
kernel32 = ctypes.windll.kernel32
kernel32.ReleaseMutex(ctypes.c_void_p(_win_mutex_handle))
kernel32.CloseHandle(ctypes.c_void_p(_win_mutex_handle))
except Exception:
pass
_win_mutex_handle = None
ICON_PATH = str(Path(__file__).parent / "icon.ico")
@ -350,13 +383,20 @@ def run_tray() -> None:
def main() -> None:
if not acquire_lock("windows.py"):
if mutex_result := _acquire_win_mutex() is False:
_show_info("Приложение уже запущено.", os.path.basename(sys.argv[0]))
return
if mutex_result is None:
log.warning("Named mutex unavailable, falling back to lock file")
if not acquire_lock():
_show_info("Приложение уже запущено.", os.path.basename(sys.argv[0]))
return
try:
run_tray()
finally:
release_lock()
_release_win_mutex()
if __name__ == "__main__":