python user32.GetMessage永远不会退出

标签 python

对于我的一些 Python 应用程序,我使用 ctypes 和 Windows API 安装系统 Hook ,为了使这些应用程序正常工作,必须运行消息循环。因为我在前台发生了其他事情,所以这个消息循环被设置为作为后台线程运行。这部分工作正常,但是当我想停止这个后台线程时,我无法让它退出,当收到 WM_QUIT 消息时,GetMessage 函数应该返回 0,并且我的消息循环的主体应该返回 0,但由于某种原因它永远不会返回。 我尝试过 SendMessage、PostMessage、PostThreadMessage 和 PostQuitMessage 的各种组合,但没有成功。

#!python3

import tkinter as tk
import ctypes
import ctypes.wintypes
from threading import Thread

user32 = ctypes.windll.user32

def loop():
    msg = ctypes.wintypes.MSG()
    while user32.GetMessageW(ctypes.byref(msg), 0, 0, 0) != 0:
        print('msg')
        user32.TranslateMessageW(msg)
        user32.DispatchMessageW(msg)
    print('end of loop')

def SendMessage1():
    user32.SendMessageA(0xFFFF, 0x001C, 0, 0) # broadcast handle, app_activate message

def SendMessage2():
    user32.SendMessageA(0, 0x001C, 0, 0) # null handle

def PostMessage1():
    user32.PostMessageA(0xFFFF, 0x001C, 0, 0)

def PostMessage2():
    user32.PostMessageA(0, 0x001C, 0, 0)

def PostThreadMessage1():
    user32.PostThreadMessageA(0xFFFF, 0x001C, 0, 0)

def PostThreadMessage2():
    user32.PostThreadMessageA(0, 0x001C, 0, 0)

def PostQuitMessage1():
    user32.PostQuitMessage(0)

def PostQuitMessage2():
    user32.PostQuitMessage(0)

root = tk.Tk()

t = Thread(target=loop)
t.daemon=True
t.start()

tk.Button(root, text='SendMessage 1', command=SendMessage1).pack()
tk.Button(root, text='SendMessage 2', command=SendMessage2).pack()
tk.Button(root, text='PostMessage 1', command=PostMessage1).pack()
tk.Button(root, text='PostMessage 2', command=PostMessage2).pack()
tk.Button(root, text='PostThreadMessage 1', command=PostThreadMessage1).pack()
tk.Button(root, text='PostThreadMessage 2', command=PostThreadMessage2).pack()
tk.Button(root, text='PostQuitMessage 1', command=PostQuitMessage1).pack()
tk.Button(root, text='PostQuitMessage 2', command=PostQuitMessage2).pack()

root.mainloop()

我还尝试了这些函数的 A 和 W 变体。我认为问题可能是因为我无法获取实际监听消息的线程的句柄,并且我也找不到任何相关文档。

我还考虑过在该线程上下文中引发异常,但由于挂起是在 dll 中而不是在 python 代码中,GIL 已被释放,这不会导致它退出。

如何让这个线程正常退出?

最佳答案

发现我确实需要线程的句柄,并且可以使用 threading.Threadnative_id 属性来访问它:

#!python3

import tkinter as tk
import ctypes
import ctypes.wintypes
from threading import Thread

user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32

WM_QUIT = 0x0012

tid = None

def loop():
    msg = ctypes.wintypes.MSG()
    while user32.GetMessageW(ctypes.byref(msg), 0, 0, 0) != 0:
        print('msg')
        user32.TranslateMessage(msg)
        user32.DispatchMessageW(msg)
    print('end of loop')
    
def PostThreadMessage():
    user32.PostThreadMessageW(tid, WM_QUIT, 0, 0)

root = tk.Tk()

t = Thread(target=loop)
t.daemon=True
t.start()
tid = t.native_id

tk.Button(root, text='PostThreadMessage', command=PostThreadMessage).pack()

root.mainloop()

关于python user32.GetMessage永远不会退出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50603171/

相关文章:

python - 尝试建立反函数

python - 如何在包中使用 __init__.py 文件?

python - 如何向 dbf 文件添加字段?

python - 标准化 Pandas 中的长数据

python - 来自 Postman 的 Django POST 请求

Python HTTP 错误 505 : HTTP Version Not Supported

python - 如何强制Python 3使用单引号输出字符串?

python - 在 Python 中打开一个压缩的 SQLite 数据库

python - 从混合类型列表构造字典

python - 在 GUI 嵌入图中使用 matplotlib Cursor 小部件