首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Winapi GetDIBits访问冲突

Winapi GetDIBits访问冲突
EN

Stack Overflow用户
提问于 2014-06-26 20:47:59
回答 2查看 916关注 0票数 0

我希望在python中获得BITMAPINFO的原始字节。这是我的完整代码:

代码语言:javascript
复制
import ctypes
from ctypes import wintypes
windll = ctypes.windll
user32 = windll.user32
gdi32 = windll.gdi32


class RECT(ctypes.Structure):
    _fields_ = [
        ('left', ctypes.c_long),
        ('top', ctypes.c_long),
        ('right', ctypes.c_long),
        ('bottom', ctypes.c_long)
    ]


class BITMAPINFOHEADER(ctypes.Structure):
    _fields_ = [
        ("biSize", wintypes.DWORD),
        ("biWidth", ctypes.c_long),
        ("biHeight", ctypes.c_long),
        ("biPlanes", wintypes.WORD),
        ("biBitCount", wintypes.WORD),
        ("biCompression", wintypes.DWORD),
        ("biSizeImage", wintypes.DWORD),
        ("biXPelsPerMeter", ctypes.c_long),
        ("biYPelsPerMeter", ctypes.c_long),
        ("biClrUsed", wintypes.DWORD),
        ("biClrImportant", wintypes.DWORD)
    ]


class RGBQUAD(ctypes.Structure):
    _fields_ = [
        ("rgbBlue", wintypes.BYTE),
        ("rgbGreen", wintypes.BYTE),
        ("rgbRed", wintypes.BYTE),
        ("rgbReserved", ctypes.c_void_p)
    ]


class BITMAP(ctypes.Structure):
    _fields_ = [
        ("bmType", ctypes.c_long),
        ("bmWidth", ctypes.c_long),
        ("bmHeight", ctypes.c_long),
        ("bmWidthBytes", ctypes.c_long),
        ("bmPlanes", wintypes.DWORD),
        ("bmBitsPixel", wintypes.DWORD),
        ("bmBits", ctypes.c_void_p)
    ]


whandle = 327756  # Just a handle of an open application
rect = RECT()
user32.GetClientRect(whandle, ctypes.byref(rect))
# bbox = (rect.left, rect.top, rect.right, rect.bottom)

hdcScreen = user32.GetDC(None)
hdc = gdi32.CreateCompatibleDC(hdcScreen)
hbmp = gdi32.CreateCompatibleBitmap(
    hdcScreen,
    rect.right - rect.left,
    rect.bottom - rect.top
)
gdi32.SelectObject(hdc, hbmp)

PW_CLIENTONLY = 1

if not user32.PrintWindow(whandle, hdc, PW_CLIENTONLY):
    raise Exception("PrintWindow failed")

bmap = BITMAP()
if not gdi32.GetObjectW(hbmp, ctypes.sizeof(BITMAP), ctypes.byref(bmap)):
    raise Exception("GetObject failed")


class BITMAPINFO(ctypes.Structure):
    _fields_ = [
        ("BITMAPINFOHEADER", BITMAPINFOHEADER),
        ("RGBQUAD", RGBQUAD * 1000)
    ]

bminfo = BITMAPINFO()
bminfo.BITMAPINFOHEADER.biSize = ctypes.sizeof(BITMAPINFOHEADER)
bminfo.BITMAPINFOHEADER.biWidth = bmap.bmWidth
bminfo.BITMAPINFOHEADER.biHeight = bmap.bmHeight
bminfo.BITMAPINFOHEADER.biPlanes = bmap.bmPlanes
bminfo.BITMAPINFOHEADER.biBitCount = bmap.bmBitsPixel
bminfo.BITMAPINFOHEADER.biCompression = 0
bminfo.BITMAPINFOHEADER.biClrImportant = 0

out = ctypes.create_string_buffer(1000)

if not gdi32.GetDIBits(hdc, hbmp, 0, bmap.bmHeight, None, bminfo, 0):
    raise Exception("GetDIBits failed")

我需要一种方法来知道RGBQUADS数组在BITMAPINFO结构中的长度以及out缓冲区的长度。1000作为占位符在那里。

