1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-04-12 15:27:14 +03:00

[1805] Apply More Pywin32

This commit is contained in:
David Sangrey 2024-06-11 11:36:43 -04:00
parent 571558daff
commit 625856c31c
No known key found for this signature in database
GPG Key ID: 3AEADBB0186884BC
6 changed files with 34 additions and 81 deletions

View File

@ -263,8 +263,6 @@ if __name__ == '__main__': # noqa: C901
import win32con import win32con
GetProcessHandleFromHwnd = windll.oleacc.GetProcessHandleFromHwnd # noqa: N806 GetProcessHandleFromHwnd = windll.oleacc.GetProcessHandleFromHwnd # noqa: N806
ShowWindow = windll.user32.ShowWindow # noqa: N806
ShowWindowAsync = windll.user32.ShowWindowAsync # noqa: N806 ShowWindowAsync = windll.user32.ShowWindowAsync # noqa: N806
COINIT_MULTITHREADED = 0 # noqa: N806,F841 COINIT_MULTITHREADED = 0 # noqa: N806,F841
@ -308,7 +306,7 @@ if __name__ == '__main__': # noqa: C901
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) CoInitializeEx(0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
# Wait for it to be responsive to avoid ShellExecute recursing # Wait for it to be responsive to avoid ShellExecute recursing
ShowWindow(window_handle, win32con.SW_RESTORE) win32gui.ShowWindow(window_handle, win32con.SW_RESTORE)
win32api.ShellExecute(0, None, sys.argv[1], None, None, win32con.SW_RESTORE) win32api.ShellExecute(0, None, sys.argv[1], None, None, win32con.SW_RESTORE)
else: else:

View File

@ -36,17 +36,15 @@ MAX_FCMATERIALS_DISCREPANCY = 5 # Timestamp difference in seconds
if sys.platform == 'win32': if sys.platform == 'win32':
import ctypes import ctypes
from ctypes.wintypes import BOOL, HWND, LPARAM, HANDLE from ctypes.wintypes import BOOL, HWND, LPARAM
import win32gui import win32gui
import win32api
from watchdog.events import FileSystemEventHandler, FileSystemEvent from watchdog.events import FileSystemEventHandler, FileSystemEvent
from watchdog.observers import Observer from watchdog.observers import Observer
from watchdog.observers.api import BaseObserver from watchdog.observers.api import BaseObserver
EnumWindowsProc = ctypes.WINFUNCTYPE(BOOL, HWND, LPARAM) EnumWindowsProc = ctypes.WINFUNCTYPE(BOOL, HWND, LPARAM)
CloseHandle = ctypes.windll.kernel32.CloseHandle
CloseHandle.argtypes = [HANDLE]
CloseHandle.restype = BOOL
GetProcessHandleFromHwnd = ctypes.windll.oleacc.GetProcessHandleFromHwnd GetProcessHandleFromHwnd = ctypes.windll.oleacc.GetProcessHandleFromHwnd
else: else:
@ -2136,7 +2134,7 @@ class EDLogs(FileSystemEventHandler):
if name and name.startswith('Elite - Dangerous'): if name and name.startswith('Elite - Dangerous'):
handle = GetProcessHandleFromHwnd(hWnd) handle = GetProcessHandleFromHwnd(hWnd)
if handle: # If GetProcessHandleFromHwnd succeeds then the app is already running as this user if handle: # If GetProcessHandleFromHwnd succeeds then the app is already running as this user
CloseHandle(handle) win32api.CloseHandle(handle)
return False # stop enumeration return False # stop enumeration
return True return True

View File

@ -188,8 +188,9 @@ class AutoInc(contextlib.AbstractContextManager):
if sys.platform == 'win32': if sys.platform == 'win32':
import ctypes import ctypes
import winreg import winreg
from ctypes.wintypes import HINSTANCE, LPCWSTR, LPWSTR, MAX_PATH, POINT, RECT, SIZE, UINT, BOOL from ctypes.wintypes import LPCWSTR, LPWSTR, MAX_PATH, POINT, RECT, SIZE, UINT, BOOL
import win32gui import win32gui
import win32api
is_wine = False is_wine = False
try: try:
WINE_REGISTRY_KEY = r'HKEY_LOCAL_MACHINE\Software\Wine' WINE_REGISTRY_KEY = r'HKEY_LOCAL_MACHINE\Software\Wine'
@ -225,10 +226,6 @@ if sys.platform == 'win32':
SHGetLocalizedName = ctypes.windll.shell32.SHGetLocalizedName SHGetLocalizedName = ctypes.windll.shell32.SHGetLocalizedName
SHGetLocalizedName.argtypes = [LPCWSTR, LPWSTR, UINT, ctypes.POINTER(ctypes.c_int)] SHGetLocalizedName.argtypes = [LPCWSTR, LPWSTR, UINT, ctypes.POINTER(ctypes.c_int)]
LoadString = ctypes.windll.user32.LoadStringW
LoadString.argtypes = [HINSTANCE, UINT, LPWSTR, ctypes.c_int]
LoadString.restype = ctypes.c_int
class PreferencesDialog(tk.Toplevel): class PreferencesDialog(tk.Toplevel):
"""The EDMC preferences dialog.""" """The EDMC preferences dialog."""
@ -313,7 +310,7 @@ class PreferencesDialog(tk.Toplevel):
# Ensure fully on-screen # Ensure fully on-screen
if sys.platform == 'win32' and CalculatePopupWindowPosition: if sys.platform == 'win32' and CalculatePopupWindowPosition:
position = RECT() position = RECT()
win32gui.GetWindowRect(win32gui.GetParent(self.winfo_id()), position) win32gui.GetWindowRect(win32gui.GetParent(self.winfo_id()))
if CalculatePopupWindowPosition( if CalculatePopupWindowPosition(
POINT(parent.winfo_rootx(), parent.winfo_rooty()), POINT(parent.winfo_rootx(), parent.winfo_rooty()),
SIZE(position.right - position.left, position.bottom - position.top), # type: ignore SIZE(position.right - position.left, position.bottom - position.top), # type: ignore
@ -1093,7 +1090,7 @@ class PreferencesDialog(tk.Toplevel):
for i in range(start, len(components)): for i in range(start, len(components)):
try: try:
if (not SHGetLocalizedName('\\'.join(components[:i+1]), buf, MAX_PATH, ctypes.byref(pidsRes)) and if (not SHGetLocalizedName('\\'.join(components[:i+1]), buf, MAX_PATH, ctypes.byref(pidsRes)) and
LoadString(ctypes.WinDLL(expandvars(buf.value))._handle, pidsRes.value, buf, MAX_PATH)): win32api.LoadString(ctypes.WinDLL(expandvars(buf.value))._handle, pidsRes.value, buf, MAX_PATH)):
display.append(buf.value) display.append(buf.value)
else: else:

View File

@ -69,16 +69,16 @@ if (config.auth_force_edmc_protocol # noqa: C901
# This could be false if you use auth_force_edmc_protocol, but then you get to keep the pieces # This could be false if you use auth_force_edmc_protocol, but then you get to keep the pieces
assert sys.platform == 'win32' assert sys.platform == 'win32'
# spell-checker: words HBRUSH HICON WPARAM wstring WNDCLASS HMENU HGLOBAL # spell-checker: words HBRUSH HICON WPARAM wstring WNDCLASS HMENU HGLOBAL
from ctypes import ( # type: ignore from ctypes import (
windll, POINTER, WINFUNCTYPE, Structure, byref, c_long, c_void_p, create_unicode_buffer, wstring_at windll, WINFUNCTYPE, Structure, byref, c_long, c_void_p, create_unicode_buffer, wstring_at
) )
from ctypes.wintypes import ( from ctypes.wintypes import (
ATOM, BOOL, DWORD, HBRUSH, HGLOBAL, HICON, HINSTANCE, HMENU, HWND, INT, LPARAM, LPCWSTR, LPMSG, LPVOID, LPWSTR, ATOM, HBRUSH, HICON, HINSTANCE, HWND, INT, LPARAM, LPCWSTR, LPWSTR,
MSG, UINT, WPARAM MSG, UINT, WPARAM
) )
from win32con import CW_USEDEFAULT
import win32gui import win32gui
import win32con import win32con
import win32api
class WNDCLASS(Structure): class WNDCLASS(Structure):
""" """
@ -101,37 +101,7 @@ if (config.auth_force_edmc_protocol # noqa: C901
('lpszClassName', LPCWSTR) ('lpszClassName', LPCWSTR)
] ]
CreateWindowExW = windll.user32.CreateWindowExW
CreateWindowExW.argtypes = [DWORD, LPCWSTR, LPCWSTR, DWORD, INT, INT, INT, INT, HWND, HMENU, HINSTANCE, LPVOID]
CreateWindowExW.restype = HWND
RegisterClassW = windll.user32.RegisterClassW
RegisterClassW.argtypes = [POINTER(WNDCLASS)]
# DefWindowProcW
# Ref: <https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-defwindowprocw>
# LRESULT DefWindowProcW([in] HWND hWnd,[in] UINT Msg,[in] WPARAM wParam,[in] LPARAM lParam);
# As per example at <https://docs.python.org/3/library/ctypes.html#ctypes.WINFUNCTYPE>
prototype = WINFUNCTYPE(c_long, HWND, UINT, WPARAM, LPARAM)
paramflags = (1, "hWnd"), (1, "Msg"), (1, "wParam"), (1, "lParam")
DefWindowProcW = prototype(("DefWindowProcW", windll.user32), paramflags)
SetForegroundWindow = windll.user32.SetForegroundWindow
# <https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessagew>
# NB: Despite 'BOOL' return type, it *can* be >0, 0 or -1, so is actually
# c_long
prototype = WINFUNCTYPE(c_long, LPMSG, HWND, UINT, UINT)
paramflags = (1, "lpMsg"), (1, "hWnd"), (1, "wMsgFilterMin"), (1, "wMsgFilterMax")
GetMessageW = prototype(("GetMessageW", windll.user32), paramflags)
TranslateMessage = windll.user32.TranslateMessage TranslateMessage = windll.user32.TranslateMessage
DispatchMessageW = windll.user32.DispatchMessageW
PostThreadMessageW = windll.user32.PostThreadMessageW
SendMessageW = windll.user32.SendMessageW
SendMessageW.argtypes = [HWND, UINT, WPARAM, LPARAM]
PostMessageW = windll.user32.PostMessageW
PostMessageW.argtypes = [HWND, UINT, WPARAM, LPARAM]
# https://docs.microsoft.com/en-us/windows/win32/dataxchg/wm-dde-initiate # https://docs.microsoft.com/en-us/windows/win32/dataxchg/wm-dde-initiate
WM_DDE_INITIATE = 0x03E0 WM_DDE_INITIATE = 0x03E0
@ -148,12 +118,6 @@ if (config.auth_force_edmc_protocol # noqa: C901
GlobalGetAtomNameW = windll.kernel32.GlobalGetAtomNameW GlobalGetAtomNameW = windll.kernel32.GlobalGetAtomNameW
GlobalGetAtomNameW.argtypes = [ATOM, LPWSTR, INT] GlobalGetAtomNameW.argtypes = [ATOM, LPWSTR, INT]
GlobalGetAtomNameW.restype = UINT GlobalGetAtomNameW.restype = UINT
GlobalLock = windll.kernel32.GlobalLock
GlobalLock.argtypes = [HGLOBAL]
GlobalLock.restype = LPVOID
GlobalUnlock = windll.kernel32.GlobalUnlock
GlobalUnlock.argtypes = [HGLOBAL]
GlobalUnlock.restype = BOOL
# Windows Message handler stuff (IPC) # Windows Message handler stuff (IPC)
# https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms633573(v=vs.85) # https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms633573(v=vs.85)
@ -171,7 +135,7 @@ if (config.auth_force_edmc_protocol # noqa: C901
if message != WM_DDE_INITIATE: if message != WM_DDE_INITIATE:
# Not a DDE init message, bail and tell windows to do the default # Not a DDE init message, bail and tell windows to do the default
# https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-defwindowproca?redirectedfrom=MSDN # https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-defwindowproca?redirectedfrom=MSDN
return DefWindowProcW(hwnd, message, wParam, lParam) return win32gui.DefWindowProc(hwnd, message, wParam, lParam)
service = create_unicode_buffer(256) service = create_unicode_buffer(256)
topic = create_unicode_buffer(256) topic = create_unicode_buffer(256)
@ -196,7 +160,7 @@ if (config.auth_force_edmc_protocol # noqa: C901
if target_is_valid and topic_is_valid: if target_is_valid and topic_is_valid:
# if everything is happy, send an acknowledgement of the DDE request # if everything is happy, send an acknowledgement of the DDE request
SendMessageW( win32gui.SendMessage(
wParam, WM_DDE_ACK, hwnd, PackDDElParam(WM_DDE_ACK, GlobalAddAtomW(appname), GlobalAddAtomW('System')) wParam, WM_DDE_ACK, hwnd, PackDDElParam(WM_DDE_ACK, GlobalAddAtomW(appname), GlobalAddAtomW('System'))
) )
@ -229,7 +193,7 @@ if (config.auth_force_edmc_protocol # noqa: C901
thread = self.thread thread = self.thread
if thread: if thread:
self.thread = None self.thread = None
PostThreadMessageW(thread.ident, win32con.WM_QUIT, 0, 0) win32api.PostThreadMessage(thread.ident, win32con.WM_QUIT, 0, 0)
thread.join() # Wait for it to quit thread.join() # Wait for it to quit
def worker(self) -> None: def worker(self) -> None:
@ -239,24 +203,25 @@ if (config.auth_force_edmc_protocol # noqa: C901
wndclass.lpfnWndProc = WndProc wndclass.lpfnWndProc = WndProc
wndclass.cbClsExtra = 0 wndclass.cbClsExtra = 0
wndclass.cbWndExtra = 0 wndclass.cbWndExtra = 0
wndclass.hInstance = windll.kernel32.GetModuleHandleW(0) wndclass.hInstance = win32gui.GetModuleHandle(0)
wndclass.hIcon = None wndclass.hIcon = None
wndclass.hCursor = None wndclass.hCursor = None
wndclass.hbrBackground = None wndclass.hbrBackground = None
wndclass.lpszMenuName = None wndclass.lpszMenuName = None
wndclass.lpszClassName = 'DDEServer' wndclass.lpszClassName = 'DDEServer'
if not RegisterClassW(byref(wndclass)): if not win32gui.RegisterClass(byref(wndclass)):
print('Failed to register Dynamic Data Exchange for cAPI') print('Failed to register Dynamic Data Exchange for cAPI')
return return
# https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw # https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw
hwnd = CreateWindowExW( hwnd = win32gui.CreateWindowEx(
0, # dwExStyle 0, # dwExStyle
wndclass.lpszClassName, # lpClassName wndclass.lpszClassName, # lpClassName
"DDE Server", # lpWindowName "DDE Server", # lpWindowName
0, # dwStyle 0, # dwStyle
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, # X, Y, nWidth, nHeight # X, Y, nWidth, nHeight
win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT,
self.master.winfo_id(), # hWndParent # Don't use HWND_MESSAGE since the window won't get DDE broadcasts self.master.winfo_id(), # hWndParent # Don't use HWND_MESSAGE since the window won't get DDE broadcasts
None, # hMenu None, # hMenu
wndclass.hInstance, # hInstance wndclass.hInstance, # hInstance
@ -276,13 +241,13 @@ if (config.auth_force_edmc_protocol # noqa: C901
# #
# But it does actually work. Either getting a non-0 value and # But it does actually work. Either getting a non-0 value and
# entering the loop, or getting 0 and exiting it. # entering the loop, or getting 0 and exiting it.
while GetMessageW(byref(msg), None, 0, 0) != 0: while win32gui.GetMessage(byref(msg), None, 0, 0) != 0:
logger.trace_if('frontier-auth.windows', f'DDE message of type: {msg.message}') logger.trace_if('frontier-auth.windows', f'DDE message of type: {msg.message}')
if msg.message == WM_DDE_EXECUTE: if msg.message == WM_DDE_EXECUTE:
# GlobalLock does some sort of "please dont move this?" # GlobalLock does some sort of "please dont move this?"
# https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-globallock # https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-globallock
args = wstring_at(GlobalLock(msg.lParam)).strip() args = wstring_at(win32gui.GlobalLock(msg.lParam)).strip()
GlobalUnlock(msg.lParam) # Unlocks the GlobalLock-ed object win32gui.GlobalUnlock(msg.lParam) # Unlocks the GlobalLock-ed object
if args.lower().startswith('open("') and args.endswith('")'): if args.lower().startswith('open("') and args.endswith('")'):
logger.trace_if('frontier-auth.windows', f'args are: {args}') logger.trace_if('frontier-auth.windows', f'args are: {args}')
@ -291,20 +256,20 @@ if (config.auth_force_edmc_protocol # noqa: C901
logger.debug(f'Message starts with {self.redirect}') logger.debug(f'Message starts with {self.redirect}')
self.event(url) self.event(url)
SetForegroundWindow(win32gui.GetParent(self.master.winfo_id())) # raise app window win32gui.SetForegroundWindow(win32gui.GetParent(self.master.winfo_id())) # raise app window
# Send back a WM_DDE_ACK. this is _required_ with WM_DDE_EXECUTE # Send back a WM_DDE_ACK. this is _required_ with WM_DDE_EXECUTE
PostMessageW(msg.wParam, WM_DDE_ACK, hwnd, PackDDElParam(WM_DDE_ACK, 0x80, msg.lParam)) win32gui.PostMessage(msg.wParam, WM_DDE_ACK, hwnd, PackDDElParam(WM_DDE_ACK, 0x80, msg.lParam))
else: else:
# Send back a WM_DDE_ACK. this is _required_ with WM_DDE_EXECUTE # Send back a WM_DDE_ACK. this is _required_ with WM_DDE_EXECUTE
PostMessageW(msg.wParam, WM_DDE_ACK, hwnd, PackDDElParam(WM_DDE_ACK, 0, msg.lParam)) win32gui.PostMessage(msg.wParam, WM_DDE_ACK, hwnd, PackDDElParam(WM_DDE_ACK, 0, msg.lParam))
elif msg.message == WM_DDE_TERMINATE: elif msg.message == WM_DDE_TERMINATE:
PostMessageW(msg.wParam, WM_DDE_TERMINATE, hwnd, 0) win32gui.PostMessage(msg.wParam, WM_DDE_TERMINATE, hwnd, 0)
else: else:
TranslateMessage(byref(msg)) # "Translates virtual key messages into character messages" ??? TranslateMessage(byref(msg)) # "Translates virtual key messages into character messages" ???
DispatchMessageW(byref(msg)) win32gui.DispatchMessage(byref(msg))
else: # Linux / Run from source else: # Linux / Run from source

View File

@ -420,7 +420,7 @@ class StatsResults(tk.Toplevel):
# Ensure fully on-screen # Ensure fully on-screen
if sys.platform == 'win32' and CalculatePopupWindowPosition: if sys.platform == 'win32' and CalculatePopupWindowPosition:
position = RECT() position = RECT()
win32gui.GetWindowRect(win32gui.GetParent(self.winfo_id()), position) win32gui.GetWindowRect(win32gui.GetParent(self.winfo_id()))
if CalculatePopupWindowPosition( if CalculatePopupWindowPosition(
POINT(parent.winfo_rootx(), parent.winfo_rooty()), POINT(parent.winfo_rootx(), parent.winfo_rooty()),
# - is evidently supported on the C side # - is evidently supported on the C side

View File

@ -422,12 +422,7 @@ class _Theme:
self.active = theme self.active = theme
if sys.platform == 'win32': if sys.platform == 'win32':
GWL_STYLE = -16 # noqa: N806 # ctypes import win32con
WS_MAXIMIZEBOX = 0x00010000 # noqa: N806 # ctypes
# tk8.5.9/win/tkWinWm.c:342
GWL_EXSTYLE = -20 # noqa: N806 # ctypes
WS_EX_APPWINDOW = 0x00040000 # noqa: N806 # ctypes
WS_EX_LAYERED = 0x00080000 # noqa: N806 # ctypes
# FIXME: Lose the "treat this like a boolean" bullshit # FIXME: Lose the "treat this like a boolean" bullshit
if theme == self.THEME_DEFAULT: if theme == self.THEME_DEFAULT:
@ -445,14 +440,14 @@ class _Theme:
root.withdraw() root.withdraw()
root.update_idletasks() # Size and windows styles get recalculated here root.update_idletasks() # Size and windows styles get recalculated here
hwnd = win32gui.GetParent(root.winfo_id()) hwnd = win32gui.GetParent(root.winfo_id())
win32gui.SetWindowLong(hwnd, GWL_STYLE, win32gui.SetWindowLong(hwnd, win32con.GWL_STYLE,
win32gui.GetWindowLong(hwnd, GWL_STYLE) & ~WS_MAXIMIZEBOX) # disable maximize win32gui.GetWindowLong(hwnd, win32con.GWL_STYLE) & ~win32con.WS_MAXIMIZEBOX) # disable maximize
if theme == self.THEME_TRANSPARENT: if theme == self.THEME_TRANSPARENT:
win32gui.SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_LAYERED) # Add to taskbar win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, win32con.WS_EX_APPWINDOW | win32con.WS_EX_LAYERED) # Add to taskbar
else: else:
win32gui.SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW) # Add to taskbar win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, win32con.WS_EX_APPWINDOW) # Add to taskbar
root.deiconify() root.deiconify()
root.wait_visibility() # need main window to be displayed before returning root.wait_visibility() # need main window to be displayed before returning