SetClipboardData fails, but GetLastError does not report any error
Again not MFC.
I have window drawing some stuff using the OPENFGL API.
I want to copy it's content to the clipboard in a user
defined resolution (say 300 DPI).
The idea is to render to a memory context.So far all works
fine.
Sometimes however sending the data to the clipboard fails.
MSDN states for SetClipboardData:
| If the function fails, the return value is NULL.
| To get extended error information, call GetLastError.
For large bitmaps SetClipboardData returns NULL, however
GetLastError returns 0, which means NO ERROR. I wonder
if this is correct.
I get around this by reducing the resolution, trying again
and informing the user about the reduction. But I have no idea
why SetClipboardData fails without GetLastError reporting
some kind of "out of memory" error.
Code goes here:
void CGlView::OnEditCopy()
{
CRect mRect;
BeginWaitCursor();
GetClientRect(&mRect);
SetRedraw(FALSE);
int nReduceResCount = 0;
while (!EditCopy(mRect, nReduceResCount++))
{
// retry again with reduced resolution
if (nReduceResCount >= 10)
break;
}
SetRedraw(TRUE);
RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_NOERASE);
EndWaitCursor();
}
BOOL CGlView::EditCopy(CRect mRect, int nReduceResCount)
{
CWinApp *pApp = AfxGetApp();
RECT rect;
HDC hDC, hMemDC, hTmpDC;
HGLRC hRC, hMemRC;
BITMAPINFO bitmapInfo;
HBITMAP hDib;
LPVOID pBitmapBits;
double fac = 1.;
int nXRes, nYRes;
BOOL bSuccess = FALSE;
if (pApp->IsKindOf(RUNTIME_CLASS(CGlWinApp)))
fac = ((CGlWinApp *)pApp)->m_nCopyResolution/72.; // 72 DPI
(screen) --> <user selected> DPI (clipboard)
for (int k = nReduceResCount; k > 0; k--)
fac /= 2.;
rect = mRect;
ASSERT(rect.left == 0);
ASSERT(rect.top == 0);
rect.right = int(rect.right*fac);
rect.bottom = int(rect.bottom*fac);
if (mRect.Width() == 0 || mRect.Height() == 0)
{
GetClientRect(&mRect);
}
nXRes = rect.right;
nYRes = rect.bottom;
ScaleFont(fac);
//nXRes = (nXRes + (sizeof(DWORD)-1)) & ~(sizeof(DWORD)-1); //
aligning width to 4 bytes (sizeof(DWORD)) avoids
nXRes = nXRes & ~(sizeof(DWORD)-1); // aligning width to 4
bytes (sizeof(DWORD)) avoids
// pixel garbage at the upper line
// First of all, initialize the bitmap header information...
memset(&bitmapInfo, 0, sizeof(BITMAPINFO));
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = nXRes;
bitmapInfo.bmiHeader.biHeight = nYRes;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 24;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = bitmapInfo.bmiHeader.biWidth *
bitmapInfo.bmiHeader.biHeight * 3;
bitmapInfo.bmiHeader.biXPelsPerMeter = int(72.*fac/.0254); // 72*fac
DPI
bitmapInfo.bmiHeader.biYPelsPerMeter = int(72.*fac/.0254); // 72*fac
DPI
// create DIB
hTmpDC = ::GetDC(m_hWnd);
hDib = CreateDIBSection(hTmpDC, &bitmapInfo, DIB_RGB_COLORS,
&pBitmapBits, NULL, (DWORD)0);
::ReleaseDC(m_hWnd, hTmpDC);
// create memory device context
if ((hMemDC = CreateCompatibleDC(m_pDC == NULL ? NULL : m_pDC-
GetSafeHdc())) == NULL)
{
DeleteObject(hDib);
return FALSE;
}
HGDIOBJ hOldDib = SelectObject(hMemDC, hDib);
// setup pixel format
if (!SetMemDcPixelFormat(hMemDC) && !SetMemDcPixelFormat(hMemDC,
TRUE))
{
if (hOldDib != NULL)
SelectObject(hMemDC, hOldDib);
DeleteObject(hDib);
DeleteDC(hMemDC);
return FALSE;
}
// create memory rendering context
if ((hMemRC = wglCreateContext(hMemDC)) == NULL)
{
if (hOldDib != NULL)
SelectObject(hMemDC, hOldDib);
DeleteObject(hDib);
DeleteDC(hMemDC);
return FALSE;
}
// Store current rendering and device contexts
GetCurrent(hDC, hRC);
// Make this hMemRC the current OpenGL rendering context.
SetCurrent(hMemDC, hMemRC);
SetOpenGLProperties();
glViewport(0, 0, nXRes, nYRes);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_DEPTH_TEST);
// must be created once for hMemDC
CreateFontBitmaps();
CDC *pDummyDC = GetDC();
pDummyDC->m_bPrinting = TRUE; // this does the trick in OnDraw: it
prevents changing rendering context and swapping buffers
OnDraw(pDummyDC);
ReleaseDC(pDummyDC);
glFinish(); // Finish all OpenGL commands
// the rendering context will be no longer needed
SetCurrent(NULL, NULL);
wglDeleteContext(hMemRC);
// Restore last rendering and device contexts
if (hDC != NULL && hRC != NULL)
SetCurrent(hDC, hRC);
// Restore the Views original font size
UnScaleFont();
CreateFontBitmaps();
if (OpenClipboard())
{
HGLOBAL hClipboardCopy = ::GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE
| GMEM_DDESHARE, sizeof(BITMAPINFOHEADER) +
bitmapInfo.bmiHeader.biSizeImage);
if (hClipboardCopy != NULL)
{
LPVOID lpClipboardCopy, lpBitmapBitsOffset;
lpClipboardCopy = ::GlobalLock((HGLOBAL) hClipboardCopy);
lpBitmapBitsOffset = (LPVOID)((BYTE*)lpClipboardCopy +
sizeof(BITMAPINFOHEADER));
memcpy(lpClipboardCopy, &bitmapInfo.bmiHeader,
sizeof(BITMAPINFOHEADER));
memcpy(lpBitmapBitsOffset, pBitmapBits,
bitmapInfo.bmiHeader.biSizeImage);
::GlobalUnlock(hClipboardCopy);
EmptyClipboard();
if (SetClipboardData(CF_DIB, hClipboardCopy) != NULL)
{
bSuccess = TRUE;
if (nReduceResCount > 0)
{
// inform the user about lower resolution here
}
}
else
{
GlobalFree(hClipboardCopy);
}
CloseClipboard();
}
}
if (hOldDib != NULL)
SelectObject(hMemDC, hOldDib);
// Delete our DIB and device context
DeleteObject(hDib);
DeleteDC(hMemDC);
return bSuccess;
}