# -*- coding: utf-8 -*- r'''重新拍电脑端窗口截图(旧图还写着改名前的"局域网遥控 被控端")。 顺便拍几张不同状态的,官网上能展示"这东西长什么样"。 做法:用 --code 启动(非 --silent,所以会显示窗口), 然后用 Win32 的 PrintWindow 把窗口内容抓下来。 不用截整个屏幕 —— 那样会把桌面上的别的东西也拍进去。 ''' from __future__ import annotations import ctypes import os import subprocess import sys import time from ctypes import wintypes from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote") SHOTS = ROOT / "_build" / "site_shots" EXE = ROOT / "dist_exe" / "ykxmz" / "ykxmz.exe" user32 = ctypes.windll.user32 gdi32 = ctypes.windll.gdi32 def find_window(title_part: str) -> int: """按标题片段找窗口句柄。""" found = [] @ctypes.WINFUNCTYPE(ctypes.c_bool, wintypes.HWND, wintypes.LPARAM) def callback(hwnd, _lparam): length = user32.GetWindowTextLengthW(hwnd) if length: buf = ctypes.create_unicode_buffer(length + 1) user32.GetWindowTextW(hwnd, buf, length + 1) if title_part in buf.value and user32.IsWindowVisible(hwnd): found.append((hwnd, buf.value)) return True user32.EnumWindows(callback, 0) return found[0] if found else (0, "") def capture(hwnd: int, path: Path) -> bool: """用 PrintWindow 抓窗口内容(比截屏干净,不带桌面背景)。""" rect = wintypes.RECT() user32.GetWindowRect(hwnd, ctypes.byref(rect)) width = rect.right - rect.left height = rect.bottom - rect.top if width <= 0 or height <= 0: return False hdc_window = user32.GetWindowDC(hwnd) hdc_mem = gdi32.CreateCompatibleDC(hdc_window) bitmap = gdi32.CreateCompatibleBitmap(hdc_window, width, height) gdi32.SelectObject(hdc_mem, bitmap) # 2 = PW_RENDERFULLCONTENT,win10+ 抓 DWM 合成内容必须用这个 ok = user32.PrintWindow(hwnd, hdc_mem, 2) class BITMAPINFOHEADER(ctypes.Structure): _fields_ = [("biSize", wintypes.DWORD), ("biWidth", wintypes.LONG), ("biHeight", wintypes.LONG), ("biPlanes", wintypes.WORD), ("biBitCount", wintypes.WORD), ("biCompression", wintypes.DWORD), ("biSizeImage", wintypes.DWORD), ("biXPelsPerMeter", wintypes.LONG), ("biYPelsPerMeter", wintypes.LONG), ("biClrUsed", wintypes.DWORD), ("biClrImportant", wintypes.DWORD)] header = BITMAPINFOHEADER() header.biSize = ctypes.sizeof(BITMAPINFOHEADER) header.biWidth = width header.biHeight = -height # 负数 = 自上而下 header.biPlanes = 1 header.biBitCount = 32 header.biCompression = 0 buffer_size = width * height * 4 buffer = ctypes.create_string_buffer(buffer_size) gdi32.GetDIBits(hdc_mem, bitmap, 0, height, buffer, ctypes.byref(header), 0) # 用 Pillow 存(项目里本来就有) from PIL import Image image = Image.frombuffer("RGBA", (width, height), buffer, "raw", "BGRA", 0, 1) image.convert("RGB").save(path) gdi32.DeleteObject(bitmap) gdi32.DeleteDC(hdc_mem) user32.ReleaseDC(hwnd, hdc_window) return bool(ok) def main() -> int: SHOTS.mkdir(parents=True, exist_ok=True) os.environ["YKXMZ_DATA"] = str(ROOT / "_build" / "_shots_data") Path(os.environ["YKXMZ_DATA"]).mkdir(parents=True, exist_ok=True) print(" 启动被控端(会显示窗口)") env = dict(os.environ) proc = subprocess.Popen( [str(EXE), "--code", "789113", "--port", "8899", "--name", "教室电脑"], cwd=str(ROOT), env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) time.sleep(9) hwnd, title = find_window("ykxmz") if not hwnd: print(" [失败] 没找到窗口") proc.terminate() return 1 print(" 找到窗口: %r" % title) # 拉到屏幕内可见位置,保证 PrintWindow 能拿到内容 user32.SetForegroundWindow(hwnd) user32.MoveWindow(hwnd, 60, 60, 420, 460, True) time.sleep(2) out = SHOTS / "pc-main.png" if capture(hwnd, out): print(" ✓ 拍了 %s %.1f KB" % (out.name, out.stat().st_size / 1024)) else: print(" [失败] 抓取失败") proc.terminate() try: proc.wait(timeout=8) except subprocess.TimeoutExpired: proc.kill() print() print(" 产物:") for p in sorted(SHOTS.glob("*.png")): print(" %-24s %6.1f KB" % (p.name, p.stat().st_size / 1024)) return 0 if __name__ == "__main__": sys.exit(main())