Search code examples
c++bitmappnggdi+screenshot

Windows 7 and ScreenShot.cpp GDI+ PNG problemo


was using XP without issue for a long time. switched to 7 and trying to capture screenshots with my previously functioning code no longer works. simple concept and relatively generic code...just find the window that i call and save it as a .png. any ideas what might make this bad boy run again? can't debug with my current setup, but it makes it all the way and spits out the error message after bmp->save(...) ...couldn't save image file. edit: also a file does get created/saved, but it is blank and not written to. perhaps the bitmap encoding or GDI is screwed up?

bool CScreenShot::Snap(CString wintitle, CString file, CString& ermsg)
{
    ermsg = ""; // no error message

    // create screen shot bitmap
    EnumWinProcStruct prm = {0, (LPSTR)(LPCTSTR)wintitle, 0};

    // Find the descriptor of the window with the caption wintitle
    EnumDesktopWindows(0, EnumWindowsProc, (LPARAM)&prm);
    if(!prm.hwnd)
    {
        ermsg.Format("couldn't find window \"%s\"", wintitle);
        return false;
    }

    // Make the window the topmost window
    SetWindowPos(prm.hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
    Sleep(300);

    // Get device context for the top-level window and client rect
    HDC hDC = GetDC(prm.hwnd);
    RECT rc;
    GetClientRect(prm.hwnd, &rc);

    HDC memDC = CreateCompatibleDC(hDC);

    // Set the size and color depth for the screen shot image
    BITMAPINFO bmpInfo;
    memset(&bmpInfo, 0, sizeof(bmpInfo));
    bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);
    bmpInfo.bmiHeader.biWidth = rc.right - rc.left;
    bmpInfo.bmiHeader.biHeight = rc.bottom - rc.top;
    bmpInfo.bmiHeader.biPlanes = 1;
    bmpInfo.bmiHeader.biBitCount = 24;
    bmpInfo.bmiHeader.biCompression = BI_RGB;
    bmpInfo.bmiHeader.biSizeImage = bmpInfo.bmiHeader.biWidth * bmpInfo.bmiHeader.biHeight * 3;

    // Create memory buffer and perform a bit-block transfer of the color data from the window to the memory
    LPVOID  addr;
    HBITMAP memBM   = CreateDIBSection(memDC, &bmpInfo, DIB_RGB_COLORS, &addr, 0, 0);

    HGDIOBJ stdBM   = SelectObject(memDC, memBM);
    BOOL    OK      = BitBlt(memDC, 0, 0, bmpInfo.bmiHeader.biWidth, bmpInfo.bmiHeader.biHeight, hDC, 0, 0, SRCCOPY);
    ReleaseDC(prm.hwnd, hDC);

    SetWindowPos(prm.hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);

    // Initialize GDI+.
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR           gdiplusToken;
    if(GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) != Ok)
    {
        ermsg.Format("couldn't start GDI+");
        return false;
    }

    // Create a Bitmap object for work with images defined by pixel data from the GDI HBitmap and the GDI HPalette.
    Bitmap* bmp = ::new Bitmap(memBM, DIB_RGB_COLORS);
    SelectObject(memDC, stdBM);
    DeleteObject(memBM);
    DeleteDC(memDC);

    // Find the encoder for "image/png" mime type
    CLSID encoderClsid;
    EncoderParameters encoderParameters;

    GetEncoderClsid(L"image/png", &encoderClsid);

    encoderParameters.Count = 0;

    // Convert file name to Unicode (wide-char) string.
    WCHAR   fn[_MAX_PATH];
    MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, file, file.GetLength() + 1, fn, _MAX_PATH);
    // Save the screen shot into the specified file using image encoder with the mime style "image/png"
    if(bmp->Save(fn, &encoderClsid, &encoderParameters) != Ok)
    {
        ermsg.Format("couldn't save image file \"%s\"", file);
        return false;
    }

    ::delete bmp;
    GdiplusShutdown(gdiplusToken);

    return true;
}

Solution

  • win7 won't accept encoderParameters.Count == 0 for some reason. Set that == 1 and you should be all set.

    you probably could also just remove that parameter from Save() (overloaded)