0

I am renovating some early MFC-based code. In which there are clipboard routines that worked fine at the time. But when I rebuild them with Visual Studio 2022, they build but hang up at EmptyClipboard() when the program is run. Sometimes when I step through the code, it will function, but most times it will hang up at the same point, with a "A breakpoint instruction (__debugbreak() statement or a similar call) was executed" message given as the only explanation. If the program is run outside of debugging, then it simply terminates. The Microsoft literature indicates that the OpenClipboard() calls for a parameter of HWND or NULL, but if NULL is used then the SetClipboardData procedure will fail. The program doesn't seem to get that far. The older version of OpenClipboard() calls for no parameters, and if I try to put one in, it is rejected. The literature calls for a header file of winuser.h. But after #included, it makes no difference, OpenClipboard() still will take no parameter. Does anyone have a clue as to what's going on? Thanks.

This is the entire original working function;

void CDibView::OnEditCopy()
{   
    if( m_id == ID_PALETTEEDIT )
    {
        m_edit.Copy();
        return;
    }
    BeginWaitCursor();
    CDibDoc* pDoc = GetDocument();
    // Clean clipboard of contents, and copy the DIB.

    if (OpenClipboard())
    {   
        BITMAPINFOHEADER    m_Dibhead;
        HANDLE  m_hBitstore2;
        
        m_Dibhead.biSize = sizeof (BITMAPINFOHEADER);
        
        CSize now = rectSave.Size();
        
        m_Dibhead.biWidth = now.cx;
        m_Dibhead.biHeight = now.cy;
        m_Dibhead.biPlanes = 1;
        m_Dibhead.biBitCount = 24;
        m_Dibhead.biCompression = 0;
        m_Dibhead.biSizeImage = ((DWORD)m_Dibhead.biWidth * m_Dibhead.biBitCount/ 8) * (DWORD)m_Dibhead.biHeight;
        m_Dibhead.biXPelsPerMeter = 0;
        m_Dibhead.biYPelsPerMeter = 0;
        m_Dibhead.biClrUsed = 0;
        m_Dibhead.biClrImportant = 0;
        
        DWORD count = (((DWORD)m_Dibhead.biWidth * m_Dibhead.biBitCount/ 8) * (DWORD)m_Dibhead.biHeight) + (unsigned long)sizeof( BITMAPINFOHEADER );
        HBITMAP hBitmap2;
        char huge* m_pBitstore;
        LPBITMAPINFOHEADER binfo;
        CClientDC dc( this );
        int next;
        for( next = 0; next < 2000; next++ )
        {
             m_pBitstore = (char huge*)GlobalAllocPtr( GPTR, count + (unsigned long)next );
             binfo = (LPBITMAPINFOHEADER)m_pBitstore;
             *binfo = m_Dibhead;
             if( !(hBitmap2 = ::CreateDIBitmap( dc.m_hDC, (LPBITMAPINFOHEADER)m_pBitstore, CBM_INIT, (LPSTR)m_pBitstore + (unsigned long)sizeof( BITMAPINFOHEADER ), (LPBITMAPINFO)m_pBitstore, DIB_RGB_COLORS ) ))
             {
                GlobalFreePtr( m_pBitstore );    //There is a problem in fabricating the packed DIB used by clipboard here
             }                                   //which dosn't seem to show until an attempt to construct a devise dependant
             else                                //bitmap used in the data transfer.  This loop searches for a size of memory
                break;                           //allocation which will function.
        }
        GlobalFreePtr( m_pBitstore );
/*
        #ifdef _DEBUG
            afxDump << "Dumping next: =  "
                    <<  next
                    << "\n";
        #endif
 */
        m_hBitstore2 = (HANDLE) ::GlobalAlloc( GPTR, count + (unsigned long)next );
        LPBITMAPINFOHEADER m_pBitstore2;
        m_pBitstore2 = (LPBITMAPINFOHEADER) ::GlobalLock( (HGLOBAL) m_hBitstore2 );
        *m_pBitstore2 = m_Dibhead;
        ::GlobalUnlock((HGLOBAL) m_hBitstore2);
        m_pBitstore = (char huge*) ::GlobalLock((HGLOBAL) m_hBitstore2);
        
        CDibDoc* pDoc = GetDocument();
        HDIB hDIB = pDoc->GetHDIB();
        BYTE huge *    lpDIBHdr;            // Pointer to BITMAPINFOHEADER
        BYTE huge *    lpDIBBits;           // Pointer to DIB bits
        lpDIBHdr  = (BYTE huge *) ::GlobalLock((HGLOBAL) hDIB);
        lpDIBBits = (BYTE huge *)::FindDIBBits((LPSTR)lpDIBHdr);
        HDC hdcMem;
        HDC hdcMem2;
        hdcMem = ::CreateCompatibleDC( dc.m_hDC );//create a memory device context (display surface)
        hdcMem2 = ::CreateCompatibleDC( dc.m_hDC );     
        HBITMAP hBitmap;
        hBitmap = CreateDIBitmap( dc.m_hDC, (LPBITMAPINFOHEADER)lpDIBHdr, CBM_INIT, lpDIBBits, (LPBITMAPINFO)lpDIBHdr, DIB_RGB_COLORS );
        char huge* ptr;
        ptr = m_pBitstore + (unsigned long)sizeof (BITMAPINFOHEADER);
        LPBITMAPINFOHEADER info;
        info = (LPBITMAPINFOHEADER)m_pBitstore;
        LPBITMAPINFO info2;
        info2 = (LPBITMAPINFO)m_pBitstore;      
        hBitmap2 = ::CreateDIBitmap( dc.m_hDC, info, CBM_INIT, ptr, info2, DIB_RGB_COLORS );
        ::SelectObject( hdcMem, hBitmap );//make this local bitmap the display surface
        ::SetMapMode( hdcMem, MM_TEXT );
        ::SelectObject( hdcMem2, hBitmap2 );//make this local bitmap the display surface            
        ::SetMapMode( hdcMem2, MM_TEXT );
        CPoint cp, pc;
        cp.x = rectSave.left;
        cp.y = rectSave.top;
        pc = DibPoint( cp );                
        ::BitBlt( hdcMem2, 0, 0,(int)m_Dibhead.biWidth,(int)m_Dibhead.biHeight, hdcMem, pc.x, pc.y, SRCCOPY );          
        ::GetDIBits( dc.m_hDC, hBitmap2, 0, (int)m_Dibhead.biHeight, ptr, info2, DIB_RGB_COLORS );
        if( m_bIscut )    //If Cut does this.
        {  
            LPBITMAPINFOHEADER lpbmi;  // pointer to a Win 3.0-style DIB
            lpbmi = (LPBITMAPINFOHEADER)lpDIBHdr;
            ::PatBlt( hdcMem, pc.x, pc.y, (int)m_Dibhead.biWidth, (int)m_Dibhead.biHeight, WHITENESS );//clear contend of local bitmap
            ::GetDIBits( dc.m_hDC, hBitmap, 0, (int)lpbmi->biHeight, lpDIBBits,(LPBITMAPINFO)lpDIBHdr, DIB_RGB_COLORS );
            pDoc->SetModifiedFlag(TRUE);
            m_bIscut = FALSE;
            ::InvalidateRect( this->m_hWnd, NULL, FALSE );
        }                               
/*      
        #ifdef _DEBUG
            afxDump << "Dumping m_hBitstore2: =  "
                    <<  m_hBitstore2
                    << "\n";
        #endif
*/          
        ::GlobalUnlock((HGLOBAL) m_hBitstore2);
        
        EmptyClipboard();
        SetClipboardData (CF_DIB, CopyHandle((HANDLE)m_hBitstore2));
        CloseClipboard();
        
        
        ::GlobalFree((HGLOBAL) m_hBitstore2);
        ::DeleteDC( hdcMem );
        ::DeleteObject( hBitmap );      
        ::GlobalUnlock((HGLOBAL) hDIB);
        ::DeleteDC( hdcMem2 );
        ::DeleteObject( hBitmap2 );     
        EndWaitCursor();
    }
}

