diff --git a/.gitignore b/.gitignore index 1dc032ba..51a629d0 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ __pycache__ /backend/windows-runner/dist/ logs.txt dcef.exe +/backend/windows-runner/dcef.spec # nginx /backend/nginx/logs diff --git a/backend/windows-runner/requirements.txt b/backend/windows-runner/requirements.txt index bfb2f425..c98a176c 100644 --- a/backend/windows-runner/requirements.txt +++ b/backend/windows-runner/requirements.txt @@ -1 +1,2 @@ -pywebview==4.0.1 \ No newline at end of file +pywebview==4.0.1 +pywin32==305 \ No newline at end of file diff --git a/backend/windows-runner/windows-runner.py b/backend/windows-runner/windows-runner.py index 137f8bef..7d407599 100644 --- a/backend/windows-runner/windows-runner.py +++ b/backend/windows-runner/windows-runner.py @@ -8,25 +8,39 @@ import atexit import threading +# https://stackoverflow.com/a/65501621 +from win32event import CreateMutex +from win32api import CloseHandle, GetLastError +from winerror import ERROR_ALREADY_EXISTS +class SingleInstance: + """ Limits application to single instance """ + + def __init__(self): + self.mutexname = "dcef-qwertyuiopasdfghjklzxcvbnm1234567890" + self.mutex = CreateMutex(None, False, self.mutexname) + self.lasterror = GetLastError() + + def is_secondary_instance(self): + return (self.lasterror == ERROR_ALREADY_EXISTS) + + def is_primary_instance(self): + return not self.is_secondary_instance() + + def __del__(self): + if self.mutex: + CloseHandle(self.mutex) + + + def is_compiled(): - if os.path.exists(__file__): - return False - else: - return True + if os.path.exists(__file__): + return False + else: + return True -is_compiled = is_compiled() -processes = [] -terminating_now = False -if is_compiled: - BASE_DIR = os.path.realpath(os.path.dirname(sys.executable)) -else: - BASE_DIR = os.path.realpath(os.path.dirname(__file__) + '/../../release') -LOG_FILE = BASE_DIR + '/dcef/logs.txt' -if os.path.exists(LOG_FILE): - os.remove(LOG_FILE) def custom_print(source, *args, **kwargs): @@ -46,6 +60,12 @@ def cleanup(): global terminating_now if not terminating_now: # Prevents cleanup from being called twice terminating_now = True + + # kill other instances of dcef.exe except this one + if is_compiled() and myapp.is_primary_instance(): + custom_print("windows-runner:", "killing other instances of dcef.exe except primary with PID " + str(os.getpid())) + os.system('taskkill /f /im dcef.exe /fi "PID ne ' + str(os.getpid()) + '"') + custom_print("windows-runner:", "cleaning up") for process in processes: custom_print("windows-runner:", "killing process", process.pid) @@ -55,9 +75,13 @@ def cleanup(): custom_print("windows-runner:", "error killing process", process.pid, e) + def create_window(): custom_print("windows-runner:", "creating window") - window = webview.create_window('DiscordChatExported-frontend','http://127.0.0.1:21011/', + title = 'DiscordChatExported-frontend' + if myapp.is_secondary_instance(): + title += ' (secondary instance)' + window = webview.create_window(title, 'http://127.0.0.1:21011/', width=1280, height=720, background_color='#36393F', @@ -128,13 +152,13 @@ def start_nginx(): def hide_console(): # https://github.com/pyinstaller/pyinstaller/issues/1339#issuecomment-122909830 - if is_compiled: + if is_compiled(): whnd = ctypes.windll.kernel32.GetConsoleWindow() if whnd != 0: ctypes.windll.user32.ShowWindow(whnd, 0) def show_console(): - if is_compiled: + if is_compiled(): whnd = ctypes.windll.kernel32.GetConsoleWindow() if whnd != 0: ctypes.windll.user32.ShowWindow(whnd, 1) @@ -143,23 +167,49 @@ def show_console(): def main(): - custom_print("windows-runner:", "started") - atexit.register(cleanup) - th_nginx = start_nginx() - time.sleep(1) - th_mongodb = start_mongodb() - th_http_server = start_http_server() - th_fastapi = start_fastapi() - th_preprocess = start_preprocess() - - - th_preprocess.join() # Wait for preprocess to finish - hide_console() - create_window() - show_console() - custom_print("windows-runner:", "finished") - cleanup() + if myapp.is_secondary_instance(): + # second instance just needs to open another window, the backend services are already running + custom_print("windows-runner:", "started secondary instance") + hide_console() + create_window() + show_console() + custom_print("windows-runner:", "finished secondary instance") + else: + if os.path.exists(LOG_FILE): + os.remove(LOG_FILE) + + custom_print("windows-runner:", "started primary instance") + atexit.register(cleanup) + th_nginx = start_nginx() + time.sleep(1) + th_mongodb = start_mongodb() + th_http_server = start_http_server() + th_fastapi = start_fastapi() + th_preprocess = start_preprocess() + + + th_preprocess.join() # Wait for preprocess to finish + + hide_console() + create_window() + show_console() + + custom_print("windows-runner:", "finished primary instance") + cleanup() + + + +myapp = SingleInstance() +processes = [] +terminating_now = False + +if is_compiled(): + BASE_DIR = os.path.realpath(os.path.dirname(sys.executable)) +else: + BASE_DIR = os.path.realpath(os.path.dirname(__file__) + '/../../release') + +LOG_FILE = BASE_DIR + '/dcef/logs.txt' if __name__ == '__main__': main() \ No newline at end of file