The main goal of my project is to take a screenshot of a specific application.
The steps that i need to perform are the following:
List all the visible windows with relative title and pid
The user enter in input the pid of the window choosen
The program gets the coordinates of the window by using the pid and than take a screenshot.
In order to get all the visible windows I'm using the following function
from sys import platform as sys_platform
if sys_platform == 'win32':
from win32gui import EnumWindows, GetWindowText, IsWindowVisible, IsIconic
from win32process import GetWindowThreadProcessId
def get_visible_windows():
if sys_platform == 'win32':
visible_windows = []
def callback(hwnd, _):
if IsWindowVisible(hwnd) and not IsIconic(hwnd):
_, cpid = GetWindowThreadProcessId(hwnd)
title = GetWindowText(hwnd)
if title != '':
visible_windows.append({'Title' : title, 'Pid' : cpid})
EnumWindows(callback, None)
return visible_windows
The problem are two:
The windows returned are more than expected, for example the "NVIDIA GeForce Overlay" window has the "WS_VISIBLE" attribute but in reality it is invisible.
The pid associated with some windows is wrong and it refers to "ApplicationFrameHost.exe". I checked in the task manager and the correct pid is associated with another process of the same program but that does not have the "WS_VISIBLE" attribute.
I need something that substitute IsWindowVisible() in order to filter all the windows.
A partial solution for the point number 2 could be to find the PID of "ApplicationFrameHost.exe" and filter all the windows eliminating those with that PID but this will solve only a part of the second problem.
After more researches I finally found the solution to the first point thanks to this post: Filtering Background Processes PyWin32
The final code is the following:
def get_visible_windows():
if sys_platform == 'win32':
class TITLEBARINFO(ctypes.Structure):
_fields_ = [("cbSize", ctypes.wintypes.DWORD), ("rcTitleBar", ctypes.wintypes.RECT),
("rgstate", ctypes.wintypes.DWORD * 6)]
visible_windows = []
def callback(hwnd, _):
# Title Info Initialization
title_info = TITLEBARINFO()
title_info.cbSize = ctypes.sizeof(title_info)
ctypes.windll.user32.GetTitleBarInfo(hwnd, ctypes.byref(title_info))
# DWM Cloaked Check
isCloaked = ctypes.c_int(0)
ctypes.WinDLL("dwmapi").DwmGetWindowAttribute(hwnd, 14, ctypes.byref(isCloaked), ctypes.sizeof(isCloaked))
# Variables
title = GetWindowText(hwnd)
# Append HWND to list
if not IsIconic(hwnd) and IsWindowVisible(hwnd) and title != '' and isCloaked.value == 0:
if not (title_info.rgstate[0] & STATE_SYSTEM_INVISIBLE):
_, cpid = GetWindowThreadProcessId(hwnd)
visible_windows.append({'title' : title, 'pid' : cpid, 'hwnd' : hwnd})
EnumWindows(callback, None)
return visible_windows
elif sys_platform == 'linux':
return
While for the second problem I solved by using the "hwnd" instead of the pid.