使用 ctypes 在 Windows 中捕获屏幕

发布于 2024-10-10 10:25:39 字数 1795 浏览 0 评论 0原文

我一直在寻找一些替代解决方案来将屏幕捕获为 Windows 计算机上的位图。

现在,我知道 PIL 有一个 ImageGrab 库,我可能最终会使用它。 然而,在我的搜索过程中,我遇到了一个使用 ctypes 访问 gdi32.dll 函数并使用这些函数捕获屏幕的解决方案。 我遇到了一个特殊的解决方案,但它不起作用,我一直在试图找出问题所在。

这是源代码:

from ctypes import *

class Bitmap(Structure):
    _fields_ = [("bitmapType", c_long),
                ("width", c_long),
                ("height", c_long),
                ("widthBytes", c_long),
                ("planes", c_short),
                ("bitsPerPixel", c_short),
                ("data", POINTER(c_ulong))]

if __name__ == "__main__":
    user32 = WinDLL("user32.dll")
    gdi32 = WinDLL("gdi32.dll")

    #Constants
    SM_CXSCREEN = 0
    SM_CYSCREEN = 1
    SRCCOPY = 0xCC0020

    #Capture the Bitmap
    width = user32.GetSystemMetrics(SM_CXSCREEN)
    height = user32.GetSystemMetrics(SM_CYSCREEN)
    screenDC = user32.GetWindowDC(user32.GetDesktopWindow())
    captureDC = gdi32.CreateCompatibleDC(screenDC)
    captureBitmap = gdi32.CreateCompatibleBitmap(screenDC, width, height)
    gdi32.SelectObject(captureDC, captureBitmap)
    gdi32.BitBlt(captureDC, 0, 0, width, height, screenDC, 0, 0, SRCCOPY)

    picture = Bitmap()
    gdi32.GetObjectA(captureBitmap, 24, byref(picture))

现在,对我来说,似乎在程序结束时,作者尝试将位图复制到 Bitmap 对象,但是: 1.我找不到gdi32.GetObjectA函数的文档 2. 当尝试查看 picutre.data 中的数据时(我希望访问 picture.data.contents 时没有出错),出现值错误:“NULL 指针访问”。

现在,在我找到此代码片段的页面中,有问题的答案,但它有点模糊并且信息不多。答案是:

You must allocate the integer array for the data pointer of the BITMAP structure.
GetObject doesn't it for you.

gdi32.CreateCompatibleBitmap 函数不应该已经分配该内存吗?GetObject 不应该只是将指针复制到位图结构的 Python 表示形式吗?

我在这里真的很困惑,如果能对这个问题有所了解,我将不胜感激。 (我已经打开了大约 15 个带有 MSDN gdi32 参考的选项卡和另外 15 个带有 ctypes 参考的选项卡,但由于我对其中任何一个都没有牢固的掌握,所以我觉得我在兜圈子)

I've been searching for some alternative solutions to capturing the screen to a bitmap on a windows machine.

Now, I am aware that PIL has an ImageGrab library and I will probably end up using it.
However, during my searches I came across a solution that uses ctypes to access gdi32.dll functions and use those to capture the screen.
There was one particular solution I came across which doesn't work, and I've been trying to figure out what's wrong it.

Here's the source code:

from ctypes import *

class Bitmap(Structure):
    _fields_ = [("bitmapType", c_long),
                ("width", c_long),
                ("height", c_long),
                ("widthBytes", c_long),
                ("planes", c_short),
                ("bitsPerPixel", c_short),
                ("data", POINTER(c_ulong))]

if __name__ == "__main__":
    user32 = WinDLL("user32.dll")
    gdi32 = WinDLL("gdi32.dll")

    #Constants
    SM_CXSCREEN = 0
    SM_CYSCREEN = 1
    SRCCOPY = 0xCC0020

    #Capture the Bitmap
    width = user32.GetSystemMetrics(SM_CXSCREEN)
    height = user32.GetSystemMetrics(SM_CYSCREEN)
    screenDC = user32.GetWindowDC(user32.GetDesktopWindow())
    captureDC = gdi32.CreateCompatibleDC(screenDC)
    captureBitmap = gdi32.CreateCompatibleBitmap(screenDC, width, height)
    gdi32.SelectObject(captureDC, captureBitmap)
    gdi32.BitBlt(captureDC, 0, 0, width, height, screenDC, 0, 0, SRCCOPY)

    picture = Bitmap()
    gdi32.GetObjectA(captureBitmap, 24, byref(picture))

Now, to me it seems that in the end of the program, the author tries to copy the bitmap to the Bitmap object, but:
1. I have failed to find documentation of the gdi32.GetObjectA function
2. When trying to view the data inside picutre.data (I hope I was not wrong by accessing picture.data.contents), I get a Value error: "NULL pointer access".

Now, in the page where I found this code snippet, there was an answer to the problem, but it was a bit vague and not very informative. The answer read:

You must allocate the integer array for the data pointer of the BITMAP structure.
GetObject doesn't it for you.

Shouldn't the gdi32.CreateCompatibleBitmap function already allocate that memory, and doesn't the GetObject just copy the pointer to the Python representation of the bitmap structure?

I'm really confused here, any light shed on this problem would be really appreciated.
(I already have like 15 open tabs with MSDN gdi32 references and 15 more with ctypes references, but seeing as I don't have a solid grasp on either, I feel like I'm going around in circles)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

春花秋月 2024-10-17 10:25:39

GetObject 不返回整个位图。 GetObject 调用后,picture.data 实际上为 NULL 。您需要 GetDIBits 才能阅读位图。以下是 GetObject 文档的相关摘录。

如果 hgdiobj 是位图句柄
通过调用 CreateDIBSection 创建,
并且指定的缓冲区很大
足够了,GetObject 函数返回
DIBSECTION 结构。此外,
BITMAP 的 bmBits 成员
结构内包含
DIBSECTION 将包含一个指向
位图的位值。

如果 hgdiobj 是位图的句柄
通过任何其他方式创建,GetObject
仅返回宽度、高度和
的颜色格式信息
位图。您可以获得位图的
通过调用 GetDIBits
GetBitmapBits 函数。


GetObject does not return the whole bitmap. picture.data is actually NULL after the GetObject call. You'll need GetDIBits to read the bitmap. Here's the relevant excerpt from the GetObject docs.

If hgdiobj is a handle to a bitmap
created by calling CreateDIBSection,
and the specified buffer is large
enough, the GetObject function returns
a DIBSECTION structure. In addition,
the bmBits member of the BITMAP
structure contained within the
DIBSECTION will contain a pointer to
the bitmap's bit values.

If hgdiobj is a handle to a bitmap
created by any other means, GetObject
returns only the width, height, and
color format information of the
bitmap
. You can obtain the bitmap's
bit values by calling the GetDIBits or
GetBitmapBits function.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文