我正在尝试创建一个窗口,然后获取所创建窗口的hwnd。
目前,我调用subprocess.run("run.exe")
来创建窗口,然后继续调用wg.GetForegroundWindow()
来获取前景窗口的hwnd,它应该是新创建的窗口。但是,这不能很好地工作,因为程序必须在它被检测为前景之前完成打开,并且用户可以在平均时间内改变焦点。
是否有更一致的方法来获取hwnd?
编辑: 以下评论帮助我现在正在做的事情:
def create_window():
user32 = ctypes.windll.user32
ole32 = ctypes.windll.ole32
EVENT_OBJECT_CREATE = 0x8000
def callback(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime):
if is_real_window(hwnd):
print(get_text(hwnd), "HOOKED")
user32.UnhookWinEvent(hWinEventHook)
return hwnd
WinEventProcType = ctypes.WINFUNCTYPE(
None,
ctypes.wintypes.HANDLE,
ctypes.wintypes.DWORD,
ctypes.wintypes.HWND,
ctypes.wintypes.LONG,
ctypes.wintypes.LONG,
ctypes.wintypes.DWORD,
ctypes.wintypes.DWORD
)
WinEventProc = WinEventProcType(callback)
user32.SetWinEventHook.restype = ctypes.wintypes.HANDLE
hook = user32.SetWinEventHook(
EVENT_OBJECT_CREATE,
EVENT_OBJECT_CREATE,
0,
WinEventProc,
0,
0,
0
)
if hook == 0:
print('SetWinEventHook failed')
sys.exit(1)
msg = ctypes.wintypes.MSG()
x = subprocess.Popen("C:\\cygwin64\\bin\\mintty.exe")
m = user32.GetMessageW(ctypes.byref(msg), 0, 0, 0)
然而,这面临两个问题。第一个,虽然我可以在回调函数中访问hwnd,但似乎没有办法让create_window返回这个值。其次,即使在调用UnhookWinEvent之后,GetMessage窗口仍继续运行。
我可以做些什么来解决这些问题?
答案 0 :(得分:0)
分配在回调中收到的hwnd
与范围有关。
回调函数是异步调用的,因此不可能返回值。
相反,您可以将值分配给另一个作用域中的变量,例如到
全局变量:
def callback((hWinEventHook, event, hwnd, ...):
global HWND
HWND = hwnd
....
尽管GetMessage
可以永远运行,但实际上它正在等待消息发送。
发布到它的消息队列。
正如Microsoft Docs for the GetMessage function中所述:
从调用线程的消息队列中检索消息。该功能分派传入的已发送消息,直到发布的消息可供检索为止。
在调用GetMessage之前,请记住存储线程ID,例如在全局变量中:
def create_window(..):
global THREAD_ID
THREAD_ID = win32api.GetCurrentThreadId()
...
m = user32.GetMessageW(ctypes.byref(msg), 0, 0, 0)
因此,任何发布到安装该挂钩的线程的消息都会使 GetMessage停止“运行”并返回一个值:
def callback(...):
...
win32gui.PostThreadMessage(THREAD_ID, win32con.WM_QUIT, 0, 0)
# also valid:
# win32gui.PostThreadMessage(THREAD_ID, win32con.WM_MOVE, 0, 0)
要进行摘钩,首先需要GetMessage循环退出。自然
调用UnhookWinEvent
的位置在GetMessage
之后,因此当后面
退出,将完成下一步的摘机:
def create_window(hWinEventHook, event, hwnd, ...):
...
m = user32.GetMessage(ctypes.byref(msg), 0, 0, 0)
user32.UnhookWinEvent( hWinEventHook )
return HWND
要轻松地将回调中收到的hwnd
与可执行文件进行匹配,
有一个有用的getProcessFileaname
函数,它返回完整路径
到可执行文件(由于要点的作者)
keturn/trackwindow.py)
"""
Much credit to Eric Blade for this:
https://mail.python.org/pipermail/python-win32/2009-July/009381.html
and David Heffernan:
http://stackoverflow.com/a/15898768/9585
"""
import win32con
import ctypes
import ctypes.wintypes
user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32
# limited information would be sufficient, but our platform doesn't have it.
processFlag = getattr(win32con, 'PROCESS_QUERY_LIMITED_INFORMATION',
win32con.PROCESS_QUERY_INFORMATION)
threadFlag = getattr(win32con, 'THREAD_QUERY_LIMITED_INFORMATION',
win32con.THREAD_QUERY_INFORMATION)
def getProcessID(dwEventThread, hwnd):
# It's possible to have a window we can get a PID out of when the thread
# isn't accessible, but it's also possible to get called with no window,
# so we have two approaches.
hThread = kernel32.OpenThread(threadFlag, 0, dwEventThread)
if hThread:
try:
processID = kernel32.GetProcessIdOfThread(hThread)
if not processID:
print("Couldn't get process for thread %s: %s" %
(hThread, ctypes.WinError()))
finally:
kernel32.CloseHandle(hThread)
else:
errors = ["No thread handle for %s: %s" %
(dwEventThread, ctypes.WinError(),)]
if hwnd:
processID = ctypes.wintypes.DWORD()
threadID = user32.GetWindowThreadProcessId(
hwnd, ctypes.byref(processID))
if threadID != dwEventThread:
print("Window thread != event thread? %s != %s" %
(threadID, dwEventThread))
if processID:
processID = processID.value
else:
errors.append(
"GetWindowThreadProcessID(%s) didn't work either: %s" % (
hwnd, ctypes.WinError()))
processID = None
else:
processID = None
if not processID:
for err in errors:
print(err)
return processID
def getProcessFilename(processID):
hProcess = kernel32.OpenProcess(processFlag, 0, processID)
if not hProcess:
print("OpenProcess(%s) failed: %s" % (processID, ctypes.WinError()))
return None
try:
filenameBufferSize = ctypes.wintypes.DWORD(4096)
filename = ctypes.create_unicode_buffer(filenameBufferSize.value)
kernel32.QueryFullProcessImageNameW(hProcess, 0, ctypes.byref(filename),
ctypes.byref(filenameBufferSize))
return filename.value
finally:
kernel32.CloseHandle(hProcess)