Python win32gui SetAsForegroundWindow function not

2020-02-25 08:19发布

I am trying to write a program that finds a window by searching for its title. Once it has found the window, it will attempt to bring it to front. I am using win32gui API to achieve this. I am able to get it to work for the most part, but for some reason it does not work if the taskmanager is in front. I have the follow sample code.

import win32gui, win32con
import re, traceback
from time import sleep

class cWindow:
    def __init__(self):
        self._hwnd = None

    def BringToTop(self):
        win32gui.BringWindowToTop(self._hwnd)

    def SetAsForegroundWindow(self):
        win32gui.SetForegroundWindow(self._hwnd)

    def Maximize(self):
        win32gui.ShowWindow(self._hwnd, win32con.SW_MAXIMIZE)

    def setActWin(self):
        win32gui.SetActiveWindow(self._hwnd)

    def _window_enum_callback(self, hwnd, wildcard):
        '''Pass to win32gui.EnumWindows() to check all the opened windows'''
        if re.match(wildcard, str(win32gui.GetWindowText(hwnd))) != None:
            self._hwnd = hwnd

    def find_window_wildcard(self, wildcard):
        self._hwnd = None
        win32gui.EnumWindows(self._window_enum_callback, wildcard)


def main():
    sleep(5)
    try:      
        wildcard = ".*Building Operation WorkStation.*"
        cW = cWindow()
        cW.find_window_wildcard(wildcard)
        cW.Maximize()
        cW.BringToTop()
        cW.SetAsForegroundWindow()

    except:
        f = open("log.txt", "w")
        f.write(traceback.format_exc())
        print traceback.format_exc()
main()

I pieced this together from multiple online sources. It seems to work for the most part but for some windows like the task manager, it'll work sometimes but fails the rest. When it doesnt work properly, all I notice is the application icon blinks yellow. Is there a proper way of doing this to make sure the window that I am interested in is set to foreground 100% of the times? I am not sure if this is relevant but I am using Windows 7 Professional (32-bit) with Service Pack 1.

2条回答
We Are One
2楼-- · 2020-02-25 08:50

Note: The following deals only with making sure that always-on-top windows such as Task Manager are hidden before activating the window - it assumes that the activation part itself works fine, which may not be the case. The conditions under which a process is allowed to call the SetForegroundWindow Windows API function are listed here.


Task Manager is special in two respects:

  • By default, it is set to display always on top, i.e., above all other windows.
  • Even when that is turned off (Options > Always on Top unchecked), you can still make it display on top of other always-on-top windows (something that ordinary windows seemingly cannot do).

Your code:

  • is working - in my tests - in the sense that the target window does become the active window.
  • is not working in the sense that the Task Manager window still stays on top of the (maximized) window.
    • even trying to make your window an always-on-top window as well wouldn't help, unfortunately.

Specifically checking for the presence of the Task Manager window and minimizing it is an option, but note that there may be other always-on-top windows, so for a robust solution you have to identify all open always-on-top windows and minimize them:

The following tries hard to identify all always-on-top windows except the Task Bar and Start button, and minimizes (effectively hides) any such windows.

The new methods are hide_always_on_top_windows and _window_enum_callback_hide.

import win32gui, win32con
import re, traceback
from time import sleep

class cWindow:
    def __init__(self):
        self._hwnd = None

    def SetAsForegroundWindow(self):
        # First, make sure all (other) always-on-top windows are hidden.
        self.hide_always_on_top_windows() 
        win32gui.SetForegroundWindow(self._hwnd)

    def Maximize(self):
        win32gui.ShowWindow(self._hwnd, win32con.SW_MAXIMIZE)

    def _window_enum_callback(self, hwnd, regex):
        '''Pass to win32gui.EnumWindows() to check all open windows'''
        if self._hwnd is None and re.match(regex, str(win32gui.GetWindowText(hwnd))) is not None:
            self._hwnd = hwnd

    def find_window_regex(self, regex):
        self._hwnd = None
        win32gui.EnumWindows(self._window_enum_callback, regex)

    def hide_always_on_top_windows(self):
        win32gui.EnumWindows(self._window_enum_callback_hide, None)

    def _window_enum_callback_hide(self, hwnd, unused):
        if hwnd != self._hwnd: # ignore self
            # Is the window visible and marked as an always-on-top (topmost) window?
            if win32gui.IsWindowVisible(hwnd) and win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE) & win32con.WS_EX_TOPMOST:
                # Ignore windows of class 'Button' (the Start button overlay) and
                # 'Shell_TrayWnd' (the Task Bar).
                className = win32gui.GetClassName(hwnd)
                if not (className == 'Button' or className == 'Shell_TrayWnd'):
                    # Force-minimize the window.
                    # Fortunately, this seems to work even with windows that
                    # have no Minimize button.
                    # Note that if we tried to hide the window with SW_HIDE,
                    # it would disappear from the Task Bar as well.
                    win32gui.ShowWindow(hwnd, win32con.SW_FORCEMINIMIZE)

