创建图像文件 & header (python + ctypes)
Creating an image file & header (python + ctypes)
经过大量的来回编辑,这就是我的位置(请记住,我是菜鸟):
现在我只是想检测一个 window 并截取那个 window 的屏幕截图(稍后我将解析屏幕截图以获取信息)。我一直在谷歌上广泛搜索 ctypes、DLL 函数,并在 python 中截取屏幕截图...我发现了一些我重复使用的代码(在搜索每个函数时,它的参数和语法以确保我理解所有内容) 但现在我被难住了。
我的功能有效,我的 screenshot.bmp 文件实际上包含数据,但在 Windows 中打开它时出现错误...(不是有效的位图文件):
我认为这与 bmp_header 或 c_bits 行有关,但我找不到很多...我查看了 header 格式,它对我来说似乎是正确的 (windows BITMAPINFOHEADER)。我不确定的是我们如何计算缓冲区的大小,也许我在那里做错了?
非常感谢有人向我解释这一点或提供 link 一篇可以帮助我的文章...
非常感谢!
#!/usr/bin/env python3
import ctypes
from struct import calcsize, pack
from ctypes.wintypes import RECT, DWORD
EnumWindows = ctypes.windll.user32.EnumWindows
EnumWindowsPointer = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
GetWindowText = ctypes.windll.user32.GetWindowTextW
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
IsWindowVisible = ctypes.windll.user32.IsWindowVisible
GetWindowRect = ctypes.windll.user32.GetWindowRect
GetWindowDC = ctypes.windll.user32.GetWindowDC
CreateCompatibleDC = ctypes.windll.gdi32.CreateCompatibleDC
CreateCompatibleBitmap = ctypes.windll.gdi32.CreateCompatibleBitmap
SelectObject = ctypes.windll.gdi32.SelectObject
BitBlt = ctypes.windll.gdi32.BitBlt
GetDIBits = ctypes.windll.gdi32.GetDIBits
CreateFile = ctypes.windll.kernel32.CreateFileW
WriteFile = ctypes.windll.kernel32.WriteFile
#Here we find HWND and LPARAM passed by EnumWindows to the callback
def CaptureWindow(hwnd, lParam):
#Restrict to visible windows
if IsWindowVisible(hwnd):
#Get title of the specific window
length = GetWindowTextLength(hwnd)
buff = ctypes.create_unicode_buffer(length + 1)
GetWindowText(hwnd, buff, length + 1)
#Verify match with our window
if "(Mode Virtuel)" in buff.value:
r = ctypes.wintypes.RECT()
GetWindowRect(hwnd,ctypes.byref(r))
rWidth = r.right-r.left
rHeight = r.bottom-r.top
srcdc = GetWindowDC(hwnd)
memdc = CreateCompatibleDC(srcdc)
bmp = CreateCompatibleBitmap(srcdc, rWidth, rHeight)
SelectObject(memdc, bmp)
BitBlt(memdc, 0, 0, rWidth, rHeight, srcdc, r.left, r.top, "SRCCOPY")
#Create a BMP header (Windows BitmapInfoHeader format)
bmp_header = pack('LLLHHLLLLLL', calcsize('LLLHHLLLLLL'), rWidth, rHeight, 1, 24, 0, 0, 0, 0, 0, 0)
c_bmp_header = ctypes.c_buffer(bmp_header)
#Create buffer for BMP data (H * W * 3 = RGB for each pixel) + (40 bytes for the header)
c_bits = ctypes.c_buffer(b' ' * ((rHeight * rWidth * 3) + 40))
GetDIBits(memdc, bmp, 0, rHeight, ctypes.byref(c_bits), ctypes.byref(c_bmp_header), "DIB_RGB_COLORS")
#Create the file
screen = CreateFile("screenshot.bmp", 3, 4, 0, 2, 128, 0)
#Write the contents of my BMP buffer in the file
c_written = DWORD()
success = WriteFile(screen, c_bits, len(c_bits), ctypes.byref(c_written), 0)
#Info
print("Screenshot saved for : {0}".format(buff.value))
print("{0} bytes were written...".format(c_written))
return True
EnumWindows(EnumWindowsPointer(CaptureWindow), 0)
好的,我找到了解决方案,但我很失望,因为它实际上并没有解决我之前代码中的问题。
为了回复评论,我喜欢了解事物的工作原理 "under the hood",并且我喜欢使用 ctypes,因为它教会了我一些 C 并允许我使用系统库(这是一个非常有趣的知识我相信在任何编码工作中都取得了进展)。此外,我总是更喜欢使用本机 python 而不是导入别人的代码(我不完全理解)。我不觉得下面的解决方案是完整的 "my own".....
无论如何,这是我的新代码,它可以完美运行并每 0.5 秒检查我的 window 以检查是否需要执行操作(通过解析特定像素的颜色变化):
#!/usr/bin/env python3
import time
import ctypes
import pyautogui
from ctypes.wintypes import RECT
EnumWindows = ctypes.windll.user32.EnumWindows
EnumWindowsPointer = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
GetWindowText = ctypes.windll.user32.GetWindowTextW
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
IsWindowVisible = ctypes.windll.user32.IsWindowVisible
GetWindowRect = ctypes.windll.user32.GetWindowRect
SetForegroundWindow = ctypes.windll.user32.SetForegroundWindow
screenshot = pyautogui.screenshot
#Here we find HWND and LPARAM passed by EnumWindows to the callback
def CaptureWindow(hwnd, lParam):
#Restrict to visible windows
if IsWindowVisible(hwnd):
#Get title of the specific window
length = GetWindowTextLength(hwnd)
buff = ctypes.create_unicode_buffer(length + 1)
GetWindowText(hwnd, buff, length + 1)
#Verify match with our window
if "(Mode Virtuel)" in buff.value:
#Get dimensions
r = ctypes.wintypes.RECT()
GetWindowRect(hwnd,ctypes.byref(r))
rWidth = r.right-r.left
rHeight = r.bottom-r.top
SetForegroundWindow(hwnd)
screen = screenshot(region=(r.left, r.top, rWidth, rHeight))
if screen.getpixel((444,412)) == (255, 145, 45):
print("Action needed !!!")
else:
print("No action needed !!!")
return True
while True:
EnumWindows(EnumWindowsPointer(CaptureWindow), 0)
time.sleep(.5)
经过大量的来回编辑,这就是我的位置(请记住,我是菜鸟):
现在我只是想检测一个 window 并截取那个 window 的屏幕截图(稍后我将解析屏幕截图以获取信息)。我一直在谷歌上广泛搜索 ctypes、DLL 函数,并在 python 中截取屏幕截图...我发现了一些我重复使用的代码(在搜索每个函数时,它的参数和语法以确保我理解所有内容) 但现在我被难住了。
我的功能有效,我的 screenshot.bmp 文件实际上包含数据,但在 Windows 中打开它时出现错误...(不是有效的位图文件):
我认为这与 bmp_header 或 c_bits 行有关,但我找不到很多...我查看了 header 格式,它对我来说似乎是正确的 (windows BITMAPINFOHEADER)。我不确定的是我们如何计算缓冲区的大小,也许我在那里做错了?
非常感谢有人向我解释这一点或提供 link 一篇可以帮助我的文章...
非常感谢!
#!/usr/bin/env python3
import ctypes
from struct import calcsize, pack
from ctypes.wintypes import RECT, DWORD
EnumWindows = ctypes.windll.user32.EnumWindows
EnumWindowsPointer = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
GetWindowText = ctypes.windll.user32.GetWindowTextW
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
IsWindowVisible = ctypes.windll.user32.IsWindowVisible
GetWindowRect = ctypes.windll.user32.GetWindowRect
GetWindowDC = ctypes.windll.user32.GetWindowDC
CreateCompatibleDC = ctypes.windll.gdi32.CreateCompatibleDC
CreateCompatibleBitmap = ctypes.windll.gdi32.CreateCompatibleBitmap
SelectObject = ctypes.windll.gdi32.SelectObject
BitBlt = ctypes.windll.gdi32.BitBlt
GetDIBits = ctypes.windll.gdi32.GetDIBits
CreateFile = ctypes.windll.kernel32.CreateFileW
WriteFile = ctypes.windll.kernel32.WriteFile
#Here we find HWND and LPARAM passed by EnumWindows to the callback
def CaptureWindow(hwnd, lParam):
#Restrict to visible windows
if IsWindowVisible(hwnd):
#Get title of the specific window
length = GetWindowTextLength(hwnd)
buff = ctypes.create_unicode_buffer(length + 1)
GetWindowText(hwnd, buff, length + 1)
#Verify match with our window
if "(Mode Virtuel)" in buff.value:
r = ctypes.wintypes.RECT()
GetWindowRect(hwnd,ctypes.byref(r))
rWidth = r.right-r.left
rHeight = r.bottom-r.top
srcdc = GetWindowDC(hwnd)
memdc = CreateCompatibleDC(srcdc)
bmp = CreateCompatibleBitmap(srcdc, rWidth, rHeight)
SelectObject(memdc, bmp)
BitBlt(memdc, 0, 0, rWidth, rHeight, srcdc, r.left, r.top, "SRCCOPY")
#Create a BMP header (Windows BitmapInfoHeader format)
bmp_header = pack('LLLHHLLLLLL', calcsize('LLLHHLLLLLL'), rWidth, rHeight, 1, 24, 0, 0, 0, 0, 0, 0)
c_bmp_header = ctypes.c_buffer(bmp_header)
#Create buffer for BMP data (H * W * 3 = RGB for each pixel) + (40 bytes for the header)
c_bits = ctypes.c_buffer(b' ' * ((rHeight * rWidth * 3) + 40))
GetDIBits(memdc, bmp, 0, rHeight, ctypes.byref(c_bits), ctypes.byref(c_bmp_header), "DIB_RGB_COLORS")
#Create the file
screen = CreateFile("screenshot.bmp", 3, 4, 0, 2, 128, 0)
#Write the contents of my BMP buffer in the file
c_written = DWORD()
success = WriteFile(screen, c_bits, len(c_bits), ctypes.byref(c_written), 0)
#Info
print("Screenshot saved for : {0}".format(buff.value))
print("{0} bytes were written...".format(c_written))
return True
EnumWindows(EnumWindowsPointer(CaptureWindow), 0)
好的,我找到了解决方案,但我很失望,因为它实际上并没有解决我之前代码中的问题。
为了回复评论,我喜欢了解事物的工作原理 "under the hood",并且我喜欢使用 ctypes,因为它教会了我一些 C 并允许我使用系统库(这是一个非常有趣的知识我相信在任何编码工作中都取得了进展)。此外,我总是更喜欢使用本机 python 而不是导入别人的代码(我不完全理解)。我不觉得下面的解决方案是完整的 "my own".....
无论如何,这是我的新代码,它可以完美运行并每 0.5 秒检查我的 window 以检查是否需要执行操作(通过解析特定像素的颜色变化):
#!/usr/bin/env python3
import time
import ctypes
import pyautogui
from ctypes.wintypes import RECT
EnumWindows = ctypes.windll.user32.EnumWindows
EnumWindowsPointer = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
GetWindowText = ctypes.windll.user32.GetWindowTextW
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
IsWindowVisible = ctypes.windll.user32.IsWindowVisible
GetWindowRect = ctypes.windll.user32.GetWindowRect
SetForegroundWindow = ctypes.windll.user32.SetForegroundWindow
screenshot = pyautogui.screenshot
#Here we find HWND and LPARAM passed by EnumWindows to the callback
def CaptureWindow(hwnd, lParam):
#Restrict to visible windows
if IsWindowVisible(hwnd):
#Get title of the specific window
length = GetWindowTextLength(hwnd)
buff = ctypes.create_unicode_buffer(length + 1)
GetWindowText(hwnd, buff, length + 1)
#Verify match with our window
if "(Mode Virtuel)" in buff.value:
#Get dimensions
r = ctypes.wintypes.RECT()
GetWindowRect(hwnd,ctypes.byref(r))
rWidth = r.right-r.left
rHeight = r.bottom-r.top
SetForegroundWindow(hwnd)
screen = screenshot(region=(r.left, r.top, rWidth, rHeight))
if screen.getpixel((444,412)) == (255, 145, 45):
print("Action needed !!!")
else:
print("No action needed !!!")
return True
while True:
EnumWindows(EnumWindowsPointer(CaptureWindow), 0)
time.sleep(.5)