【发布时间】:2018-05-13 07:04:54
【问题描述】:
我正在尝试捕获屏幕内容,直接修改抓取图像的位,然后将结果放在剪贴板上。 (实际上,我最终对剪贴板并不感兴趣,而是将其用作测试步骤。)
我从this question 的答案之一开始。但是,它使用CreateCompatibleBitmap,据我了解,无法直接访问使用该函数创建的位图位,因此我尝试改用CreateDIBSection。这是我目前所拥有的:
void GetScreenShot(void)
{
int x1, y1, w, h;
// get screen dimensions
x1 = GetSystemMetrics(SM_XVIRTUALSCREEN);
y1 = GetSystemMetrics(SM_YVIRTUALSCREEN);
w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
// copy screen to bitmap
HDC hScreen = GetDC(NULL);
HDC hDC = CreateCompatibleDC(hScreen);
if( !hDC )
throw 0;
// This works:
//HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, w, h);
BITMAPINFO BitmapInfo;
BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfo.bmiHeader.biWidth = w;
BitmapInfo.bmiHeader.biHeight = h;
BitmapInfo.bmiHeader.biPlanes = 1;
BitmapInfo.bmiHeader.biBitCount = 24; // assumption; ok for our use case
BitmapInfo.bmiHeader.biCompression = BI_RGB;
BitmapInfo.bmiHeader.biSizeImage = ((w * 3 + 3) & ~3) * h;
BitmapInfo.bmiHeader.biXPelsPerMeter = (int)(GetDeviceCaps( hScreen, LOGPIXELSX ) * 39.3701 + 0.5);
BitmapInfo.bmiHeader.biYPelsPerMeter = (int)(GetDeviceCaps( hScreen, LOGPIXELSY ) * 39.3701 + 0.5);
BitmapInfo.bmiHeader.biClrUsed = 0;
BitmapInfo.bmiHeader.biClrImportant = 0;
BitmapInfo.bmiColors[0].rgbBlue = 0;
BitmapInfo.bmiColors[0].rgbGreen = 0;
BitmapInfo.bmiColors[0].rgbRed = 0;
BitmapInfo.bmiColors[0].rgbReserved = 0;
void *pBits;
// This does not work:
HBITMAP hBitmap = CreateDIBSection( hScreen, &BitmapInfo, DIB_RGB_COLORS, &pBits, NULL, 0 );
if( !hBitmap )
throw 0;
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
if( !old_obj )
throw 0;
if( !BitBlt(hDC, 0, 0, w, h, hScreen, x1, y1, SRCCOPY) )
throw 0;
if( !SelectObject(hDC, old_obj) )
throw 0;
if( !GdiFlush() )
throw 0;
// this is where we would modify the image
// save bitmap to clipboard
if( !OpenClipboard(NULL) )
throw 0;
if( !EmptyClipboard() )
throw 0;
if( !SetClipboardData( CF_BITMAP, hBitmap ) ) // CF_DIB causes the throw
throw 0;
if( !CloseClipboard() )
throw 0;
// clean up
DeleteDC(hDC);
ReleaseDC(NULL, hScreen);
DeleteObject(hBitmap);
}
但是,这不起作用。所有调用都报告成功,但图像并未最终出现在剪贴板上。
当我在调试器中运行它时,在调用 BitBlt 之后,我可以在 pBits 看到看起来像图像数据的内容,尽管有点可疑,因为第一组值都有 R、G、B一样,但我屏幕的左下角实际上是蓝色的。无论如何,即使实际的位是错误的,我也应该在剪贴板上得到 something 的图像,但我没有。
我尝试使用CF_DIB 作为SetClipboardData 的第一个参数而不是CF_BITMAP,但随后调用失败。
如果我注释掉对CreateDIBSection 的调用并取消注释对CreateCompatibleBitmap 的调用,那么它可以工作,但我没有机会直接修改图像位。
我想我可以先捕获我的 DIB 部分,对其进行修改,然后调用 CreateCompatibleBitmap 并将 DIB 部分中的 blit 复制到“兼容位图”中,但毫无理由地再次复制这些位似乎有点愚蠢。
为什么我不能将我的 DIB 部分传递给 SetClipboardData?
(我必须说我讨厌使用 GDI 等。它通常像泥巴一样清晰。)
【问题讨论】:
-
CF_DIB想要一个打包的位图,即包含BITMAPINFO后跟位图位的单个内存块 - 而不是 GDI 句柄。CreateDIBSection允许您使用文件映射句柄和偏移量指定位图的放置位置。创建一个足够大的内存映射文件以包含标头和位,并适当地设置dwOffset以指向标头。 -
@JonathanPotter 感谢您的提示.. 有一点不清楚是什么作为
SetClipboardData的第二个参数传递。我试过从CreateDIBSection返回,从CreateFileMapping返回,和(HANDLE)(((BITMAPINFO*)pBits) - 1),但它们都不起作用。使用CF_DIB时,都会报错0x00000006 The handle is invalid.我已经把header复制到了映射中(CreateDIBSection显然没有),但是还是不行。 -
另一个奇怪的是
CF_BITMAPnever 会产生错误。甚至SetClipboardData( CF_BITMAP, (HANDLE)0xCCCCCCCC )声称要成功!这个API好像有点乱。 -
CreateCompatibleBitmap让您可以直接访问这些位。GetDIBits怎么了? -
@BarmakShemirani 谢谢你的提示。如果我理解正确,
GetDIBits复制 位,那么一旦我做出更改,我就必须致电SetDIBits将它们放回原处。但我想我得试试这个,因为我不知道还能做什么。有点令人沮丧,我不得不为本来只需要 2 份的东西买 4 份副本……难怪现代程序这么慢!
标签: windows winapi clipboard gdi