访问冲突导致gdi32.GetDIBits失败。我想这是因为我必须有数组和缓冲区的正确长度。

我发了全部资料,因为我不知道是什么失败了。任何帮助都是非常感谢的。

更新

  • 修正了BITMAP中的BITMAPs是WORDs,RGBQUAD中的空指针是BYTE
  • 获取图像数据的大小: def round_up32(n):multiple = 32,multiple < n: multiple += 32返回multiple data_len = round_up32(bmap.bmWidth * bmap.bmBitsPixel) * bmap.bmHeight

仍然违反了访问权限。

我也看到了那个没有用于32位/像素位图的RGBQUAD数组。。这是真的吗?

EN

回答 2

Stack Overflow用户

发布于 2014-06-27 23:26:49

我所犯的错误:

  • 结构(谢谢大卫):
    • BITMAP没有DWORD;它有WORDs。
    • RGBQUADrgbReserved是一个BYTE,而不是一个空指针。
    • 对于每像素32位的位图,BITMAPINFO不需要RGBQUAD数组。

  • 指针(谢谢罗杰,我来自python :P ):
    • GetDIBits参数lpvBits需要一个指向缓冲区的指针
    • GetDIBits参数lpbi需要指向结构的指针。

我不知道的是:

缓冲区必须有多大。引用乔纳森

位图的每一行在大小上都是bmWidth * bmBitsPixel位,舍入到下一个32位的倍数。用bmHeight乘以行长来计算图像数据的总大小。

我想出了这个:

代码语言:javascript
复制
def round_up32(n):
    multiple = 32

    while multiple < n:
        multiple += 32

    return multiple

scanline_len = round_up32(bmap.bmWidth * bmap.bmBitsPixel)
data_len = scanline_len * bmap.bmHeight

然后使用data_lenctypes.create_string_buffer()进行无效化。

GetDIBits只返回像素数据,所以我必须构建标题。

在进行了所有这些更改后,没有什么失败的,但是图像被倒置了。我发现GetDIBits由于兼容的原因返回倒置的扫描行。我用字节做了一个新的PIL图像,然后翻转它。

全部资料来源如下:

代码语言:javascript
复制
import struct

from PIL import Image
from PIL.ImageOps import flip

import ctypes
from ctypes import wintypes
windll = ctypes.windll
user32 = windll.user32
gdi32 = windll.gdi32


class RECT(ctypes.Structure):
    _fields_ = [
        ('left', ctypes.c_long),
        ('top', ctypes.c_long),
        ('right', ctypes.c_long),
        ('bottom', ctypes.c_long)
    ]


class BITMAPINFOHEADER(ctypes.Structure):
    _fields_ = [
        ("biSize", wintypes.DWORD),
        ("biWidth", ctypes.c_long),
        ("biHeight", ctypes.c_long),
        ("biPlanes", wintypes.WORD),
        ("biBitCount", wintypes.WORD),
        ("biCompression", wintypes.DWORD),
        ("biSizeImage", wintypes.DWORD),
        ("biXPelsPerMeter", ctypes.c_long),
        ("biYPelsPerMeter", ctypes.c_long),
        ("biClrUsed", wintypes.DWORD),
        ("biClrImportant", wintypes.DWORD)
    ]


class BITMAPINFO(ctypes.Structure):
    _fields_ = [
        ("bmiHeader", BITMAPINFOHEADER)
    ]


class BITMAP(ctypes.Structure):
    _fields_ = [
        ("bmType", ctypes.c_long),
        ("bmWidth", ctypes.c_long),
        ("bmHeight", ctypes.c_long),
        ("bmWidthBytes", ctypes.c_long),
        ("bmPlanes", wintypes.WORD),
        ("bmBitsPixel", wintypes.WORD),
        ("bmBits", ctypes.c_void_p)
    ]


