Jolie Rouge


all ur parentheses Я belong to me


Qt C++ Screen Capture using BitBlt

So I got to thinking I wanted to be able to take abitrary screen captures based on some startx/starty -> endx/endy input. I found various sites showing how to do this, but most of them were pretty confusing. I thought I would post the final working code here.

void RSystem::snap(int startx, int starty,int endx,int endy) {
    HDC hdcScreen,hdcMemDC = NULL;
    HBITMAP hbmScreen = NULL;
    BITMAP bmpScreen;
    HWND hDesktopWnd = NULL;

    hDesktopWnd = GetDesktopWindow();
    hdcScreen = GetDC(hDesktopWnd);
    hdcMemDC = CreateCompatibleDC(hdcScreen);
    if (!hdcScreen) {
        qDebug() << "No hdcScreen!";
        return;
    }
    hbmScreen = CreateCompatibleBitmap(hdcScreen,endx - startx,endy - starty);
    if (!hbmScreen) {
        qDebug() << "No hbmScreen";
        return;
    }
    SelectObject(hdcMemDC,hbmScreen);
    if (!BitBlt(hdcMemDC, 0, 0, endx - startx,endy - starty, hdcScreen, startx, starty, SRCCOPY)) {
        qDebug() << "BitBlt failed!";
        return;
    }

    GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);

    BITMAPFILEHEADER bmfHeader;
    BITMAPINFOHEADER bi;

    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = bmpScreen.bmWidth;
    bi.biHeight = bmpScreen.bmHeight;
    bi.biPlanes = 1;
    bi.biBitCount = 32;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrUsed = 0;
    bi.biClrImportant = 0;

    DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;

    HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize);
    char *lpbitmap = (char *)GlobalLock(hDIB);

    GetDIBits(hdcMemDC, hbmScreen, 0,
            (UINT)bmpScreen.bmHeight,
            lpbitmap,
            (BITMAPINFO *)&bi, DIB_RGB_COLORS);
    HANDLE hFile = CreateFile(L"captureqwsx.bmp",
            GENERIC_WRITE,
            0,
            NULL,
            CREATE_ALWAYS,
            FILE_ATTRIBUTE_NORMAL, NULL);

    DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
    bmfHeader.bfSize = dwSizeofDIB;
    bmfHeader.bfType = 0x4D42; //BM

    DWORD dwBytesWritten = 0;
    WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);

    GlobalUnlock(hDIB);
    GlobalFree(hDIB);
    CloseHandle(hFile);
    DeleteObject(hbmScreen);
    ReleaseDC(NULL, hdcMemDC);
    ReleaseDC(hDesktopWnd,hdcScreen);
}
  • Chris

    I’m not quite sure what this example has to do with QT. The only thing of QT you seems to use is “qDebug”. Everything else is just plain C++ call to API.

    • Anonymous

      Thanks for you comment 🙂 You are correct, there is no QT relevant code, at the time of writing, and I think even today, there is no real QT-ish way to snap the screen. I spent hours searching for one before I had to figure it out more or less for myself. If that saves someone else some time, I think I can be forgiven the false advertising.

      I was at least using QT Creator when I wrote it.