You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用纯ctypes实现Python截图的位图磁盘保存功能?

用纯ctypes实现Windows位图保存功能

你之前用pywin32的SaveBitmapFile实现截图保存,但这个并非原生WinAPI,纯ctypes环境下需要手动构造BMP文件结构并写入磁盘。下面是完整的实现方案:

完整代码实现

import ctypes
from ctypes import wintypes

# 绑定Windows系统库
user32 = ctypes.windll.user32
gdi32 = ctypes.windll.gdi32
kernel32 = ctypes.windll.kernel32

# 定义WinAPI常用类型
HANDLE = wintypes.HANDLE
HWND = wintypes.HWND
HDC = wintypes.HDC
HBITMAP = wintypes.HBITMAP
DWORD = wintypes.DWORD
LONG = wintypes.LONG
WORD = wintypes.WORD
BYTE = wintypes.BYTE
LPVOID = wintypes.LPVOID

# 定义BMP文件头结构体
class BITMAPFILEHEADER(ctypes.Structure):
    _pack_ = 2  # 按2字节对齐,符合BMP文件头要求
    _fields_ = [
        ("bfType", WORD),
        ("bfSize", DWORD),
        ("bfReserved1", WORD),
        ("bfReserved2", WORD),
        ("bfOffBits", DWORD)
    ]

# 定义BMP信息头结构体
class BITMAPINFOHEADER(ctypes.Structure):
    _fields_ = [
        ("biSize", DWORD),
        ("biWidth", LONG),
        ("biHeight", LONG),
        ("biPlanes", WORD),
        ("biBitCount", WORD),
        ("biCompression", DWORD),
        ("biSizeImage", DWORD),
        ("biXPelsPerMeter", LONG),
        ("biYPelsPerMeter", LONG),
        ("biClrUsed", DWORD),
        ("biClrImportant", DWORD)
    ]

# 定义BMP信息结构体
class BITMAPINFO(ctypes.Structure):
    _fields_ = [
        ("bmiHeader", BITMAPINFOHEADER),
        ("bmiColors", DWORD * 3)  # 24位真彩色无需调色板,仅占位
    ]

def saveBitmapToDisk(width, height, hdc, hbitmap, file_path):
    # 初始化BMP信息头
    bmi = BITMAPINFO()
    bmi.bmiHeader.biSize = ctypes.sizeof(BITMAPINFOHEADER)
    bmi.bmiHeader.biWidth = width
    bmi.bmiHeader.biHeight = -height  # 负值表示图像从上到下存储(BMP标准)
    bmi.bmiHeader.biPlanes = 1
    bmi.bmiHeader.biBitCount = 24  # 24位真彩色
    bmi.bmiHeader.biCompression = 0  # BI_RGB无压缩格式
    # 计算对齐后的图像数据大小(每行像素需为4字节倍数)
    bmi.bmiHeader.biSizeImage = ((width * 3 + 3) // 4) * 4 * height

    # 创建像素数据缓冲区
    buffer_size = bmi.bmiHeader.biSizeImage
    buffer = ctypes.create_string_buffer(buffer_size)

    # 从HBITMAP中提取像素数据
    gdi32.GetDIBits(hdc, hbitmap, 0, height, buffer, ctypes.byref(bmi), 0)

    # 构造BMP文件头
    bmfh = BITMAPFILEHEADER()
    bmfh.bfType = 0x4D42  # "BM"标识,BMP文件特征码
    bmfh.bfSize = ctypes.sizeof(BITMAPFILEHEADER) + ctypes.sizeof(BITMAPINFOHEADER) + buffer_size
    bmfh.bfOffBits = ctypes.sizeof(BITMAPFILEHEADER) + ctypes.sizeof(BITMAPINFOHEADER)

    # 写入文件
    with open(file_path, "wb") as f:
        f.write(bmfh)
        f.write(bmi.bmiHeader)
        f.write(buffer.raw)

def screenshot():
    # 自动获取屏幕分辨率,替代硬编码
    w = user32.GetSystemMetrics(0)
    h = user32.GetSystemMetrics(1)
    image_path = "out.bmp"

    hwnd = user32.GetDesktopWindow()
    wDC = user32.GetWindowDC(hwnd)

    # 修正:CreateCompatibleDC需传入DC句柄而非窗口句柄
    cDC = gdi32.CreateCompatibleDC(wDC)

    dataBitMap = gdi32.CreateCompatibleBitmap(wDC, w, h)
    old_bitmap = gdi32.SelectObject(cDC, dataBitMap)
    # 修正:BitBlt源DC应为wDC,不是窗口句柄
    gdi32.BitBlt(cDC, 0, 0, w, h, wDC, 0, 0, 0x00CC0020)  # SRCCOPY模式复制图像

    saveBitmapToDisk(w, h, cDC, dataBitMap, image_path)

    # 恢复DC中原位图,避免资源泄漏
    gdi32.SelectObject(cDC, old_bitmap)
    gdi32.DeleteDC(cDC)
    user32.ReleaseDC(hwnd, wDC)
    gdi32.DeleteObject(dataBitMap)

    return image_path

if __name__ == "__main__":
    screenshot()

关键说明

  • 结构体定义:严格遵循Windows API的BMP文件结构规范,注意字节对齐要求。
  • GetDIBits:原生WinAPI函数,负责从HBITMAP句柄中提取原始像素数据,这里使用无压缩的24位真彩色格式。
  • 图像对齐处理:BMP文件要求每行像素数据长度为4的倍数,计算biSizeImage时需处理该对齐逻辑。
  • 原代码修复:
    • 修正CreateCompatibleDC参数错误,需传入已有DC句柄而非窗口句柄。
    • 修正BitBlt源DC参数错误,需使用窗口DC而非窗口句柄。
    • 添加SelectObject恢复DC中原位图,避免GDI资源泄漏。

内容的提问来源于stack exchange,提问作者agrivts

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 16:35:35