nat-echlin
nat-echlin

Reputation: 63

How do I run a function when the console window is closed?

I have a main process (main.py) that is designed to run forever. It will only stop running when I close the console window. This is bundled as an .exe that I create using PyInstaller.

When I close the console window, I need it to run a function logout, which sends an HTTP request to an AWS Lambda function.

How do I accomplish this?

Upvotes: 1

Views: 666

Answers (2)

John Zhang
John Zhang

Reputation: 103

Here is an example of using ctypes, which is built into Python.

import time
import ctypes
from ctypes import wintypes

_HandlerRoutine = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD)

CTRL_C_EVENT = 0
CTRL_BREAK_EVENT = 1
CTRL_CLOSE_EVENT = 2
CTRL_LOGOFF_EVENT = 5
CTRL_SHUTDOWN_EVENT = 6

def ctrl_handler(ctrl_type):
    if ctrl_type in (CTRL_C_EVENT, CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT, CTRL_SHUTDOWN_EVENT, CTRL_LOGOFF_EVENT):
        print(f"Ctrl handler received {ctrl_type}.")
        time.sleep(2)
        return True

    return False

_ctrl_handler = _HandlerRoutine(ctrl_handler)
kernel32 = ctypes.windll.kernel32
if not kernel32.SetConsoleCtrlHandler(_ctrl_handler, True):
    raise ctypes.WinError(ctypes.get_last_error())

try:
    print(f"process ready")
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    pass

It's also worth noting that if a program is not attached to a console, SetConsoleCtrlHandler has no effect. SetConsoleCtrlHandler should be called after a program is attached to a console or allocates a console.

If you want to run clean-up code when the program exits, try safe-exit

Upvotes: 1

nat-echlin
nat-echlin

Reputation: 63

To answer my own question for future users;

import win32con
import win32api

win32api.SetConsoleCtrlHandler(exit_handler, 1)


def logout():
    # do something


def exit_handler(event):
    if event in [win32con.CTRL_C_EVENT, win32con.CTRL_LOGOFF_EVENT,
                         win32con.CTRL_BREAK_EVENT, win32con.CTRL_SHUTDOWN_EVENT,
                         win32con.CTRL_CLOSE_EVENT]:
        logout()

Where logout is the function I want to run on console closure.

Be advised that you will want to only register the function once, as the same function can and will be registered more than once if you call SetConsoleCtrlHandler multiple times.

It may also be a good practice to have a global flag of sorts, that will make sure the exit handler runs only once in the event of Windows sending multiple CTRL events.

If your logout function takes more than 1-2 seconds to execute, the entire function may not run, as most console CTRL events are not ones that can be caught - they are solely alerts that Windows is about to end your process, and it is not possible to prevent closure.

Upvotes: 2

Related Questions