获取创建窗口的hwnd处理程序

时间:2016-12-08 06:17:38

标签: python winapi

我正在尝试创建一个窗口,然后获取所创建窗口的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窗口仍继续运行。

我可以做些什么来解决这些问题?

1 个答案:

答案 0 :(得分:0)

  1. 分配在回调中收到的hwnd与范围有关。 回调函数是异步调用的,因此不可能返回值。 相反,您可以将值分配给另一个作用域中的变量,例如到 全局变量:

    def callback((hWinEventHook, event, hwnd, ...):
        global HWND
        HWND = hwnd
        ....
    
  2. 尽管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)  
    
  3. 要进行摘钩,首先需要GetMessage循环退出。自然 调用UnhookWinEvent的位置在GetMessage之后,因此当后面 退出,将完成下一步的摘机:

    def create_window(hWinEventHook, event, hwnd, ...):
        ...
        m = user32.GetMessage(ctypes.byref(msg), 0, 0, 0)
        user32.UnhookWinEvent( hWinEventHook )
        return HWND
    
  4. 要轻松地将回调中收到的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)