mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-04-16 17:12:21 +03:00
Merge pull request #851 from EDCD/enhancement/848-suppress-dupe-process-popup
Add --suppress-dupe-process-popup and move enforce_single_instance() call after
This commit is contained in:
commit
61275b73a1
@ -15,9 +15,32 @@ from sys import platform
|
||||
from time import localtime, strftime, time
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from config import applongname, appname, appversion, appversion_nobuild, config, copyright
|
||||
from constants import applongname, appname, protocolhandler_redirect
|
||||
|
||||
# config will now cause an appname logger to be set up, so we need the
|
||||
# console redirect before this
|
||||
if __name__ == '__main__':
|
||||
# Command-line arguments
|
||||
parser = argparse.ArgumentParser(
|
||||
prog=appname,
|
||||
description="Utilises Elite Dangerous Journal files and the Frontier "
|
||||
"Companion API (CAPI) service to gather data about a "
|
||||
"player's state and actions to upload to third-party sites "
|
||||
"such as EDSM, Inara.cz and EDDB."
|
||||
)
|
||||
|
||||
parser.add_argument('--trace',
|
||||
help='Set the Debug logging loglevel to TRACE',
|
||||
action='store_true',
|
||||
)
|
||||
|
||||
parser.add_argument('--suppress-dupe-process-popup',
|
||||
help='Suppress the popup from when the application detects another instance already running',
|
||||
action='store_true'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if __name__ == "__main__":
|
||||
def no_other_instance_running() -> bool: # noqa: CCR001
|
||||
"""
|
||||
Ensure only one copy of the app is running under this user account.
|
||||
@ -70,7 +93,7 @@ if __name__ == "__main__":
|
||||
and window_title(window_handle) == applongname \
|
||||
and GetProcessHandleFromHwnd(window_handle):
|
||||
# If GetProcessHandleFromHwnd succeeds then the app is already running as this user
|
||||
if len(sys.argv) > 1 and sys.argv[1].startswith(protocolhandler.redirect):
|
||||
if len(sys.argv) > 1 and sys.argv[1].startswith(protocolhandler_redirect):
|
||||
CoInitializeEx(0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
|
||||
# Wait for it to be responsive to avoid ShellExecute recursing
|
||||
ShowWindow(window_handle, SW_RESTORE)
|
||||
@ -93,6 +116,10 @@ if __name__ == "__main__":
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
|
||||
# Check for CL arg that suppresses this popup.
|
||||
if args.suppress_dupe_process_popup:
|
||||
sys.exit(0)
|
||||
|
||||
root = tk.Tk(className=appname.lower())
|
||||
|
||||
frame = tk.Frame(root)
|
||||
@ -136,6 +163,11 @@ if __name__ == "__main__":
|
||||
|
||||
# unbuffered not allowed for text in python3, so use `1 for line buffering
|
||||
sys.stdout = sys.stderr = open(join(tempfile.gettempdir(), f'{appname}.log'), mode='wt', buffering=1)
|
||||
# TODO: Test: Make *sure* this redirect is working, else py2exe is going to cause an exit popup
|
||||
|
||||
# isort: off
|
||||
from config import appversion, appversion_nobuild, config, copyright
|
||||
# isort: on
|
||||
|
||||
from EDMCLogging import edmclogger, logger, logging
|
||||
|
||||
@ -1146,65 +1178,6 @@ class AppWindow(object):
|
||||
self.blank_menubar.grid(row=0, columnspan=2, sticky=tk.NSEW)
|
||||
|
||||
|
||||
def enforce_single_instance() -> None:
|
||||
# Ensure only one copy of the app is running under this user account. OSX does this automatically. Linux TODO.
|
||||
if platform == 'win32':
|
||||
import ctypes
|
||||
from ctypes.wintypes import BOOL, HWND, INT, LPARAM, LPCWSTR, LPWSTR
|
||||
|
||||
EnumWindows = ctypes.windll.user32.EnumWindows # noqa: N806
|
||||
GetClassName = ctypes.windll.user32.GetClassNameW # noqa: N806
|
||||
GetClassName.argtypes = [HWND, LPWSTR, ctypes.c_int]
|
||||
GetWindowText = ctypes.windll.user32.GetWindowTextW # noqa: N806
|
||||
GetWindowText.argtypes = [HWND, LPWSTR, ctypes.c_int]
|
||||
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW # noqa: N806
|
||||
GetProcessHandleFromHwnd = ctypes.windll.oleacc.GetProcessHandleFromHwnd # noqa: N806
|
||||
|
||||
SW_RESTORE = 9 # noqa: N806
|
||||
SetForegroundWindow = ctypes.windll.user32.SetForegroundWindow # noqa: N806
|
||||
ShowWindow = ctypes.windll.user32.ShowWindow # noqa: N806
|
||||
ShowWindowAsync = ctypes.windll.user32.ShowWindowAsync # noqa: N806
|
||||
|
||||
COINIT_MULTITHREADED = 0 # noqa: N806,F841
|
||||
COINIT_APARTMENTTHREADED = 0x2 # noqa: N806
|
||||
COINIT_DISABLE_OLE1DDE = 0x4 # noqa: N806
|
||||
CoInitializeEx = ctypes.windll.ole32.CoInitializeEx # noqa: N806
|
||||
|
||||
ShellExecute = ctypes.windll.shell32.ShellExecuteW # noqa: N806
|
||||
ShellExecute.argtypes = [HWND, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR, INT]
|
||||
|
||||
def window_title(h):
|
||||
if h:
|
||||
text_length = GetWindowTextLength(h) + 1
|
||||
buf = ctypes.create_unicode_buffer(text_length)
|
||||
if GetWindowText(h, buf, text_length):
|
||||
return buf.value
|
||||
return None
|
||||
|
||||
@ctypes.WINFUNCTYPE(BOOL, HWND, LPARAM)
|
||||
def enumwindowsproc(window_handle, l_param):
|
||||
# class name limited to 256 - https://msdn.microsoft.com/en-us/library/windows/desktop/ms633576
|
||||
cls = ctypes.create_unicode_buffer(257)
|
||||
if GetClassName(window_handle, cls, 257) \
|
||||
and cls.value == 'TkTopLevel' \
|
||||
and window_title(window_handle) == applongname \
|
||||
and GetProcessHandleFromHwnd(window_handle):
|
||||
# If GetProcessHandleFromHwnd succeeds then the app is already running as this user
|
||||
if len(sys.argv) > 1 and sys.argv[1].startswith(protocolhandler.redirect):
|
||||
# Browser invoked us directly with auth response. Forward the response to the other app instance.
|
||||
CoInitializeEx(0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
|
||||
# Wait for it to be responsive to avoid ShellExecute recursing
|
||||
ShowWindow(window_handle, SW_RESTORE)
|
||||
ShellExecute(0, None, sys.argv[1], None, None, SW_RESTORE)
|
||||
else:
|
||||
ShowWindowAsync(window_handle, SW_RESTORE)
|
||||
SetForegroundWindow(window_handle)
|
||||
sys.exit(0)
|
||||
return True
|
||||
|
||||
EnumWindows(enumwindowsproc, 0)
|
||||
|
||||
|
||||
def test_logging():
|
||||
logger.debug('Test from EDMarketConnector.py top-level test_logging()')
|
||||
|
||||
@ -1221,24 +1194,6 @@ Locale LC_TIME: {locale.getlocale(locale.LC_TIME)}'''
|
||||
|
||||
# Run the app
|
||||
if __name__ == "__main__":
|
||||
enforce_single_instance()
|
||||
|
||||
# Command-line arguments
|
||||
parser = argparse.ArgumentParser(
|
||||
prog=appname,
|
||||
description="Utilises Elite Dangerous Journal files and the Frontier "
|
||||
"Companion API (CAPI) service to gather data about a "
|
||||
"player's state and actions to upload to third-party sites "
|
||||
"such as EDSM, Inara.cz and EDDB."
|
||||
)
|
||||
|
||||
parser.add_argument('--trace',
|
||||
help='Set the Debug logging loglevel to TRACE',
|
||||
action='store_true',
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.trace:
|
||||
logger.setLevel(logging.TRACE)
|
||||
edmclogger.set_channels_loglevel(logging.TRACE)
|
||||
|
@ -6,9 +6,9 @@ from os.path import expanduser, dirname, exists, isdir, join, normpath
|
||||
from sys import platform
|
||||
import semantic_version
|
||||
|
||||
from constants import applongname, appname
|
||||
|
||||
# Any of these may be imported by plugins
|
||||
appname = 'EDMarketConnector'
|
||||
applongname = 'E:D Market Connector'
|
||||
appcmdname = 'EDMC'
|
||||
# appversion **MUST** follow Semantic Versioning rules:
|
||||
# <https://semver.org/#semantic-versioning-specification-semver>
|
||||
|
14
constants.py
Normal file
14
constants.py
Normal file
@ -0,0 +1,14 @@
|
||||
"""
|
||||
All constants for the application.
|
||||
|
||||
This file should contain all constants that the application uses, but that
|
||||
means migrating the existing ones, so for now it's only the ones that we've
|
||||
found necessary to move out of other files.
|
||||
"""
|
||||
|
||||
# config.py
|
||||
appname = 'EDMarketConnector'
|
||||
applongname = 'E:D Market Connector'
|
||||
|
||||
# protocol.py
|
||||
protocolhandler_redirect = 'edmc://auth'
|
@ -5,7 +5,8 @@ import threading
|
||||
import urllib.request, urllib.error, urllib.parse
|
||||
import sys
|
||||
|
||||
from config import appname, config
|
||||
from config import config
|
||||
from constants import protocolhandler_redirect
|
||||
|
||||
|
||||
if sys.platform == 'win32':
|
||||
@ -20,7 +21,7 @@ if sys.platform == 'win32':
|
||||
class GenericProtocolHandler(object):
|
||||
|
||||
def __init__(self):
|
||||
self.redirect = 'edmc://auth' # Base redirection URL
|
||||
self.redirect = protocolhandler_redirect # Base redirection URL
|
||||
self.master = None
|
||||
self.lastpayload = None
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user