嗨,我正在尝试用 c++ 创建一个屏幕捕获类(因为 python 屏幕截图速度很慢)。我创建了这个类,但我无法创建具有奇怪分辨率(例如 101x101)的屏幕截图。
我在 VS 中遇到一堆堆损坏和访问冲突错误,但我不知道如何调试它。感谢任何提示/技巧。
这是课程:
#include <windows.h>
#include <gdiplus.h>
#include <iostream>
#include <opencv2/opencv.hpp>
#pragma comment (lib,"Gdiplus.lib")
class ScreenCapturer {
private:
// GDI+ token and startup input
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
// Device contexts
HDC hScreenDC;
HDC hMemoryDC;
// Screen dimensions
int lastWidth;
int lastHeight;
// Bitmap
HBITMAP hBitmap;
HBITMAP hOldBitmap; // Store the old bitmap here to use for recovery and cleanup
public:
ScreenCapturer(){
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
hScreenDC = CreateDC(L"DISPLAY", NULL, NULL, NULL);
if (hScreenDC == NULL) {
throw std::runtime_error("Failed to create screen device context");
}
hMemoryDC = CreateCompatibleDC(hScreenDC);
if (hMemoryDC == NULL) {
DeleteDC(hScreenDC);
throw std::runtime_error("Failed to create memory device context.");
}
lastWidth = GetDeviceCaps(hScreenDC, HORZRES);
lastHeight = GetDeviceCaps(hScreenDC, VERTRES);
hBitmap = CreateCompatibleBitmap(hScreenDC, lastWidth, lastHeight);
if (!hBitmap) {
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);
throw std::runtime_error("Failed to create compatible bitmap");
}
hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);
}
~ScreenCapturer() {
SelectObject(hMemoryDC, hOldBitmap);
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);
DeleteObject(hBitmap);
Gdiplus::GdiplusShutdown(gdiplusToken);
}
cv::Mat captureScreen(int x, int y, int width, int height) {
if (lastWidth != width || lastHeight != height) {
SelectObject(hMemoryDC, hOldBitmap);
DeleteObject(hBitmap);
hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
/*hOldBitmap = (HBITMAP)*/SelectObject(hMemoryDC, hBitmap); // Do we need to re-store the old bitmap?
lastWidth = width;
lastHeight = height;
}
if (BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, x, y, SRCCOPY) == NULL) {
std::cout << "ERROR " << std::endl;
}
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = width;
bi.biHeight = -height; // Negative for top-left origin
bi.biPlanes = 1;
bi.biBitCount = 24;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
// Allocate a cv::Mat for the area
cv::Mat areaImage(height, width, CV_8UC3);
// Copy the bits to the cv::Mat
if (GetDIBits(hMemoryDC, hBitmap, 0, (UINT)height, areaImage.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS) == NULL) {
std::cout << "ERROR " << std::endl;
}
return areaImage;
}
};
我正在这样测试这个类:
#include "ScreenCapturer.cpp"
int main() {
ScreenCapturer capturer;
auto screenshot1 = capturer.captureScreen(0, 0, 1920, 1080); // Works
cv::imwrite("test1.png", screenshot1);
auto screenshot2 = capturer.captureScreen(0, 0, 100, 100); // Works
cv::imwrite("test2.png", screenshot2);
auto screenshot3 = capturer.captureScreen(0, 0, 101, 101); // Doesn't work
cv::imwrite("test3.png", screenshot3);
std::cout << "Done\n";
}
我试图从 VS 获取更多信息。当它在调试模式下失败时,它首先会出现一条执行断点指令并显示以下消息:
断点指令(__debugbreak() 语句或类似的调用) 在Everylay.exe中执行。
然后当我继续时,它会显示如下消息:
Everylay.exe 中 0x00007FF9A7A4F349 (ntdll.dll) 处存在未处理的异常: 0xC0000374:堆已损坏(参数: 0x00007FF9A7AB97F0)。
然后是这个:
Everylay.exe 中的 0x00007FF9A797D990 (ntdll.dll) 处抛出异常: 0xC0000005:读取位置 0xFFFFFFFFFFFFFFFF 时发生访问冲突。
我真的不知道如何调试这个。希望有人能帮忙。
lpvBits
的
GetDIBits
参数指向的缓冲区必须具有足以容纳
height * scanline_width
字节的大小。其中
scanline_width
是 4 字节对齐的
img_width * bytes_per_pixel
。对于 101 图像宽度和每像素 3 字节的
cv::mat
提供的缓冲区来说,其大小将不够,因为它无法正确对齐扫描线。所以你会遇到缓冲区溢出。