My understanding is that in general when you add a handle to the clipboard, the clipboard then owns the handle and you are not responsible for deleting it, and you should not delete it. This is what it says here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms649051%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
If SetClipboardData succeeds, the system owns the object identified by
the hMem parameter. The application may not write to or free the data
once ownership has been transferred to the system, but it can lock and
read from the data until the CloseClipboard function is called. (The
memory must be unlocked before the Clipboard is closed.) If the hMem
parameter identifies a memory object, the object must have been
allocated using the function with the GMEM_MOVEABLE flag.
And indeed I see lots of examples where people call GlobalAlloc(), put some text in the global handle, call SetClipboardData(), and then don't free the global handle, because the clipboard owns it.
But in the case of HBITMAP data, added via SetClipboardData(CF_BITMAP, hBitmap)
, I see lots of examples like these:
https://stackoverflow.com/a/7292773/384670
https://stackoverflow.com/a/28248531/384670
In these cases, the code does delete the HBITMAP after adding it to the clipboard.
Is there a difference between an HBITMAP handle and a GlobalAlloc() handle with respect to the clipboard? Is there something special about the CF_BITMAP that's an exception to the rule, and the clipboard copies the handle instead of owns it? Can you point me to official (MSDN) documentation that explains the difference?
Edit:
Here is one more example that involves both types of handle in one article: http://www.codeproject.com/Articles/42/All-you-ever-wanted-to-know-about-the-Clipboard
Notice that in the case of the bitmap the author specifically says:
//copy has been made on clipboard so we can delete
Here are the results of the experiment encouraged by Jonathan Potter. The handle in question is in the variable HBITMAP hbmScreen
. The result of this experiment is that I can access hbmScreen in all of the checkpoints (1) through (4), and can save the correct image at the end. So this tells me that the handle is still mine to use. As I say, I don't think this definitively shows that the handle is not also owned by the clipboard. But since calling DeleteObject( hbmScreen )
after all of this also appears to work without problem, I'm going to continue doing that.
if ( OpenClipboard( NULL ) && EmptyClipboard() )
{
BITMAPINFOHEADER bi = { 0 };
bi.biSize = sizeof( BITMAPINFOHEADER );
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biWidth = rcClient.right - rcClient.left;
bi.biHeight = rcClient.bottom - rcClient.top;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0; // 3 * ScreenX * ScreenY;
BYTE *lpbitmap = (BYTE *)malloc( bi.biWidth * bi.biHeight * 4 );
// (1) call to make sure we can access hbmScreen here
memset( lpbitmap, 0, bi.biWidth * bi.biHeight * 4 );
GetDIBits( hdcScreen, hbmScreen, 0, (UINT)bi.biHeight, lpbitmap, (BITMAPINFO *)&bi, DIB_RGB_COLORS );
HANDLE hResult = SetClipboardData( CF_BITMAP, hbmScreen );
CloseClipboard();
// (2) call to check if we can access hbmScreen here
memset( lpbitmap, 0, bi.biWidth * bi.biHeight * 4 );
GetDIBits( hdcScreen, hbmScreen, 0, (UINT)bi.biHeight, lpbitmap, (BITMAPINFO *)&bi, DIB_RGB_COLORS );
OpenClipboard( NULL );
EmptyClipboard();
CloseClipboard();
// (3) call to check if we can access hbmScreen here
memset( lpbitmap, 0, bi.biWidth * bi.biHeight * 4 );
GetDIBits( hdcMemDC, hbmScreen, 0, (UINT)bi.biHeight, lpbitmap, (BITMAPINFO *)&bi, DIB_RGB_COLORS );
// (4) actually use the data from hbmScreen
std::vector<unsigned char> image;
int n = bi.biWidth * bi.biHeight * 4;
image.resize( n );
int i = 0;
for ( int y = bi.biHeight - 1 ; y >= 0 ; y-- )
{
for ( int x = 0 ; x < bi.biWidth ; x++ )
{
int base = ( y * bi.biWidth + x ) * 4;
image[ i++ ] = lpbitmap[ base + 2 ]; // r
image[ i++ ] = lpbitmap[ base + 1 ]; // g
image[ i++ ] = lpbitmap[ base ]; // b
image[ i++ ] = lpbitmap[ base + 3 ]; // a
}
}
free( lpbitmap );
unsigned error = lodepng::encode( "C:/a.png", image, bi.biWidth, bi.biHeight );
}