import time
from ctypes import wintypes
import ctypes
import win32gui
import win32con
import win32ui
import cv2
import numpy as np
class WindowManager:
@staticmethod
def Capture_Win32_Gpu(handle: int, model: int = 3):
"""
通过win32方式截图,并且无视硬件加速
https://stackoverflow.com/questions/19695214/screenshot-of-inactive-window-printwindow-win32gui
"""
ctypes.windll.user32.SetProcessDPIAware() # 抑制缩放
rect = win32gui.GetWindowRect(handle)
width, height = rect[2] - rect[0], rect[3] - rect[1]
hwnd_dc = win32gui.GetWindowDC(handle)
mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)
save_dc = mfc_dc.CreateCompatibleDC()
save_bit_map = win32ui.CreateBitmap()
save_bit_map.CreateCompatibleBitmap(mfc_dc, width, height)
save_dc.SelectObject(save_bit_map)
ctypes.windll.user32.PrintWindow(handle, save_dc.GetSafeHdc(), model) # 如果仍然是黑屏,尝试修改数字 ‘3’ (从0开始...)
bmpinfo = save_bit_map.GetInfo()
bmpstr = save_bit_map.GetBitmapBits(True)
capture = np.frombuffer(bmpstr, dtype=np.uint8).reshape((bmpinfo["bmHeight"], bmpinfo["bmWidth"], 4))
capture = np.ascontiguousarray(capture)[..., :-1]
win32gui.DeleteObject(save_bit_map.GetHandle())
save_dc.DeleteDC()
mfc_dc.DeleteDC()
win32gui.ReleaseDC(handle, hwnd_dc)
capture = cv2.cvtColor(capture, cv2.COLOR_RGBA2RGB)
return capture
@staticmethod
def Get_Handle():
"""
取窗口句柄
:return: int,窗口句柄
"""
return win32gui.GetForegroundWindow()
@staticmethod
def Get_Window_Position_And_Size(handle):
"""
窗口取位置和大小
:param handle: int,窗口句柄
:return:
"""
return win32gui.GetWindowRect(handle)
class WindowRect(ctypes.Structure):
_fields_ = [
("left", ctypes.c_long),
("top", ctypes.c_long),
("right", ctypes.c_long),
("bottom", ctypes.c_long)
]
def __init__(self):
"""
调用Windows官方API实现对window的操作
"""
self.user32 = ctypes.windll.user32
self.kernel32 = ctypes.windll.kernel32
# self.GetLastError = ctypes.windll.kernel32.GetLastError()
self.gdi32 = ctypes.WinDLL("Gdi32.dll")
def Find_Windows(self, parent_handle: int = None, class_name: str = None, window_title: str = None):
"""
模糊遍历窗口
:param parent_handle: str,父窗口句柄
:param class_name: str,欲寻找窗口类名
:param window_title: str,欲寻找窗口标题
:return: list,窗口句柄列表
"""
handle_list = []
if parent_handle is None:
parent_handle = self.user32.GetDesktopWindow()
handle = self.user32.GetWindow(parent_handle, 5)
while handle != 0:
valid = True
if class_name is not None:
actual_class_name = str(self.Get_Class_Name(handle)).lower()
if actual_class_name.find(class_name.lower()) == -1:
valid = False
time.sleep(0)
if window_title is not None:
actual_window_title = str(self.Get_Window_Title(handle)).lower()
if actual_window_title.find(window_title.lower()) == -1:
valid = False
time.sleep(0)
if valid:
handle_list.append(handle)
handle = self.user32.GetWindow(handle, 2)
return handle_list
def Get_Class_Name(self, handle: int):
"""
取窗口类名
:param handle: int,窗口句柄
:return: str,窗口类名
"""
GetClassNameA = self.user32.GetClassNameA
GetClassNameA.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int]
GetClassNameA.restype = ctypes.c_int
buffer_size = 256
name_buffer = ctypes.create_string_buffer(buffer_size)
result = GetClassNameA(handle, name_buffer, buffer_size)
if result != 0:
class_name = name_buffer.value.decode('utf-8')
return class_name
else:
return ""
def Get_Window_Title(self, handle: int):
"""
取窗口标题
:param handle: int,窗口句柄
:return: str,窗口标题
"""
GetWindowTextLengthW = self.user32.GetWindowTextLengthW
GetWindowTextLengthW.argtypes = [ctypes.c_int]
GetWindowTextLengthW.restype = ctypes.c_int
GetWindowTextW = self.user32.GetWindowTextW
GetWindowTextW.argtypes = [ctypes.c_int, ctypes.c_wchar_p, ctypes.c_int]
GetWindowTextW.restype = ctypes.c_int
length = GetWindowTextLengthW(handle)
if length > 0:
buffer_size = length + 1
title_buffer = ctypes.create_unicode_buffer(buffer_size)
result = GetWindowTextW(handle, title_buffer, buffer_size)
if result != 0:
title = title_buffer.value
return title
else:
return ""
else:
return ""
def Is_Window_Visible(self, handle: int):
"""
窗口是否可见
:param handle: int,窗口句柄
:return: bool,如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。
"""
return self.user32.IsWindowVisible(handle)
def Set_Window_Position_And_Size(self, handle, left=None, top=None, width=None, height=None):
"""
设置窗口位置和大小
:param handle: int,窗口句柄
:param left: int,窗口左侧的新位置
:param top: int,窗口顶部的新位置
:param width: int,窗口的新宽度
:param height: int,窗口的新高度
:return: bool,如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零
"""
rect = self.WindowRect()
self.user32.GetWindowRect(handle, ctypes.byref(rect))
if left is None:
left = rect.left
if top is None:
top = rect.top
if width is None:
width = rect.right - rect.left
if height is None:
height = rect.bottom - rect.top
return self.user32.MoveWindow(handle, left, top, width, height, True)
def Set_Window_Topmost(self, handle, activate_window=True):
"""
窗口置顶
:param handle: int,窗口句柄
:param activate_window: bool,是否激活窗口
:return: bool,如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。
"""
HWND_TOPMOST = -1
SWP_SHOWWINDOW = 0x40
SWP_NOSIZE = 0x1
SWP_NOMOVE = 0x2
SWP_NOACTIVATE = 0x10
flags = SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE
if not activate_window:
flags |= SWP_NOACTIVATE
result = self.user32.SetWindowPos(handle, HWND_TOPMOST, 0, 0, 0, 0, flags)
return result != 0
def Set_Window_Display_Affinity(self, handle, affinity):
"""
窗口反截图,仅支持自身调用
:param handle: int,顶级窗口的句柄。 窗口必须属于当前进程。
:param affinity: int,0:取消设置;1:黑框;2:透明
:return: bool,如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。
"""
HWND = ctypes.c_void_p
DWORD = ctypes.c_ulong
BOOL = ctypes.c_int
self.user32.SetWindowDisplayAffinity.argtypes = (HWND, DWORD)
self.user32.SetWindowDisplayAffinity.restype = BOOL
AFFINITY_CONSTANTS = {
0: 0x00000000, # WDA_NONE
1: 0x00000001, # WDA_MONITOR
2: 0x00000011 # WDA_EXCLUDEFROMCAPTURE
}
affinity_value = AFFINITY_CONSTANTS.get(affinity, 0)
result = self.user32.SetWindowDisplayAffinity(handle, affinity_value)
print(self.kernel32.GetLastError())
return result
def Enum_Child_Window_Handles(self, hwnd):
"""
枚举给定主窗口句柄的子窗口句柄,并返回一个包含子窗口句柄的列表。
:param hwnd: int,主窗口句柄
:return: list,子窗口句柄列表
"""
# 定义Windows API函数的参数类型
EnumChildProc = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
EnumChildWindows = self.user32.EnumChildWindows
# 定义回调函数,用于处理枚举到的子窗口
def enum_child_proc(hwnd, lparam):
# 将子窗口句柄添加到列表中
ctypes.cast(lparam, ctypes.POINTER(ctypes.py_object)).contents.value.append(hwnd)
return True
# 创建一个空的子窗口句柄列表
child_handles = []
# 枚举子窗口句柄
EnumChildWindows(hwnd, EnumChildProc(enum_child_proc), ctypes.byref(ctypes.py_object(child_handles)))
# 返回子窗口句柄列表
return child_handles
def Window_Draw_Border_Rectangle(self, handle: int, BorderThickness: int = 2, BorderColor: int = 0xFF0000,
rect_left: int = 0, rect_top: int = 0, rect_right: int = 100,
rect_bottom: int = 100, isTransparent: bool = False):
"""
窗口绘制矩形
:param handle: int,窗口句柄
:param BorderThickness: int,矩形边框粗细
:param BorderColor: int,矩形边框颜色
:param rect_left: int,左边
:param rect_top: int,顶边
:param rect_right: int,右边
:param rect_bottom: int,底边
:param isTransparent: bool,是否透明
:return:
"""
# 获取窗口DC
hdc = win32gui.GetDC(handle)
# 创建画笔
pen = win32gui.CreatePen(win32con.PS_SOLID, BorderThickness, BorderColor)
# 创建画刷
if isTransparent:
brush = win32gui.GetStockObject(win32con.HOLLOW_BRUSH) # 空画刷,不填充
# 将背景模式设置为透明
win32gui.SetBkMode(hdc, win32con.TRANSPARENT)
else:
brush = win32gui.CreateSolidBrush(0x00000000) # 0x00000000 表示透明
try:
# 使用上下文管理器管理画笔和画刷
with self._SelectObjects(hdc, pen, brush):
# 绘制矩形
result = self.gdi32.Rectangle(hdc, rect_left, rect_top, rect_right, rect_bottom)
finally:
# 释放窗口DC
win32gui.ReleaseDC(handle, hdc)
return result
# 自定义上下文管理器来管理GDI对象
class _SelectObjects:
def __init__(self, hdc, *objects):
self.hdc = hdc
self.objects = objects
def __enter__(self):
for obj in self.objects:
win32gui.SelectObject(self.hdc, obj)
return self
def __exit__(self, exc_type, exc_value, traceback):
for obj in self.objects:
win32gui.DeleteObject(obj)
if __name__ == "__main__":
# 示例用法:
window_manager = WindowManager()
# 查找所有可见窗口
# visible_windows = window_manager.Find_Windows()
# for handle in visible_windows:
# class_name = window_manager.Get_Class_Name(handle)
# window_title = window_manager.Get_Window_Title(handle)
# print(f"Handle: {handle}, Class Name: {class_name}, Window Title: {window_title}")
# 设置窗口置顶
# window_handle = 6556218 # 替换为实际窗口句柄
# window_manager.Set_Window_Topmost(window_handle)
# 设置窗口位置和大小
# window_manager.Set_Window_Position_And_Size(window_handle, left=100, top=100, width=800, height=600)
# 示例用法
# handle = 1509402 # 获取当前窗口句柄
# RectangleThickness = 2 # 矩形边框线宽
# RectangleColor = 0xFF0000 # 矩形颜色 (红色)
# 指定矩形的坐标和大小
# rect_left = 100
# rect_top = 100
# rect_right = 200
# rect_bottom = 200
# 绘制矩形
# result = window_manager.Window_Draw_Border_Rectangle(handle, RectangleThickness, RectangleColor, rect_left,
# rect_top,
# rect_right,
# rect_bottom, True)
版权属于:CuteKitty
本文链接:https://blog.cutekitty.cn/index.php/archives/16/
作品采用
知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可