gpt4 book ai didi

windows - 为什么不能在剪贴板上放置DIB部分?

转载 作者:行者123 更新时间:2023-12-03 11:10:45 25 4
gpt4 key购买 nike

我试图捕获屏幕内容,直接修改捕获图像的位,然后将结果放在剪贴板上。 (实际上,我最终对剪贴板并不感兴趣,但是正在将其用作测试步骤。)

我从对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);
}

但是,这不起作用。所有调用均报告成功,但该图像未最终显示在剪贴板上。

当我在调试器中运行此代码时,在调用 pBits之后,我可以在 BitBlt上看到看起来像图像数据的图片,尽管这有点令人怀疑,因为第一堆值的R,G,B都相同,但是最下面的值屏幕的左上角实际上是蓝色。无论如何,即使实际的位错了,我也应该在剪贴板上得到一些图像,但是我没有。

我尝试使用 CF_DIB作为 SetClipboardData的第一个参数,而不是 CF_BITMAP,但是调用失败。

如果我注释掉对 CreateDIBSection的调用并取消注释对 CreateCompatibleBitmap的调用,那么它可以工作,但是我没有机会直接修改图像位。

我想我可以先捕获我的DIB部分,然后对其进行修改,然后调用 CreateCompatibleBitmap,然后将DIB部分中的内容blit到“兼容位图”中,但是似乎没有任何明显的理由再次复制这些位。

为什么我不能将DIB部分传递给 SetClipboardData

(我必须说我讨厌与GDI等一起工作。通常很明显是泥泞。)

最佳答案

最终,当我找到this时,找到了答案。 MSDN上的API文档对此相当含糊,可能是因为它本身可以追溯到很早,但是看起来剪贴板函数都使用Windows 3.x样式的内存分配系统(GlobalAlloc等)。

对于系统剪贴板,将共享内存直接公开给应用程序是有意义的,而不是OS必须将数据复制到内部缓冲区中。但是剪贴板功能可以追溯到很远,以至于不存在基于较新页面文件的共享内存方案,因此它们必须使用GlobalAlloc内存。当32位Windows出现时,仅模拟该机制而不是破坏现有的应用程序代码会更有意义。

我强烈怀疑,出于类似的原因,大多数GDI句柄实际上也实际上是GlobalAlloc句柄,这就是为什么您可以将CreateCompatibleBitmap的返回值传递给剪贴板。相比之下,CreateDIBSection没有完全使用旧式分配,这很明显,因为您可以告诉它将位存储在文件映射中。 (我怀疑它返回的句柄仍来自GlobalAlloc,但是如此分配的块又包含指向图像数据虚拟内存的直接指针,因此SetClipboardData对此进行了测试,因为它显然是“陷阱”。)

因此,我只通过让CreateDIBSection分配到所需的位置来固定所有内容,因为无论如何,都不可能将其交给SetClipboardData,然后在我想发送到剪贴板时执行此操作:

void CScreenshot::SendToClipboard( void )
{
HGLOBAL hClipboardDib = GlobalAlloc( GMEM_MOVEABLE | GMEM_SHARE, cbDib );
if( !hClipboardDib )
throw 0;

void *pClipboardDib = GlobalLock( hClipboardDib );
memcpy( pClipboardDib, &BitmapInfo, sizeof(BITMAPINFOHEADER) );
memcpy( (BITMAPINFOHEADER*)pClipboardDib+1, pBits, BitmapInfo.bmiHeader.biSizeImage );
GlobalUnlock( hClipboardDib );

if( !OpenClipboard( NULL ) )
{
GlobalFree( hClipboardDib );
throw 0;
}

EmptyClipboard();
SetClipboardData( CF_DIB, hClipboardDib );
CloseClipboard();
}

不幸的是,我必须在这里制作一个冗余副本,但是从正面来看,我强烈怀疑阅读剪贴板的应用程序会看到相同的副本,而不是Windows在内部进行任何进一步的复制。

如果我想成为一个总效率很高的迷,我怀疑 CreateCompatibleBitmap返回的句柄可以用于对 GlobalLock的调用中,那么您就可以直接获得这些位,而无需在 CScreenshot::SendToClipboard中生成副本,因为您可以将其直接传递给 SetClipboardData。但是,我也强烈怀疑这是未记录的行为(如果我错了,请纠正我!),所以这是一个非常糟糕的主意。您还必须跟踪是否将其传递到剪贴板,如果没有,则不要在其上调用 DeleteObject。但是我不确定。我还怀疑 SetClipboardData无论如何都要复制它,因为它可能没有与 GMEM_SHARE一起分配。

感谢评论者使我更加了解它。

关于windows - 为什么不能在剪贴板上放置DIB部分?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50313607/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com