使用 C++ returns 旧数据捕获特定 window
Capturing a specific window using C++ returns old data
我目前正在做一个需要截取特定 window 屏幕截图的项目。
这就是我到目前为止所得到的:
主要功能
int main() {
LPCSTR windowname = "Calculator";
HWND handle = FindWindowA(NULL, windowname);
while (!handle) {
std::cout << "Process not found..." << std::endl;
handle = FindWindowA(NULL, windowname);
Sleep(100);
}
Mat img = captureScreenMat(handle);
resetMat();
imwrite("test2.jpg", img);
showInMovedWindow("IMG", img);
waitKey(0);
destroyAllWindows();
return 0;
}
winCapture
Mat src;
void showInMovedWindow(string winname, Mat img) {
namedWindow(winname);
moveWindow(winname, 40, 30);
imshow(winname, img);
}
BITMAPINFOHEADER createBitmapHeader(int width, int height)
{
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = width;
bi.biHeight = -height;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
return bi;
}
int getHeight(HWND hwnd) {
RECT rect;
GetWindowRect(hwnd, &rect);
int height = rect.bottom - rect.top;
return height;
}
int getWidth(HWND hwnd) {
RECT rect;
GetWindowRect(hwnd, &rect);
int width = rect.right - rect.left;
return width;
}
Mat captureScreenMat(HWND hwnd)
{
HDC hwindowDC = GetDC(hwnd);
HDC hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);
int screenx = GetSystemMetrics(SM_XVIRTUALSCREEN);
int screeny = GetSystemMetrics(SM_YVIRTUALSCREEN);
int width = getWidth(hwnd);
int height = getHeight(hwnd);
src.create(height, width, CV_8UC4);
HBITMAP hbwindow = CreateCompatibleBitmap(hwindowDC, width, height);
BITMAPINFOHEADER bi = createBitmapHeader(width, height);
//DEBUG
cout << hbwindow << endl;
cout << hwindowCompatibleDC << endl;
cout << hwindowDC << endl;
SelectObject(hwindowCompatibleDC, hbwindow);
StretchBlt(hwindowCompatibleDC, 0, 0, width, height, hwindowDC, screenx, screeny, width, height, SRCCOPY);
GetDIBits(hwindowCompatibleDC, hbwindow, 0, height, src.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
DeleteObject(hbwindow);
DeleteDC(hwindowCompatibleDC);
DeleteDC(hwindowDC);
ReleaseDC(NULL, hwindowDC);
return src;
}
void resetMat() {
src.release();
}
大多数 windows 使用这种方法工作得很好,但有一些 windows 是第一次工作,我尝试对它们进行 img,但每次我尝试采取同一过程的另一个屏幕截图,它只是给了我我截取的第一个屏幕截图。它仅在重新启动该过程后再次起作用,即使如此,它也仅对一个屏幕截图再次起作用,并且之后的所有屏幕截图都是相同的。
我认为这会是某种内存泄漏,但我正在删除所有对象并释放句柄。
我认为手柄有问题,但我不知道是什么。
我不熟悉 windowsAPI 的工作,希望有人比我更了解。
已修复:进程阻止创建句柄。
当您调用 SelectObject
时,您必须保存之前的 selected 句柄(可从 return 值获得)并且您必须 select 在删除或释放设备上下文。
现在你违反了一堆规则。
- 正在删除 selected 到设备上下文中的位图。
- 正在删除从
GetDC
获得的 DC。
- 在同一句柄上同时调用
DeleteDC
和 ReleaseDC
。
- 传递
NULL
作为 ReleaseDC
的第一个参数,这应该与传递给 GetDC
的 HWND
相同。
- 删除 DC 而不 select 将原始位图重新放入其中。
这些 bug 会把 window DC 搞得一团糟。如果它是瞬态 DC,系统可能会立即清理你的烂摊子。但是如果 window class 有 CS_OWNDC
或 CS_CLASSDC
标志,你会对 window 造成永久性伤害。这就是为什么您的方法似乎适用于某些 windows 而不是其他人。
我目前正在做一个需要截取特定 window 屏幕截图的项目。 这就是我到目前为止所得到的:
主要功能
int main() {
LPCSTR windowname = "Calculator";
HWND handle = FindWindowA(NULL, windowname);
while (!handle) {
std::cout << "Process not found..." << std::endl;
handle = FindWindowA(NULL, windowname);
Sleep(100);
}
Mat img = captureScreenMat(handle);
resetMat();
imwrite("test2.jpg", img);
showInMovedWindow("IMG", img);
waitKey(0);
destroyAllWindows();
return 0;
}
winCapture
Mat src;
void showInMovedWindow(string winname, Mat img) {
namedWindow(winname);
moveWindow(winname, 40, 30);
imshow(winname, img);
}
BITMAPINFOHEADER createBitmapHeader(int width, int height)
{
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = width;
bi.biHeight = -height;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
return bi;
}
int getHeight(HWND hwnd) {
RECT rect;
GetWindowRect(hwnd, &rect);
int height = rect.bottom - rect.top;
return height;
}
int getWidth(HWND hwnd) {
RECT rect;
GetWindowRect(hwnd, &rect);
int width = rect.right - rect.left;
return width;
}
Mat captureScreenMat(HWND hwnd)
{
HDC hwindowDC = GetDC(hwnd);
HDC hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);
int screenx = GetSystemMetrics(SM_XVIRTUALSCREEN);
int screeny = GetSystemMetrics(SM_YVIRTUALSCREEN);
int width = getWidth(hwnd);
int height = getHeight(hwnd);
src.create(height, width, CV_8UC4);
HBITMAP hbwindow = CreateCompatibleBitmap(hwindowDC, width, height);
BITMAPINFOHEADER bi = createBitmapHeader(width, height);
//DEBUG
cout << hbwindow << endl;
cout << hwindowCompatibleDC << endl;
cout << hwindowDC << endl;
SelectObject(hwindowCompatibleDC, hbwindow);
StretchBlt(hwindowCompatibleDC, 0, 0, width, height, hwindowDC, screenx, screeny, width, height, SRCCOPY);
GetDIBits(hwindowCompatibleDC, hbwindow, 0, height, src.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
DeleteObject(hbwindow);
DeleteDC(hwindowCompatibleDC);
DeleteDC(hwindowDC);
ReleaseDC(NULL, hwindowDC);
return src;
}
void resetMat() {
src.release();
}
大多数 windows 使用这种方法工作得很好,但有一些 windows 是第一次工作,我尝试对它们进行 img,但每次我尝试采取同一过程的另一个屏幕截图,它只是给了我我截取的第一个屏幕截图。它仅在重新启动该过程后再次起作用,即使如此,它也仅对一个屏幕截图再次起作用,并且之后的所有屏幕截图都是相同的。 我认为这会是某种内存泄漏,但我正在删除所有对象并释放句柄。 我认为手柄有问题,但我不知道是什么。 我不熟悉 windowsAPI 的工作,希望有人比我更了解。
已修复:进程阻止创建句柄。
当您调用 SelectObject
时,您必须保存之前的 selected 句柄(可从 return 值获得)并且您必须 select 在删除或释放设备上下文。
现在你违反了一堆规则。
- 正在删除 selected 到设备上下文中的位图。
- 正在删除从
GetDC
获得的 DC。 - 在同一句柄上同时调用
DeleteDC
和ReleaseDC
。 - 传递
NULL
作为ReleaseDC
的第一个参数,这应该与传递给GetDC
的HWND
相同。 - 删除 DC 而不 select 将原始位图重新放入其中。
这些 bug 会把 window DC 搞得一团糟。如果它是瞬态 DC,系统可能会立即清理你的烂摊子。但是如果 window class 有 CS_OWNDC
或 CS_CLASSDC
标志,你会对 window 造成永久性伤害。这就是为什么您的方法似乎适用于某些 windows 而不是其他人。