New submission from Eryk Sun <[email protected]>:
A console script should be able to handle Windows console logoff and shutdown
events with relatively simple ctypes code, such as the following:
import ctypes
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
CTRL_C_EVENT = 0
CTRL_BREAK_EVENT = 1
CTRL_CLOSE_EVENT = 2
CTRL_LOGOFF_EVENT = 5
CTRL_SHUTDOWN_EVENT = 6
@ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_ulong)
def console_ctrl_handler(event):
if event == CTRL_SHUTDOWN_EVENT:
return handle_shutdown()
if event == CTRL_LOGOFF_EVENT:
return handle_logoff()
if event == CTRL_CLOSE_EVENT:
return handle_close()
if event == CTRL_BREAK_EVENT:
return handle_break()
if event == CTRL_C_EVENT:
return handle_cancel()
return False # chain to next handler
if not kernel32.SetConsoleCtrlHandler(console_ctrl_handler, True):
raise ctypes.WinError(ctypes.get_last_error())
As of 3.9, it's not possible for python.exe to receive the above logoff and
shutdown events via ctypes. In these two cases, the console doesn't even get to
send a close event, so a console script cannot exit gracefully.
The session server (csrss.exe) doesn't send the logoff and shutdown console
events to python.exe because it's seen as a GUI process, which is expected to
handle WM_QUERYENDSESSION and WM_ENDSESSION instead. That requires creating a
hidden window and running a message loop, which is not nearly as simple as a
console control handler.
The system registers python.exe as a GUI process because user32.dll is loaded,
which means the process is ready to interact with the desktop in every way,
except for the final step of actually creating UI objects. In particular,
loading user32.dll causes the system to extend the process and its threads with
additional kernel data structures for use by win32k.sys (e.g. a message queue
for each thread). It also opens handles for and connects to the session's
"WinSta0" interactive window station (a container for an atom table, clipboard,
and desktops) and "Default" desktop (a container for UI objects such as
windows, menus, and hooks). (The process can connect to a different desktop or
window station if set in the lpDesktop field of the process startup info. Also,
if the process access token is for a service or batch logon, by default it
connects to a non-interactive window station that's named for the logon ID. For
example, the SYSTEM logon ID is 0x3e7, so a SYSTEM service or batch pro
cess gets connected to "Service-0x0-3e7$".)
Prior to 3.9, python3x.dll loads shlwapi.dll (the lightweight shell API) to
access the Windows path functions PathCanonicalizeW and PathCombineW.
shlwapi.dll in turn loads user32.dll. 3.9+ is one step closer to the non-GUI
goal because it no longer depends on shlwapi.dll. Instead it always uses the
newer PathCchCanonicalizeEx and PathCchCombineEx functions from
api-ms-win-core-path-l1-1-0.dll, which is implemented by the base API
(kernelbase.dll) instead of the shell API.
The next hurdle is extension modules, especially the _ctypes extension module,
since it's needed for the console control handler. _ctypes.pyd loads ole32.dll,
which in turn loads user32.dll. This is just to call ProgIDFromCLSID, which is
rarely used. I see no reason that ole32.dll can't be delay loaded or just
manually link to ProgIDFromCLSID on first use via GetModuleHandleW /
LoadLibraryExW and GetProcAddress. I did a quick patch to implement the latter,
and, since user32.dll no longer gets loaded, the console control handler is
enabled for console logoff and shutdown events. So this is the minimal fix to
resolve this issue in 3.9+.
---
Additional modules
winsound loads user32.dll for MessageBeep. The Beep and PlaySound functions
don't require user32.dll, so winsound is still useful if it gets delay loaded.
_ssl and _hashlib depend on libcrypto, which loads user32.dll for MessageBoxW,
GetProcessWindowStation and GetUserObjectInformationW. The latter two are
called in OPENSSL_isservice [1] in order to get the window station name. If
StandardError isn't a valid file handle, OPENSSL_isservice determines whether
an error should be reported as an event or interactively shown with a message
box. user32.dll can be delay loaded for this, which, if I'm reading the source
right, will never occur as long as StandardError is a valid file.
[1]:
https://github.com/openssl/openssl/blob/e7fb44e7c3f7a37ff83a6b69ba51a738e549bf5c/crypto/cryptlib.c#L193
----------
components: Extension Modules, Library (Lib), Windows, ctypes
messages: 373659
nosy: eryksun, paul.moore, steve.dower, tim.golden, zach.ware
priority: normal
severity: normal
stage: needs patch
status: open
title: Enable handling logoff and shutdown Windows console events
type: enhancement
versions: Python 3.10, Python 3.9
_______________________________________
Python tracker <[email protected]>
<https://bugs.python.org/issue41298>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com