Winapi 用于CF_DIBV5的Windows剪贴板::GetClipboardData()是否会导致剪贴板上的图像被修改和损坏?
我在(至少)Win10上发现,在通过Alt-PrtScrn(可能是合成格式)创建的CF_DIBV5上调用Winapi 用于CF_DIBV5的Windows剪贴板::GetClipboardData()是否会导致剪贴板上的图像被修改和损坏?,winapi,clipboard,ole,Winapi,Clipboard,Ole,我在(至少)Win10上发现,在通过Alt-PrtScrn(可能是合成格式)创建的CF_DIBV5上调用::GetClipboardData(),会导致图像被修改(并且基本上已损坏) 例如,在\u WM\u CLIPBOARDUPDATE()上的处理程序上,下面的简单循环将导致损坏(请注意,您需要使用调试模式,以便::GetClipboardData()未优化) 要进行测试,首先不要运行处理剪贴板的应用程序,使用Alt-PrntScrn捕捉数据,然后将其粘贴到绘图。现在运行处理剪贴板的应用程序(
::GetClipboardData()
,会导致图像被修改(并且基本上已损坏)
例如,在\u WM\u CLIPBOARDUPDATE()上的处理程序上,下面的简单循环将导致损坏(请注意,您需要使用调试模式,以便::GetClipboardData()未优化)
要进行测试,首先不要运行处理剪贴板的应用程序,使用Alt-PrntScrn捕捉数据,然后将其粘贴到绘图。现在运行处理剪贴板的应用程序(以下示例)。重复Alt PrntScrn过程,您将看到捕获窗口的右侧在左侧结束,而不是在该区域居中的位置有所不同
void CMainFrame::OnClipboardUpdate()
if (::OpenClipboard(AfxGetMainWnd()->m_hWnd)) {
UINT uformat=0;
while ((uformat=::EnumClipboardFormats(uformat))!=0) {
if (uformat==CF_DIBV5) {
// get the data - run in debug mode so not optimized out
HGLOBAL hglobal=::GetClipboardData(uformat);
}
}
// clean up
::CloseClipboard();
}
}
要启用处理程序,需要调用AddClipboardFormatListener(GetSafeHwnd())代码>类似于int CMainFrame::OnCreate(LPCREATESTRUCT LPCREATESTRUCT)
然后RemoveClipboardFormatListener(GetSafeHwnd())代码>打开void CMainFrame::OnDestroy()
那么这是Win10(和其他Windows版本)中的一个bug,还是我应该做一些示例没有做的事情?(我知道还有其他格式,但CF_DBIV5正是我想要的)
我使用的是版本1903(OS Build 18362.838)
请注意,示例pic在左侧有右侧项目,在左下角有一些垃圾像素。我在应用程序运行时使用alt-prtscrn,粘贴在油漆中
我的分辨率是2560x1600
下面是一个指向将导致问题的项目的链接:
您可以在中找到以下描述:
BI\u位域的红色、绿色和蓝色位域掩码
紧跟在BitMapInfo标头
、BITMAPV4HEADER
之后,然后
BITMAPV5HEADER
结构。bitmapv4头文件
和bitmapv5头文件
结构包含红色、绿色和蓝色遮罩的其他成员
具体如下
当bitmapinfo
的biCompression
成员设置为biu BITFIELDS
并且函数接收到类型为LPBITMAPINFO
的参数时,颜色掩码将立即跟随在标题后面。颜色表(如果存在)将跟随颜色遮罩<代码>位图CoreHeader
位图不支持颜色掩码
正确处理CF_DIBV5
后,您将成功绘制图像。下面是Win32 C++的一个例子,可以参考:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static UINT uFormat = (UINT) -1;
HDC hdcMem = NULL;
RECT rc = {0};
BYTE * pData = NULL;
BITMAPV5HEADER *pDibv5Info = NULL;
switch (message)
{
case WM_CLIPBOARDUPDATE:
{
if (IsClipboardFormatAvailable(CF_DIBV5))
{
uFormat = CF_DIBV5;
::CloseClipboard();
GetClientRect(hWnd, &rc);
InvalidateRect(hWnd, &rc, TRUE);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
switch (uFormat)
{
case CF_DIBV5:
hdcMem = CreateCompatibleDC(hdc);
if (hdcMem != NULL)
{
if (::OpenClipboard(hWnd)) {
HANDLE hglobal = ::GetClipboardData(uFormat);
pData = (BYTE*)GlobalLock(hglobal);
if (pData)
{
pDibv5Info = (BITMAPV5HEADER *)pData;
int offset = pDibv5Info->bV5Size + pDibv5Info->bV5ClrUsed * sizeof(RGBQUAD);
if (pDibv5Info->bV5Compression == BI_BITFIELDS)
offset += 3 * sizeof(DWORD); //three DWORD color masks that specify the red, green, and blue components
pData += offset;
SetDIBitsToDevice(hdc, 20, 20, pDibv5Info->bV5Width, pDibv5Info->bV5Height, 0, 0, 0, pDibv5Info->bV5Height, pData, (BITMAPINFO *)pDibv5Info, 0);
}
GlobalUnlock(hglobal);
::CloseClipboard();
}
}
break;
}
EndPaint(hWnd, &ps);
}
break;
}
在“我的应用程序”窗口中绘制的正确图像:
我可以不使用代码复制相同的问题:
if (pDibv5Info->bV5Compression == BI_BITFIELDS)
offset += 3 * sizeof(DWORD);
损坏的图像:
为什么要使用EnumClipboardFormats()
查找1种格式?您可以改用IsClipboardFormatAvailable()
。或者,只需无条件调用GetClipboardData()
,如果所需的格式不可用,就让它失败。我处理了更多的格式,这只是为了显示问题。@df234987我无法在Windows 10 1909 build 18363.778上重现此问题。你能显示你的Windows版本和显示损坏图像的快照吗?添加了操作系统版本和示例图片。我在Windows 10 1903(os Build 18362.836)上进行了测试,它也适用于我。你的.838是打字错误吗?您能否再次检查一下,这个问题是否是由您显示的CMainFrame::OnClipboardUpdate()
中的代码行引起的?例如,删除CMainFrame::OnClipboardUpdate()
的主体,此问题是否仍会重现?我不知道这与报告的Windows错误有什么关系?我没有画任何东西,除了调用::GetClipboardData(CF_DIBV5)代码>。一旦完成,将图像粘贴到其他应用程序将损坏。如果你不这样做,那没关系。因此,windows::GetClipboardData()中的某些内部内容正在破坏某些内容。我不确定MSPAINT使用的是什么,但它已被::GetClipboardData()损坏。@df234987无需报告剪贴板API/文档的问题。剪贴板上有CF_DIBV5
的正确数据,因此我可以使用这些数据绘制正确的图像GetClipboardData()
不会损坏任何内容。它会导致系统崩溃。您看到此问题是因为mspaint似乎不支持转换后的格式DIBV5。如果您还有任何问题,请随时告诉我。或者使用CF_位图或CF_DIB?它似乎会要求任何它想要的格式。如果你把一个CF_DIBV5放在剪贴板上,它要求提供CF_位图,它会返回一个CF_位图。因此,如果剪贴板正在GetClipboardData()
上转换它,然后将其转换回CF_位图或CF_DIB,那么这可能就是问题所在。但有些东西改变了某些东西,我怀疑MSPAINT是否会要求不同的格式。您是否已完成此操作,然后尝试了CF_位图和CF_DIB版本?@df234987只有一次转换。如果请求的格式与可用格式匹配,则不进行转换,而如果请求的格式与可用格式不匹配,则进行转换。我们不知道打印屏幕快捷方式提供了什么格式,但它提供了mspaint支持的格式。我们也不知道msprint请求的格式,但它似乎不是CF_DIBV5
GetClipboardData(CF_DIB)
和GetClipboardData(CF_位图)
不会导致此问题。@df234987d您是对的。当在CF_DIBV5
之前请求CF_DIB
格式时,将正确检索数据。但是,当在CF_DIBV5
之后请求CF_DIB
格式时,检索到的数据错误,生成了附加的颜色掩码表。我会在内部报告这个问题。