【问题标题】:Windows clipboard ::GetClipboardData() for CF_DIBV5 causes the image on the clipboard to be modified and corrupted?CF_DIBV5 的 Windows 剪贴板 ::GetClipboardData() 导致剪贴板上的图像被修改和损坏?
【发布时间】:2020-05-14 03:40:17
【问题描述】:

我在(至少)Win10 上发现,在通过 Alt-PrtScrn(可能是合成格式)创建的 CF_DIBV5 上调用 ::GetClipboardData() 会导致图像被修改(并且基本上已损坏)。

例如,在ON_WM_CLIPBOARDUPDATE() 的处理程序上,下面的简单循环将导致损坏(请注意,您需要使用调试模式,因此不会优化 ::GetClipboardData())。

要进行测试,首先不要运行处理剪贴板的应用,使用 Alt-PrntScrn 捕获数据,然后将其粘贴到 Paint。现在运行处理剪贴板的应用程序(下面的示例)。重复 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();
    }
}

要启用处理程序,您需要在 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 中调用 AddClipboardFormatListener(GetSafeHwnd());,然后在 void CMainFrame::OnDestroy() 上调用 RemoveClipboardFormatListener(GetSafeHwnd());

那么这是 Win10(和其他 Windows 版本)中的一个错误,还是我应该做一些示例没有做的事情? (我知道存在其他格式,但 CF_DBIV5 是我想要的)。

我使用的是 1903 版(操作系统内部版本 18362.838)

请注意,示例图片左侧有右侧项目,左下方有一些垃圾像素。我在应用程序运行时 alt-prtscrn,粘贴在油漆中。

我的分辨率是 2560x1600。

这是一个会导致问题的项目的链接:

Sample Project

【问题讨论】:

  • 为什么要使用EnumClipboardFormats() 来查找1 格式?您可以改用IsClipboardFormatAvailable()。或者,只需无条件调用GetClipboardData(),如果所需格式不可用,则让它失败。
  • 我处理更多格式,这只是为了显示问题而提取出来的。
  • @df234987 我无法在 Windows 10 1909 build 18363.778 上重现此问题。您能否显示您的 Windows 版本和显示损坏图像的快照?
  • 添加了操作系统版本和示例图片。
  • 我在 Windows 10 1903 (OS Build 18362.836) 上测试,它也适用于我。您的 .838 是错字吗?您能否仔细检查一下,如果您显示的 CMainFrame::OnClipboardUpdate() 中的代码行引入了此问题?比如去掉CMainFrame::OnClipboardUpdate()的body,这个问题还能重现吗?

标签: winapi clipboard ole


【解决方案1】:

您可以在documentation 中找到以下描述:

BI_BITFIELD 位图的红色、绿色和蓝色位域掩码 立即关注BITMAPINFOHEADERBITMAPV4HEADERBITMAPV5HEADER 结构。 BITMAPV4HEADERBITMAPV5HEADER 结构包含红色、绿色和蓝色蒙版的附加成员 如下。

BITMAPINFOHEADERbiCompression 成员设置为BI_BITFIELDS 并且函数接收LPBITMAPINFO 类型的参数时,颜色掩码将立即跟随标题。颜色表(如果存在)将遵循颜色掩码。 BITMAPCOREHEADER 位图不支持颜色蒙版。

当您正确处理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);

损坏的图像:

【讨论】:

  • 我不知道这与报告的 Windows 错误有什么关系?我没有画任何东西,除了打电话::GetClipboardData(CF_DIBV5);。完成后,将图像粘贴到其他应用程序已损坏。如果你不这样做,那很好。因此,Windows ::GetClipboardData() 内部的某些东西正在破坏某些东西。我不确定 MSPAINT 使用什么,但它已被 ::GetClipboardData() 损坏。
  • @df234987 无需报告剪贴板 API/文档的问题。剪贴板具有CF_DIBV5 的正确数据,因此我可以使用该数据绘制正确的图像。 GetClipboardData() 不会破坏任何东西。它会导致系统converts an available format to the requested format。您会看到此问题,因为 mspaint 似乎不支持转换后的格式 DIBV5。如果您仍有任何疑问,请随时告诉我。
  • 或者也许使用 CF_BITMAP 或 CF_DIB?它似乎会要求它想要的任何格式。如果您将 CF_DIBV5 放在剪贴板上并且它要求 CF_BITMAP 它会返回一个 CF_BITMAP。因此,如果剪贴板将其转换为GetClipboardData(),然后将其转换回 CF_BITMAP 或 CF_DIB,那么这可能就是问题所在。但是有些东西会改变一些东西,我怀疑 MSPAINT 会请求不同的格式。你这样做了,然后尝试了 CF_BITMAP 和 CF_DIB 版本?
  • @df234987 只有一次转换。如果请求的格式与可用的格式匹配,则不会发生转换,而如果请求的格式与可用的格式不匹配,则会发生转换。我们不知道打印屏幕快捷方式为我们提供了什么格式,但它提供了 mspaint 支持的格式。我们也不知道 msprint 请求的格式,但似乎不是CF_DIBV5GetClipboardData(CF_DIB)GetClipboardData(CF_BITMAP) 不会导致此问题。
  • @df234987d 你是对的。在CF_DIBV5 之前请求CF_DIB 格式时,将正确检索数据。但是,当在CF_DIBV5 之后请求CF_DIB 格式时,检索到的数据是错误的,并生成了一个额外的颜色掩码表。我会在内部报告这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-08
  • 1970-01-01
  • 1970-01-01
  • 2011-10-14
  • 1970-01-01
相关资源
最近更新 更多