对于我的一些 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.Thread
的 native_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/