def main():
    sleep(5)
    try:      
        regex = ".*Building Operation WorkStation.*"
        cW = cWindow()
        cW.find_window_regex(regex)
        cW.Maximize()
        cW.SetAsForegroundWindow()

    except:
        f = open("log.txt", "w")
        f.write(traceback.format_exc())
        print(traceback.format_exc())
main()
查看更多
太酷不给撩
3楼-- · 2020-02-25 08:55

I found a solution: if taskmanager, then kill it. I added a method to cWindow:

def kill_task_manager(self):
    # Here I use your method to find a window because of an accent in my french OS,
    # but you should use win32gui.FindWindow(None, 'Task Manager complete name').
    wildcard = 'Gestionnaire des t.+ches de Windows'
    self.find_window_wildcard(wildcard)
    if self._hwnd:
        win32gui.PostMessage(self._hwnd, win32con.WM_CLOSE, 0, 0)  # kill it
        sleep(0.5)  # important to let time for the window to be closed

Call this method just after cW = cWindow().

Another bug trap is to prevent this exception in SetAsForegroundWindow:

error: (0, 'SetForegroundWindow', 'No error message is available')

just send an alt key before the win32gui call:

# Add this import
import win32com.client

# Add this to __ini__
self.shell = win32com.client.Dispatch("WScript.Shell")

# And SetAsForegroundWindow becomes
def SetAsForegroundWindow(self):
    self.shell.SendKeys('%')
    win32gui.SetForegroundWindow(self._hwnd)

Last, if I may, do not compare != None but is not None. More pythonic ;)

This is the full code:

# coding: utf-8

import re, traceback
import win32gui, win32con, win32com.client
from time import sleep


class cWindow:
    def __init__(self):
        self._hwnd = None
        self.shell = win32com.client.Dispatch("WScript.Shell")

    def BringToTop(self):
        win32gui.BringWindowToTop(self._hwnd)

    def SetAsForegroundWindow(self):
        self.shell.SendKeys('%')
        win32gui.SetForegroundWindow(self._hwnd)

    def Maximize(self):
        win32gui.ShowWindow(self._hwnd, win32con.SW_MAXIMIZE)

    def setActWin(self):
        win32gui.SetActiveWindow(self._hwnd)

    def _window_enum_callback(self, hwnd, wildcard):
        '''Pass to win32gui.EnumWindows() to check all the opened windows'''
        if re.match(wildcard, str(win32gui.GetWindowText(hwnd))) is not None:
            self._hwnd = hwnd

    def find_window_wildcard(self, wildcard):
        self._hwnd = None
        win32gui.EnumWindows(self._window_enum_callback, wildcard)

    def kill_task_manager(self):
        wildcard = 'Gestionnaire des t.+ches de Windows'
        self.find_window_wildcard(wildcard)
        if self._hwnd:
            win32gui.PostMessage(self._hwnd, win32con.WM_CLOSE, 0, 0)
            sleep(0.5)

def main():
    sleep(5)
    try:
        wildcard = ".*Building Operation WorkStation.*"
        cW = cWindow()
        cW.kill_task_manager()
        cW.find_window_wildcard(wildcard)
        cW.BringToTop()
        cW.Maximize()
        cW.SetAsForegroundWindow()

    except:
        f = open("log.txt", "w")
        f.write(traceback.format_exc())
        print(traceback.format_exc())


if __name__ == '__main__':
    main()

Sources: how do I close window with handle using win32gui in Python and win32gui.SetActiveWindow() ERROR : The specified procedure could not be found.

查看更多
登录 后发表回答