With #define huge /* nothing */ to cancel the huge key word.

Since the initial query above, I have deduced that the problem lies before the point where the program hangs up, without an indication of the cause. After scouring the literature, I have yet to find a solution. The program is based upon the old Biblook example. The routines from back then, for copying, were duplicated from elsewhere in the code for manipulating regions in the client area, which still work just fine. The code for copy and paste, as is, worked back then too. If I change the ptr variable from the orginal to what is indicated below, then the copy and paste procedure works properly the first time accessed, but no more. If I cancel out the ::GetDIBits(dc.m_hDC, hBitmap2, 0, (int)m_Dibhead.biHeight, ptr, info2, DIB_RGB_COLORS); procedure then the program doesn't hang up but it will not copy the selected client area.

Below is the current state of the function with comments, as I tried to ascertain the function. At this point, I am assuming the an entirly different process is currently employed, but I can not find an example of it. Or there's still an error in my code that I can't pin down.

 void CDibView::OnEditCopy()
{   
    if( m_id == ID_PALETTEEDIT ) //Do this if copying text
    {
        m_edit.Copy();
        return;
    }
    BeginWaitCursor();
    CDibDoc* pDoc = GetDocument();
    // Clean clipboard of contents, and copy the DIB.
    //COleDataSource* pData = new COleDataSource; //For test
    if (::OpenClipboard(m_hWnd))
    {
        //* Setup Dib header
        //With #include <wingdi.h>
        BITMAPINFOHEADER    m_Dibhead;//Header
        BITMAPV5HEADER      m_V5Dibhead;//Tested but ::CreateDIBitmap wouldn't take
        HANDLE  m_hBitstore2;
            //*Fill header fields
        m_Dibhead.biSize = sizeof(BITMAPINFOHEADER);
        m_V5Dibhead.bV5Size = sizeof(BITMAPV5HEADER);

        CSize now = rectSave.Size(); //Size of select rectangle

        m_Dibhead.biWidth = now.cx;
        m_V5Dibhead.bV5Width = now.cx;
        m_Dibhead.biHeight = now.cy;
        m_V5Dibhead.bV5Height = now.cy;
        m_Dibhead.biPlanes = 1;
        m_V5Dibhead.bV5Planes = 1;
        m_Dibhead.biBitCount = 24;
        m_V5Dibhead.bV5BitCount = 24;
        m_Dibhead.biCompression = 0;
        m_V5Dibhead.bV5Compression = 0;
        m_Dibhead.biSizeImage = ((DWORD)m_Dibhead.biWidth * m_Dibhead.biBitCount / 8) * (DWORD)m_Dibhead.biHeight;
        m_V5Dibhead.bV5SizeImage = m_Dibhead.biSizeImage;
        m_Dibhead.biXPelsPerMeter = 0;
        m_V5Dibhead.bV5XPelsPerMeter = 0;
        m_Dibhead.biYPelsPerMeter = 0;
        m_V5Dibhead.bV5YPelsPerMeter = 0;
        m_Dibhead.biClrUsed = 0;
        m_V5Dibhead.bV5ClrUsed = 0;
        m_Dibhead.biClrImportant = 0;
        m_V5Dibhead.bV5ClrImportant = 0;
        //* End header setup
            //Size of Dib file in DWORD
        DWORD count = (((DWORD)m_Dibhead.biWidth * m_Dibhead.biBitCount / 8) * (DWORD)m_Dibhead.biHeight) + (unsigned long)sizeof(BITMAPINFOHEADER);
        //DWORD count = (((DWORD)m_Dibhead.biWidth * m_Dibhead.biBitCount / 8) * (DWORD)m_Dibhead.biHeight) + (unsigned long)sizeof(BITMAPINFOHEADER);
        HBITMAP hBitmap2;   //Bitmap handle for Clipboard
        char* m_pBitstore;  //Character pointer to Dib data
        
        CClientDC dc(this);

        //Below was used with early memory architecture to find the smallest usable memory for needed dib
            //* Find smallest memory storage that CreateDIBitmap will take
        int next = 0;
        //for (next = 0; next < 2000; next++)
        //{     // Establish a memory size to test
        //  m_pBitstore = (char*)GlobalAllocPtr(GPTR, count + (unsigned long)next);
        //      //Test it
        //  if (!(hBitmap2 = ::CreateDIBitmap(dc.m_hDC, (LPBITMAPINFOHEADER)m_pBitstore, CBM_INIT, (LPSTR)m_pBitstore + (unsigned long)sizeof(BITMAPINFOHEADER), (LPBITMAPINFO)m_pBitstore, DIB_RGB_COLORS)))
        //  {
        //      GlobalFreePtr(m_pBitstore);    //There is a problem in fabricating the packed DIB used by clipboard here
        //  }                                   //which dosn't seem to show until an attempt to construct a devise dependant
        //  else                                //bitmap used in the data transfer.  This loop searches for a size of memory
        //      break;                           //allocation which will function.
        //}
        //GlobalFreePtr(m_pBitstore); // Free up this memory
        //::DeleteObject(hBitmap2); //Delete the test dib
        
            // Establish memory for dib using the above tested size
        // was m_hBitstore2 = (HANDLE) ::GlobalAlloc( GPTR, count + (unsigned long)next );
        m_hBitstore2 = (HANDLE) ::GlobalAlloc(GMEM_FIXED, count + (unsigned long)next);
        LPBITMAPINFOHEADER m_pBitstore2; // Establish pointer to this memory and thus the header
        m_pBitstore2 = (LPBITMAPINFOHEADER) ::GlobalLock((HGLOBAL)m_hBitstore2);
        *m_pBitstore2 = m_Dibhead; // Copy the header values to memory
            // Unlock the memory for the dib and then relock to retrieve a char pointer
        ::GlobalUnlock((HGLOBAL)m_hBitstore2);
        m_pBitstore = (char*) ::GlobalLock((HGLOBAL)m_hBitstore2);
            //* Get a handle to the whole client window
        HDIB hDIB = pDoc->GetHDIB();
        BYTE* lpDIBHdr;            // Pointer to BITMAPINFOHEADER
        BYTE* lpDIBBits;           // Pointer to DIB bits
        lpDIBHdr = (BYTE*) ::GlobalLock((HGLOBAL)hDIB);
        lpDIBBits = (BYTE*)::FindDIBBits((LPSTR)lpDIBHdr);
            // Make two device contexts
        HDC hdcMem; // One for cutting out the selection box content
        HDC hdcMem2; // One for placing into Clipboard
        hdcMem = ::CreateCompatibleDC(dc.m_hDC);//create a memory device context (display surface)
        hdcMem2 = ::CreateCompatibleDC(dc.m_hDC);
            // Create new bitmap using the hDib content
        HBITMAP hBitmap;
        hBitmap = ::CreateDIBitmap(dc.m_hDC, (LPBITMAPINFOHEADER)lpDIBHdr, CBM_INIT, lpDIBBits, (LPBITMAPINFO)lpDIBHdr, DIB_RGB_COLORS);
            // Make pointer to the dib data
        char* ptr;
        //ptr = m_pBitstore + (unsigned long)sizeof(BITMAPINFOHEADER);
        ptr = m_pBitstore + (lpDIBBits - lpDIBHdr);
            // Make poiner to header in the established memory
        LPBITMAPINFOHEADER info;
        info = (LPBITMAPINFOHEADER)m_pBitstore;
            // Make  pointer to a BITMAPINFOHEADER struct plus a RGBQUAD struct
        LPBITMAPINFO info2;
        info2 = (LPBITMAPINFO)m_pBitstore;
            // Create the new (BUT EMPTY) dib using these values
        hBitmap2 = ::CreateDIBitmap(dc.m_hDC, info, CBM_INIT, ptr, info2, DIB_RGB_COLORS);
        ::SelectObject(hdcMem, hBitmap);//make this local bitmap for cutting out selection box
        ::SetMapMode(hdcMem, MM_TEXT);
        ::SelectObject(hdcMem2, hBitmap2);//make this local bitmap for the Clipboard            
        ::SetMapMode(hdcMem2, MM_TEXT);
            // Retrieve location for top-left corner of selection box
        CPoint cp, pc;
        cp.x = rectSave.left;
        cp.y = rectSave.top;
        pc = DibPoint(cp); // Find location of data in client window
            // Retrieve color data from selection box for Clipboard and put it in new dib
        BOOL bb = ::BitBlt(hdcMem2, 0, 0, (int)m_Dibhead.biWidth, (int)m_Dibhead.biHeight, hdcMem, pc.x, pc.y, SRCCOPY);
        if (!bb)
        {
            ::MessageBox(m_hWnd, "BitBlt has failed", "Failed", MB_OK);
        }
            // Get image data of bitmap for Clipboard
        int gb = ::GetDIBits(dc.m_hDC, hBitmap2, 0, (int)m_Dibhead.biHeight, ptr, info2, DIB_RGB_COLORS);
        if (gb == ERROR_INVALID_PARAMETER || gb == 0)
        {
            ::MessageBox(m_hWnd, "GetDIBits has failed", "Failed", MB_OK);
        }
        if (m_bIscut)    //If Cut, does this.
        {
            LPBITMAPINFOHEADER lpbmi;  // pointer to a Win 3.0-style DIB
            lpbmi = (LPBITMAPINFOHEADER)lpDIBHdr;
                // Make selection box image white
            ::PatBlt(hdcMem, pc.x, pc.y, (int)m_Dibhead.biWidth, (int)m_Dibhead.biHeight, WHITENESS);//clear contend of local bitmap
                // Make white dib for selection box
            ::GetDIBits(dc.m_hDC, hBitmap, 0, (int)lpbmi->biHeight, lpDIBBits, (LPBITMAPINFO)lpDIBHdr, DIB_RGB_COLORS);
            pDoc->SetModifiedFlag(TRUE);
            m_bIscut = FALSE;
            ::InvalidateRect(this->m_hWnd, NULL, FALSE);
        }
        ::GlobalUnlock((HGLOBAL)m_hBitstore2);

        /*pData->CacheGlobalData(CF_DIB, m_hBitstore2);//For test
        pData->SetClipboard();*/

        ::EmptyClipboard();
        ::SetClipboardData(CF_DIB, CopyHandle((HANDLE)m_hBitstore2));
        ::CloseClipboard();

        ::GlobalFree((HGLOBAL)m_hBitstore2);
        ::DeleteDC(dc);
        ::DeleteDC(hdcMem);
        ::DeleteObject(hBitmap);
        ::GlobalUnlock((HGLOBAL)hDIB);
        ::DeleteDC(hdcMem2);
        ::DeleteObject(hBitmap2);
        EndWaitCursor();
    }
}

