Windows clipboard ::GetClipboardData() for CF_DIBV5 causes the image on the clipboard to be modified and corrupted?

Viewed 360

I found on (at least) Win10 that calling ::GetClipboardData() on a CF_DIBV5 created via Alt-PrtScrn (may be a synthesized format) causes the image to be modified (and basically corrupted).

For example, on the handler for ON_WM_CLIPBOARDUPDATE() the simple loop below will cause the corruption (note that you need to use debug mode so the ::GetClipboardData() is not optimized out).

To test, first don't run your app that processes the clipboard, use Alt-PrntScrn to capture data, then paste it to Paint. Now run the app that process the clipboard (with below sample below). Repeat Alt-PrntScrn process and you'll see it's different where the right side of the captured window ends up on the left and not centered in the area.

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();
    }
}

To enable the handler you need to call AddClipboardFormatListener(GetSafeHwnd()); in something like int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) then RemoveClipboardFormatListener(GetSafeHwnd()); on void CMainFrame::OnDestroy()

So is this a bug in Win10 (and other Windows Versions) or should I be doing something else that the sample isn't doing? (I know other formats exist, but the CF_DBIV5 is what I wanted).

I'm on Version 1903 (OS Build 18362.838)

Note the sample pic has right side items on left and some garbage pixels in lower left. I alt-prtscrn while app running, pasted in paint.

sample pic

My resolution is 2560x1600.

Here's a link to a project that will cause the problem:

Sample Project

1 Answers

You can find the following description in the documentation :

The red, green, and blue bitfield masks for BI_BITFIELD bitmaps immediately follow the BITMAPINFOHEADER, BITMAPV4HEADER, and BITMAPV5HEADER structures. The BITMAPV4HEADER and BITMAPV5HEADER structures contain additional members for red, green, and blue masks as follows.

enter image description here

When the biCompression member of BITMAPINFOHEADER is set to BI_BITFIELDS and the function receives an argument of type LPBITMAPINFO, the color masks will immediately follow the header. The color table, if present, will follow the color masks. BITMAPCOREHEADER bitmaps do not support color masks.

When you handle CF_DIBV5 correctly you will draw the image successfully. The following is an example of Win32 C++ you can refer to:

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;
}

The correct image drawn in my application window:

enter image description here

I can reproduce the same issue without code:

if (pDibv5Info->bV5Compression == BI_BITFIELDS)
    offset += 3 * sizeof(DWORD);

The corrupted image:

enter image description here

Related