[Python] Can't exit program - win32gui PumpMessages

Discussão em 'Python' iniciado por Stack, Abril 6, 2025.

  1. Stack

    Stack Membro Participativo

    I have code that needs to detect when a new media device is attached / detached.

    I have copied some code based on pywin32 to do so:

    import win32api, win32con, win32gui
    from ctypes import *
    from threading import Thread

    #
    # Device change events (WM_DEVICECHANGE wParam)
    #
    DBT_DEVICEARRIVAL = 0x8000
    DBT_DEVICEQUERYREMOVE = 0x8001
    DBT_DEVICEQUERYREMOVEFAILED = 0x8002
    DBT_DEVICEMOVEPENDING = 0x8003
    DBT_DEVICEREMOVECOMPLETE = 0x8004
    DBT_DEVICETYPESSPECIFIC = 0x8005
    DBT_CONFIGCHANGED = 0x0018

    #
    # type of device in DEV_BROADCAST_HDR
    #
    DBT_DEVTYP_OEM = 0x00000000
    DBT_DEVTYP_DEVNODE = 0x00000001
    DBT_DEVTYP_VOLUME = 0x00000002
    DBT_DEVTYPE_PORT = 0x00000003
    DBT_DEVTYPE_NET = 0x00000004

    #
    # media types in DBT_DEVTYP_VOLUME
    #
    DBTF_MEDIA = 0x0001
    DBTF_NET = 0x0002

    WORD = c_ushort
    DWORD = c_ulong


    class DEV_BROADCAST_HDR(Structure):
    _fields_ = [
    ("dbch_size", DWORD),
    ("dbch_devicetype", DWORD),
    ("dbch_reserved", DWORD)
    ]


    class DEV_BROADCAST_VOLUME(Structure):
    _fields_ = [
    ("dbcv_size", DWORD),
    ("dbcv_devicetype", DWORD),
    ("dbcv_reserved", DWORD),
    ("dbcv_unitmask", DWORD),
    ("dbcv_flags", WORD)
    ]


    def drive_from_mask(mask):
    n_drive = 0
    while 1:
    if (mask & (2 ** n_drive)):
    return n_drive
    else:
    n_drive += 1


    class Notification:
    def __init__(self):
    message_map = {
    win32con.WM_DEVICECHANGE: self.onDeviceChange
    }

    wc = win32gui.WNDCLASS()
    hinst = wc.hInstance = win32api.GetModuleHandle(None)
    wc.lpszClassName = "DeviceChangeDemo"
    wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW
    wc.hCursor = win32gui.LoadCursor(0, win32con.IDC_ARROW)
    wc.hbrBackground = win32con.COLOR_WINDOW
    wc.lpfnWndProc = message_map
    classAtom = win32gui.RegisterClass(wc)
    style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
    self.hwnd = win32gui.CreateWindow(
    classAtom,
    "Device Change Demo",
    style,
    0, 0,
    win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT,
    0, 0,
    hinst, None
    )

    def register_callbacks(self, device_arrival, device_removal):
    self.dvc_arr = device_arrival
    self.dvc_rem = device_removal

    def onDeviceChange(self, hwnd, msg, wparam, lparam):
    #
    # WM_DEVICECHANGE:
    # wParam - type of change: arrival, removal etc.
    # lParam - what's changed?
    # if it's a volume then...
    # lParam - what's changed more exactly
    #
    dev_broadcast_hdr = DEV_BROADCAST_HDR.from_address(lparam)

    if wparam == DBT_DEVICEARRIVAL:
    if dev_broadcast_hdr.dbch_devicetype == DBT_DEVTYP_VOLUME:
    dev_broadcast_volume = DEV_BROADCAST_VOLUME.from_address(lparam)
    drive_letter = drive_from_mask(dev_broadcast_volume.dbcv_unitmask)
    self.dvc_arr(chr(ord("A") + drive_letter))

    if wparam == DBT_DEVICEREMOVECOMPLETE:
    self.dvc_rem()

    return 1


    def listener_thread(clb_arr, clb_rem):
    w = Notification()
    w.register_callbacks(clb_arr, clb_rem)
    win32gui.PumpMessages()


    def create_listener(clb_arr, clb_rem):
    thread = Thread(target = listener_thread, args=(clb_arr, clb_rem))
    thread.start()
    return thread


    if __name__ == '__main__':
    print("Starting listening")
    w = Notification()
    win32gui.PumpMessages()


    Basically I spawn this Notification() class in its own thread, and register some callbacks for when a device is connected / disconnected. But I cannot get out of the win32gui.PumpMessages. Code to exit this:

    t = create_listener(callback_arrival, callback_removal)
    print("Trying to shut down")
    handle_window = win32gui.FindWindow(None, "Device Change Demo")
    win32gui.SendMessage(handle_window, win32con.WM_QUIT, 0, 0)
    print("Sent WM_QUIT message to " + str(handle_window))
    print("Sent message")
    t.join()
    print("Thread joined")
    print("Destroyed windows")
    print("Exiting...")


    This results in the output:

    my handle is 2294256
    Trying to shut down
    Sent WM_QUIT message to 2294256
    Sent message


    Thread is never joined. My callbacks work, but the PumpMessages() just never stops being blocked. The handle I am sending the signal to is the same as the handle during the window creation. Am I doing the sending wrong? Should it be a different handle to send it to?

    Code reference: https://timgolden.me.uk/pywin32-docs/win32gui__PumpMessages_meth.html

    Continue reading...

Compartilhe esta Página