The following is what was in the Call Stack Window after the crash, followed by the content of the crash message box;

ntdll.dll!_RtlReportCriticalFailure@12()
ntdll.dll!_RtlpReportHeapFailure@4()
ntdll.dll!_RtlpHpHeapHandleError@12()
ntdll.dll!_RtlpLogHeapFailure@24()
ntdll.dll!_RtlpAnalyzeHeapFailure@12()
ntdll.dll!@RtlpAllocateHeap@24()
ntdll.dll!_RtlpAllocateHeapInternal@16()
ntdll.dll!RtlAllocateHeap()
[External Code]
Diblook.exe!CDibView::OnEditCopy() Line 671
    at C:\VS Projects\Diblook\DIBVIEW.CPP(671)
[External Code]
Diblook.exe!WinMain(HINSTANCE__ * hInstance=0x007b0000, HINSTANCE__ * hPrevInstance=0x00000000, char * lpCmdLine=0x01228053, int nCmdShow=10) Line 26
    at D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\appmodul.cpp(26)
[External Code]

A breakpoint instruction (__debugbreak() statement or a similar call) was executed in Diblook.exe.

I finally tried doubling the amount calculated for the size of the dib file.

DWORD count = (((DWORD)m_Dibhead.biWidth * m_Dibhead.biBitCount / 8) * (DWORD)m_Dibhead.biHeight) + (unsigned long)sizeof(BITMAPINFOHEADER);
count = count * 2;

With this, the procedure was been working. Anybody know the proper calculation for this value? Thank you.

  • Can you provide a minimal reproducible example? – blackbrandt Feb 21 '23 at 13:36
  • Did you at any point attach the program to a debugger, went through the effort to trigger the break point and then take a stack backtrace, to identify the exact call graph and the locations (down to the line) of the calls that lead to that break? You literally have a `debugbreak` doing all the difficult leg work for you already. *How about you dump the debug break stack backtrace, and post it here, too?* – datenwolf Feb 26 '23 at 14:54

0 Answers0