0

We have an app that is built with openframeworks. When started, it first opens a console window that does some work (and stays open) and then starts two more child processes that each open a window in fullscreen, one on each monitor. According to the guy that is building the app, it is impossible to give those two windows titles.

My job is to build a script that:

  1. Checks if the app has crashed and reopens it
  2. Verifies that the windows are in the foreground and one of them is in focus and fixes them if they aren't

I want to reuse an old python script of mine that did exactly this and altered it to fit the bill.

from time import sleep
import subprocess
import psutil
import re
import win32gui
import win32con

client_path = "C:\\path_to_app.exe"
window_name = ""


class WindowMgr:
    """Encapsulates some calls to the winapi for window management"""

    def __init__(self, ):
        """Constructor"""
        self._handle = None

    def find_window(self, class_name, window_name=None):
        """find a window by its class_name"""
        self._handle = win32gui.FindWindow(class_name, window_name)

    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._handle = hwnd

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

    def set_foreground(self):
        """put the window in the foreground"""
        win32gui.SetForegroundWindow(self._handle)

    def maximize(self):
        win32gui.ShowWindow(self._handle, win32con.SW_MAXIMIZE)

    def is_minimized(self):
        return win32gui.IsIconic(self._handle)


def client_crashed():
    for pid in psutil.pids():
        if psutil.Process(pid).name() == "app.exe":
            return False
    return True


if __name__ == "__main__":

    w = WindowMgr()
    w.find_window_wildcard(window_name)
    print("Checking")
    while True:
        if client_crashed() is True:
            print("Reopening app.exe")
            subprocess.Popen([client_path])
        else:
            print("Not crashed")
        if w.is_minimized:
            print("Maximizing")
            w.set_foreground()
            w.maximize()
        else:
            print("Not minimized")

        print("Sleeping for 10")
        sleep(10)

Now the checking for crashing and restarting works just fine. But since the windows have no title, the best I've come up with so far is to check for windows with no name, which apparently opens random programms like the Windows 10 movie programm (or at least brings them to the foreground which is weird because they should not be running).

Is there a better way to bring a window into focus without knowing its name? One thought of mine was to get the parent process and then access the children from there and bring them into focus somehow, but I've not been able to figure out how.

If there are better ways to achieve what I want than using python, I would also be glad for any pointers in that direction.

Alex Eggers
  • 328
  • 3
  • 16

2 Answers2

1

If you have unique window class name you can try GetClassName() https://msdn.microsoft.com/en-us/library/windows/desktop/ms633582(v=vs.85).aspx

Or get the window process GetWindowThreadProcessId() and check whether it's an instance of your app. See How to get the process name in C++

Mihayl
  • 3,821
  • 2
  • 13
  • 32
  • This is code that you would put in the app itself? Im not sure what I would do with this in my script. – Alex Eggers Nov 10 '17 at 12:35
  • No, just a sample of the API you could use to the get name of the process image/exe file if getCalssName not helps. – Mihayl Nov 10 '17 at 12:37
  • Maybe this [List running processes on 64-bit Windows](https://stackoverflow.com/a/1632274/8918119) is more the Python way. – Mihayl Nov 10 '17 at 12:44
  • I have no trouble fetching processes, but I cannot force the window into focus that way. I can get the processes by finding their parent. – Alex Eggers Nov 10 '17 at 12:54
  • Try maximize() first. Here is a similar post [Python Window Activation](https://stackoverflow.com/questions/2090464/python-window-activation) and as last there is a list of conditions for SetWindowsforeground to work see the remarks section [SetForegroundWindow function](https://msdn.microsoft.com/en-us/library/windows/desktop/ms633539(v=vs.85).aspx) – Mihayl Nov 10 '17 at 13:06
1

One thought of mine was to get the parent process and then access the children from there and bring them into focus somehow, but I've not been able to figure out how."

I've been through the very similar issue, I wanted to find a child proccess with random title name, I only had the name of the executable. Here is what I've done.

import win32gui


def windowFocusPassingPID(pid):
    def callback(hwnd, list_to_append):
        list_to_append.append((hwnd, win32gui.GetWindowText(hwnd)))

    window_list = []
    win32gui.EnumWindows(callback, window_list)
    for i in window_list:
        print(i)  # if you want to check each item
        if pid == i[0]:
            print(i)  # printing the found item (id, window_title)
            win32gui.ShowWindow(i[0], 5)
            win32gui.SetForegroundWindow(i[0])
            break

# Here we will call the function, providing a valid PID
windowFocusPassingPID(INSERT_PID_HERE_IT_MUST_BE_AN_INTEGER)

So, this is a function that will call win32gui.EnumWindows, which handles all top-level windows. Those windows will be appended to the window_list, letting us know all the ID's and window names... In the loop, we will iterate over the list and match the PID you want to bring to focus... You can comment both print(i) if you want.

Here is the documentation:

http://timgolden.me.uk/pywin32-docs/win32gui__EnumWindows_meth.html

SanTasso
  • 411
  • 4
  • 3