-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstance_lock.py
More file actions
110 lines (85 loc) · 3.2 KB
/
Copy pathinstance_lock.py
File metadata and controls
110 lines (85 loc) · 3.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
"""Single instance lock to prevent multiple instances of the application."""
import sys
import ctypes
from ctypes import wintypes
from types import TracebackType
# Windows API constants
MUTEX_ALL_ACCESS = 0x1F0001
ERROR_ALREADY_EXISTS = 183
class SingleInstanceLock:
"""
Ensures only one instance of the application runs at a time using a Windows mutex.
"""
def __init__(self, name: str = "StellaSoraModLauncher_SingleInstance"):
self.name = name
self.mutex_handle = None
self._acquired = False
def acquire(self) -> bool:
"""
Try to acquire the single instance lock.
Returns:
True if this is the first instance, False if another instance exists
"""
if sys.platform != "win32":
# Non-Windows platforms - always allow
return True
try:
# Try to create a named mutex
kernel32 = ctypes.windll.kernel32
self.mutex_handle = kernel32.CreateMutexW(
None, # default security attributes
True, # initially owned
self.name # mutex name
)
last_error = kernel32.GetLastError()
if last_error == ERROR_ALREADY_EXISTS:
# Another instance is already running
if self.mutex_handle:
kernel32.CloseHandle(self.mutex_handle)
self.mutex_handle = None
return False
self._acquired = True
return True
except Exception:
# If we can't create mutex, allow the instance to run
return True
def release(self) -> None:
"""Release the single instance lock."""
if self.mutex_handle and sys.platform == "win32":
try:
kernel32 = ctypes.windll.kernel32
kernel32.ReleaseMutex(self.mutex_handle)
kernel32.CloseHandle(self.mutex_handle)
except Exception:
pass
finally:
self.mutex_handle = None
self._acquired = False
def __enter__(self):
self.acquire()
return self
def __exit__(self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None) -> bool:
self.release()
return False
def find_and_focus_existing_window(window_title: str = "Stella Sora Mod Launcher") -> bool:
"""
Find an existing window with the given title and bring it to foreground.
Returns:
True if window was found and focused, False otherwise
"""
if sys.platform != "win32":
return False
try:
user32 = ctypes.windll.user32
# Find window by title
hwnd = user32.FindWindowW(None, window_title)
if hwnd:
# Restore if minimized
SW_RESTORE = 9
user32.ShowWindow(hwnd, SW_RESTORE)
# Bring to foreground
user32.SetForegroundWindow(hwnd)
return True
return False
except Exception:
return False