def get_window_image(whandle):
    def round_up32(n):
        multiple = 32

        while multiple < n:
            multiple += 32

        return multiple

    rect = RECT()
    user32.GetClientRect(whandle, ctypes.byref(rect))
    bbox = (rect.left, rect.top, rect.right, rect.bottom)

    hdcScreen = user32.GetDC(None)
    hdc = gdi32.CreateCompatibleDC(hdcScreen)
    hbmp = gdi32.CreateCompatibleBitmap(
        hdcScreen,
        bbox[2] - bbox[0],
        bbox[3] - bbox[1]
    )
    gdi32.SelectObject(hdc, hbmp)

    PW_CLIENTONLY = 1

    if not user32.PrintWindow(whandle, hdc, PW_CLIENTONLY):
        raise Exception("PrintWindow failed")

    bmap = BITMAP()
    if not gdi32.GetObjectW(hbmp, ctypes.sizeof(BITMAP), ctypes.byref(bmap)):
        raise Exception("GetObject failed")

    if bmap.bmBitsPixel != 32:
        raise Exception("WTF")

    scanline_len = round_up32(bmap.bmWidth * bmap.bmBitsPixel)
    data_len = scanline_len * bmap.bmHeight

    # http://msdn.microsoft.com/en-us/library/ms969901.aspx
    bminfo = BITMAPINFO()
    bminfo.bmiHeader.biSize = ctypes.sizeof(BITMAPINFOHEADER)
    bminfo.bmiHeader.biWidth = bmap.bmWidth
    bminfo.bmiHeader.biHeight = bmap.bmHeight
    bminfo.bmiHeader.biPlanes = 1
    bminfo.bmiHeader.biBitCount = 24  # bmap.bmBitsPixel
    bminfo.bmiHeader.biCompression = 0

    data = ctypes.create_string_buffer(data_len)

    DIB_RGB_COLORS = 0

    get_bits_success = gdi32.GetDIBits(
        hdc, hbmp,
        0, bmap.bmHeight,
        ctypes.byref(data), ctypes.byref(bminfo),
        DIB_RGB_COLORS
    )
    if not get_bits_success:
        raise Exception("GetDIBits failed")

    # http://msdn.microsoft.com/en-us/library/dd183376%28v=vs.85%29.aspx
    bmiheader_fmt = "LllHHLLllLL"

    unpacked_header = [
        bminfo.bmiHeader.biSize,
        bminfo.bmiHeader.biWidth,
        bminfo.bmiHeader.biHeight,
        bminfo.bmiHeader.biPlanes,
        bminfo.bmiHeader.biBitCount,
        bminfo.bmiHeader.biCompression,
        bminfo.bmiHeader.biSizeImage,
        bminfo.bmiHeader.biXPelsPerMeter,
        bminfo.bmiHeader.biYPelsPerMeter,
        bminfo.bmiHeader.biClrUsed,
        bminfo.bmiHeader.biClrImportant
    ]

    # Indexes: biXPelsPerMeter = 7, biYPelsPerMeter = 8
    # Value from https://stackoverflow.com/a/23982267/2065904
    unpacked_header[7] = 3779
    unpacked_header[8] = 3779

    image_header = struct.pack(bmiheader_fmt, *unpacked_header)

    image = image_header + data

    return flip(Image.frombytes("RGB", (bmap.bmWidth, bmap.bmHeight), image))

将窗口句柄(int)传递给get_window_image(),它将返回一个PIL映像。

唯一的问题是颜色是..。奇怪吗?,我改天再想办法。

票数 2
EN

Stack Overflow用户

发布于 2022-08-23 02:38:26

以下代码是错误的

代码语言:javascript
复制
def round_up32(n):
    multiple = 32

    while multiple < n:
        multiple += 32

    return multiple

scanline_len = round_up32(bmap.bmWidth * bmap.bmBitsPixel)
data_len = scanline_len * bmap.bmHeight

您正在试图找到创建数组的字节数,或者只是分配一个内存块。内存总是字节。因此,bmap.bmBitsPixel应该转换为字节。32位是4字节。因为您已经在检查bmap.bmBitsPixel的32位,所以用4替换bmap.bmBitsPixel并删除round_up32函数。

代码语言:javascript
复制
scanline_len = bmap.bmWidth * 4
data_len = scanline_len * bmap.bmHeight
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24439949

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档