capture_devtools_bg.py
Python script that captures a screenshot of the Dev Tools Chrome browser window without bringing it to the foreground, using the Win32 PrintWindow API. Identifies the correct window by matching chrome.exe processes that were launched with the chrome-devtools-profile flag.
"""
Capture a screenshot of the Dev Tools Chrome browser window WITHOUT bringing it
to the foreground, using the Win32 PrintWindow API.
Usage: python capture_devtools_bg.py [output_path]
"""
import ctypes
import ctypes.wintypes
import subprocess
import sys
# We need win32 modules
try:
import win32gui
import win32ui
import win32con
HAS_WIN32 = True
except ImportError:
HAS_WIN32 = False
from PIL import Image
def get_devtools_pids():
"""Get all PIDs of chrome.exe processes using the chrome-devtools-profile."""
result = subprocess.run(
'wmic process where "name=\'chrome.exe\' and commandline like \'%chrome-devtools-profile%\'" get processid',
shell=True, capture_output=True, text=True
)
pids = set()
for line in result.stdout.splitlines():
line = line.strip()
if line.isdigit():
pids.add(int(line))
return pids
def find_main_chrome_window(pids):
"""Find the main Chrome window HWND from the set of PIDs."""
GetWindowThreadProcessId = ctypes.windll.user32.GetWindowThreadProcessId
IsWindowVisible = ctypes.windll.user32.IsWindowVisible
GetWindowTextW = ctypes.windll.user32.GetWindowTextW
GetWindowTextLengthW = ctypes.windll.user32.GetWindowTextLengthW
EnumWindows = ctypes.windll.user32.EnumWindows
WNDENUMPROC = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.wintypes.HWND, ctypes.wintypes.LPARAM)
candidates = []
def callback(hwnd, _):
if not IsWindowVisible(hwnd):
return True
pid = ctypes.wintypes.DWORD()
GetWindowThreadProcessId(hwnd, ctypes.byref(pid))
if pid.value in pids:
length = GetWindowTextLengthW(hwnd)
if length > 0:
buf = ctypes.create_unicode_buffer(length + 1)
GetWindowTextW(hwnd, buf, length + 1)
title = buf.value
# Skip small dialog/notification windows; keep the main browser
if title and 'wants to' not in title:
candidates.append((hwnd, title))
return True
EnumWindows(WNDENUMPROC(callback), 0)
if candidates:
# Prefer the one with the longest title (usually the main content window)
candidates.sort(key=lambda x: len(x[1]), reverse=True)
return candidates[0]
return None, None
def capture_window_background_pil(hwnd, output_path):
"""Capture window using PIL ImageGrab with GetWindowRect (no foreground needed
if window is visible, but won't capture if occluded)."""
rect = ctypes.wintypes.RECT()
ctypes.windll.user32.GetWindowRect(hwnd, ctypes.byref(rect))
from PIL import ImageGrab
img = ImageGrab.grab(bbox=(rect.left, rect.top, rect.right, rect.bottom))
img.save(output_path)
return True
def capture_window_background_win32(hwnd, output_path):
"""Capture window content using PrintWindow - works even if window is behind others."""
rect = ctypes.wintypes.RECT()
ctypes.windll.user32.GetWindowRect(hwnd, ctypes.byref(rect))
width = rect.right - rect.left
height = rect.bottom - rect.top
hwndDC = win32gui.GetWindowDC(hwnd)
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
saveDC = mfcDC.CreateCompatibleDC()
saveBitMap = win32ui.CreateBitmap()
saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)
saveDC.SelectObject(saveBitMap)
# PW_RENDERFULLCONTENT = 2 captures even DWM-composited content
result = ctypes.windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 2)
bmpinfo = saveBitMap.GetInfo()
bmpstr = saveBitMap.GetBitmapBits(True)
img = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']),
bmpstr, 'raw', 'BGRX', 0, 1)
img.save(output_path)
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
return True
if __name__ == '__main__':
output = sys.argv[1] if len(sys.argv) > 1 else 'devtools_screenshot.png'
pids = get_devtools_pids()
if not pids:
print("ERROR: No chrome-devtools-profile processes found.")
sys.exit(1)
print(f"Found {len(pids)} devtools PIDs")
hwnd, title = find_main_chrome_window(pids)
if not hwnd:
print("ERROR: Could not find Dev Tools Chrome window.")
sys.exit(1)
print(f"Found window: '{title}' (HWND {hwnd})")
if HAS_WIN32:
print("Using PrintWindow (background capture)...")
capture_window_background_win32(hwnd, output)
else:
print("win32gui not available, falling back to PIL grab (may capture foreground)...")
capture_window_background_pil(hwnd, output)
print(f"Saved to: {output}")