How to capture the screen of a window that contains transparency? - c++

Trying to find a way that also works when the window is covered by another.
I tried PrintWindow but it doesn't store the transparency information in the hdc.
I also tried BitBlt, but the image saved doesn't contains the window transparency, and I'm not sure how to capture it when its covered by another windows.
int CaptureAnImage(HWND hWnd)
{
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
DWORD dwBytesWritten = 0;
DWORD dwSizeofDIB = 0;
HANDLE hFile = NULL;
char* lpbitmap = NULL;
HANDLE hDIB = NULL;
DWORD dwBmpSize = 0;
// Retrieve the handle to a display device context for the client
// area of the window.
hdcScreen = GetDC(hWnd); // GetDC(NULL);
hdcWindow = GetDC(hWnd);
// Create a compatible DC, which is used in a BitBlt from the window DC.
hdcMemDC = CreateCompatibleDC(hdcWindow);
if (!hdcMemDC)
{
MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK);
goto done;
}
// Get the client area for size calculation.
RECT rcClient;
GetClientRect(hWnd, &rcClient);
// This is the best stretch mode.
SetStretchBltMode(hdcWindow, HALFTONE);
// The source DC is the entire screen, and the destination DC is the current window (HWND).
if (!StretchBlt(hdcWindow,
0, 0,
rcClient.right, rcClient.bottom,
hdcScreen,
0, 0,
rcClient.right, //GetSystemMetrics(SM_CXSCREEN),
rcClient.bottom, //GetSystemMetrics(SM_CYSCREEN),
SRCCOPY | CAPTUREBLT))
{
MessageBox(hWnd, L"StretchBlt has failed", L"Failed", MB_OK);
goto done;
}
// Create a compatible bitmap from the Window DC.
hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
if (!hbmScreen)
{
MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK);
goto done;
}
// Select the compatible bitmap into the compatible memory DC.
SelectObject(hdcMemDC, hbmScreen);
// Bit block transfer into our compatible memory DC.
if (!BitBlt(hdcMemDC,
0, 0,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
hdcWindow,
0, 0,
SRCCOPY | CAPTUREBLT))
{
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
goto done;
}
// Get the BITMAP from the HBITMAP.
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;
dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
hDIB = GlobalAlloc(GHND, dwBmpSize);
lpbitmap = (char*)GlobalLock(hDIB);
// Gets the "bits" from the bitmap, and copies them into a buffer
// that's pointed to by lpbitmap.
GetDIBits(hdcWindow, hbmScreen, 0,
(UINT)bmpScreen.bmHeight,
lpbitmap,
(BITMAPINFO*)&bi, DIB_RGB_COLORS);
// A file is created, this is where we will save the screen capture.
hFile = CreateFile(L"captureqwsx.png",
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
// Add the size of the headers to the size of the bitmap to get the total file size.
dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
// Size of the file.
bmfHeader.bfSize = dwSizeofDIB;
// bfType must always be BM for Bitmaps.
bmfHeader.bfType = 0x4D42; // BM.
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
// Unlock and Free the DIB from the heap.
GlobalUnlock(hDIB);
GlobalFree(hDIB);
// Close the handle for the file that was created.
CloseHandle(hFile);
// Clean up.
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL, hdcScreen);
ReleaseDC(hWnd, hdcWindow);
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
HWND hWnd = FindWindow(NULL, L"Untitled - Notepad");
DWORD exstyle = GetWindowLong(hWnd, GWL_EXSTYLE);
SetWindowLong(hWnd, GWL_EXSTYLE, exstyle | WS_EX_LAYERED);
SetLayeredWindowAttributes(hWnd, 0, 100, LWA_ALPHA);
CaptureAnImage(hWnd);
}

Related

C++ WinAPI screenshot of a window in the background

I want to take screenshots of a window which has no focus. My function works for some windows but not for all and I don't know why. I tried it to capture the window of Bluestacks App Player and it works perfectly. But for Nox App Player and some other games it doesn't work at all. I just get a black image with the size of the window.
Here is the code so far:
void screenshot_window(HWND handle) {
RECT client_rect = { 0 };
GetClientRect(handle, &client_rect);
int width = client_rect.right - client_rect.left;
int height = client_rect.bottom - client_rect.top;
HDC hdcScreen = GetDC(handle);
HDC hdc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen, width, height);
SelectObject(hdc, hbmp);
BitBlt(hdc, 0, 0, width, height, hdcScreen, 0, 0, SRCCOPY);
BITMAPINFO bmp_info = { 0 };
bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);
bmp_info.bmiHeader.biWidth = width;
bmp_info.bmiHeader.biHeight = height;
bmp_info.bmiHeader.biPlanes = 1;
bmp_info.bmiHeader.biBitCount = 24;
bmp_info.bmiHeader.biCompression = BI_RGB;
int bmp_padding = (width * 3) % 4;
if (bmp_padding != 0) bmp_padding = 4 - bmp_padding;
BYTE *bmp_pixels = new BYTE[(width * 3 + bmp_padding) * height];;
GetDIBits(hdc, hbmp, 0, height, bmp_pixels, &bmp_info, DIB_RGB_COLORS);
BITMAPFILEHEADER bmfHeader;
HANDLE bmp_file_handle = CreateFile("TestFile.bmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// Add the size of the headers to the size of the bitmap to get the total file size
DWORD dwSizeofDIB = (width * 3 + bmp_padding) * height + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
//Size of the file
bmfHeader.bfSize = dwSizeofDIB;
//bfType must always be BM for Bitmaps
bmfHeader.bfType = 0x4D42; //BM
DWORD dwBytesWritten = 0;
WriteFile(bmp_file_handle, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(bmp_file_handle, (LPSTR)&bmp_info.bmiHeader, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(bmp_file_handle, (LPSTR)bmp_pixels, (width * 3 + bmp_padding) * height, &dwBytesWritten, NULL);
//Close the handle for the file that was created
CloseHandle(bmp_file_handle);
DeleteDC(hdc);
DeleteObject(hbmp);
ReleaseDC(NULL, hdcScreen);
delete[] bmp_pixels;
}
This can happen with many applications where the target window is simply a container and is not responsible for paint messages. Standard win32 applications like notepad don't behave as such. But you may run in to this problem with many browsers for example.
You can always take screenshot of desktop window. You can get the screen coordinate of target window, then bitblt that section of target window. Make the following changes to your code:
//GetClientRect(handle, &client_rect);
GetWindowRect(handle, &client_rect);
//HDC hdcScreen = GetDC(handle);
HDC hdcScreen = GetDC(HWND_DESKTOP);
//BitBlt(hdc, 0, 0, width, height, hdcScreen, 0, 0, SRCCOPY);
BitBlt(hdc, 0, 0, width, height, hdcScreen, client_rect.left, client_rect.top, SRCCOPY);
//ReleaseDC(NULL, hdcScreen);
ReleaseDC(HWND_DESKTOP, hdcScreen);
The target window must be top-most visible window on the screen before taking screen shot. For example you can call screenshot_window in this order:
HWND hwnd = FindWindow(0, L"Calculator");
SetForegroundWindow(hwnd);
Sleep(1000);
screenshot_window(hwnd);
Alternatively, you can use Dwm Thumbnail APIs to paint the target window in your own window. But again, you cannot use GetDC(my_hWnd) to access the bitmap from "Dwm Thumbnail" on your window. Again you would have to take screen shot of desktop window using GetDC(HWND_DESKTOP). This time make sure your own window is the top window.
Application must be DPI aware otherwise screen coordinates will not match.
Also there is a resource leak in the original code. GetDC should be cleaned up with ReleaseDC using the same handle, not NULL
HDC hdcScreen = GetDC(handle);
...
//ReleaseDC(NULL, hdcScreen);
ReleaseDC(handle, hdcScreen);

Convert a windows BITMAP to a PIX (unsigned char buffer)

I'm taking a screenshot of a window in order to proccess it with Leptonica and later do some OCR with Tesseract
The problem is, performance wise I would like to avoid writing and reading the BMP to the disc and just work in memory instead. This is how I make the screenshot:
int width, height = 0;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
// Retrieve the handle to a display device context for the client
// area of the window.
//hdcScreen = GetDC(NULL);
//hdcWindow = GetDC(hWnd);
hdcWindow = GetDC(hWnd);
// Create a compatible DC which is used in a BitBlt from the window DC
hdcMemDC = CreateCompatibleDC(hdcWindow);
if (!hdcMemDC)
{
MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK);
goto done;
}
// Get the client area for size calculation
RECT rcClient;
GetClientRect(hWnd, &rcClient);
// Create a compatible bitmap from the Window DC
hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
if (!hbmScreen)
{
MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK);
goto done;
}
// Select the compatible bitmap into the compatible memory DC.
SelectObject(hdcMemDC, hbmScreen);
// Bit block transfer into our compatible memory DC.
if (!BitBlt(hdcMemDC,
0, 0,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
hdcWindow,
0, 0,
SRCCOPY))
{
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
goto done;
}
// Get the BITMAP from the HBITMAP
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;
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize);
char *lpbitmap = (char *)GlobalLock(hDIB);
// Gets the "bits" from the bitmap and copies them into a buffer
// which is pointed to by lpbitmap.
GetDIBits(hdcWindow, hbmScreen, 0,
(UINT)bmpScreen.bmHeight,
lpbitmap,
(BITMAPINFO *)&bi, DIB_RGB_COLORS);
// A file is created, this is where we will save the screen capture.
HANDLE hFile = CreateFile(L"pics/UI.bmp",
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
// Add the size of the headers to the size of the bitmap to get the total file size
DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
//Size of the file
bmfHeader.bfSize = dwSizeofDIB;
//bfType must always be BM for Bitmaps
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);
//Unlock and Free the DIB from the heap
GlobalUnlock(hDIB);
GlobalFree(hDIB);
//Close the handle for the file that was created
CloseHandle(hFile);
width = rcClient.right - rcClient.left;
height = rcClient.bottom - rcClient.top;
//Clean up
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(hWnd, hdcWindow);
And this is how I read it:
PIX* pixUI = pixRead("pics/UI.bmp");
So, I've seen that the library has a PIX * pixReadMemBmp ( const l_uint8 *cdata, size_t size ) method which takes a l_uint8 which is an unsigned char buffer
The problem is, I don't understand how I can get such a buffer from my HBITMAP or BITMAP object.
Copy the bitmap into a buffer first, then hand this buffer to pixReadMemBmp(). The copying has to be done because , I assume, the pixReadMemBmp() function needs the both bitmap headers in front of the bitmap data, as it would be in a file. Pseudocode:
std::vector<unsigned char> buffer(sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwBmpSize);
std::copy(reinterpret_cast<unsigned char*>(&bmfHeader), reinterpret_cast<unsigned char*>(&bmfHeader) + sizeof(BITMAPFILEHEADER), buffer.begin());
std::copy(reinterpret_cast<unsigned char*>(&bi), reinterpret_cast<unsigned char*>(&bi) + sizeof(BITMAPINFOHEADER), buffer.begin() + sizeof(BITMAPFILEHEADER));
std::copy(lpbitmap, lpbitmap + dwBmpSize, buffer.begin() + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER));
pixReadMemBmp(&buffer[0], buffer.size());

Capturing Entire ScreenShot Of PC in WIndows 8.1

I'm making a C++ program which takes screenshot of the entire screen. I am facing quite a problem. When I run the program, it takes screenshot of the console screen only, not the entire desktop.
HDC Screen = CreateDC(L"DISPLAY", NULL, NULL, NULL);
HDC Capture = CreateCompatibleDC(Screen);
int width = GetDeviceCaps(Screen, HORZRES);
int height = GetDeviceCaps(Screen, VERTRES);
LPBYTE lpcapture;
BITMAPINFO bmiCapture =
{ { sizeof(BITMAPINFOHEADER),width,height,1,24,BI_RGB,0,0,0,0,0 } };
HBITMAP hbmCapture = CreateDIBSection(Screen, &bmiCapture, DIB_RGB_COLORS, (LPVOID *)&lpcapture, NULL, 0);
if (hbmCapture)
{
HBITMAP hbmOld = (HBITMAP) SelectObject(Capture, Capture);
BitBlt(Capture, 0, 0, width, height, Screen, 0, 0, SRCCOPY);
SelectObject(Capture, hbmOld);
}
DeleteDC(Capture);
DeleteDC(Screen);
return hbmCapture;
This should work in a console program, but it would show black console screen in the middle
int ScreenCapture(const char* fname)
{
int result = 0;
HWND hWnd = GetDesktopWindow();
HBITMAP hbmScreen = NULL;
HDC hdcScreen = GetDC(NULL);
HDC hdcWindow = GetDC(hWnd);
int w = GetSystemMetrics(SM_CXSCREEN);
int h = GetSystemMetrics(SM_CYSCREEN);
HDC hdcMemDC = CreateCompatibleDC(hdcWindow);
if (!hdcMemDC) goto cleanup;
hbmScreen = CreateCompatibleBitmap(hdcWindow, w, h);
if (!hbmScreen) goto cleanup;
SelectObject(hdcMemDC, hbmScreen);
if (!BitBlt(hdcMemDC, 0, 0, w, h, hdcWindow, 0, 0, SRCCOPY)) goto cleanup;
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = w;
bi.biHeight = h;
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 = ((w * bi.biBitCount + 31) / 32) * 4 * h;
HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize);
char* lpbitmap = (char*)GlobalLock(hDIB);
GetDIBits(hdcWindow, hbmScreen, 0, h, lpbitmap, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
bmfHeader.bfSize = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bmfHeader.bfType = 0x4D42; //'BM' for Bitmaps
DWORD temp = 0;
HANDLE hFile = CreateFileA(fname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &temp, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &temp, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &temp, NULL);
CloseHandle(hFile);
GlobalUnlock(hDIB);
GlobalFree(hDIB);
result = 1; //success
cleanup:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(NULL, hdcScreen);
ReleaseDC(hWnd, hdcWindow);
return result;
}

Drawing a partially transparent GDI+ Bitmap to a borderless window using UpdateLayeredWindow

I'm trying to create a non-rectangular window with semi-transparent pixels. The image does not come from a PNG but is drawn on-the-fly using GDI+ calls.
I create the window as follows:
WNDCLASSEX wc = WNDCLASSEX();
wc.cbSize = sizeof(wc);
HINSTANCE instance = GetModuleHandle(nullptr);
std::wstring classname(L"gditest ui window class");
if (!GetClassInfoEx(instance, classname.c_str(), &wc)) {
//wc.cbSize;
//wc.style = CS_DROPSHADOW;
wc.lpfnWndProc = process_messages;
//wc.cbClsExtra;
//wc.cbWndExtra;
wc.hInstance = instance;
wc.hIcon;
wc.hCursor = LoadCursor(0, IDC_ARROW);
//wc.hbrBackground;
//wc.lpszMenuName;
wc.lpszClassName = classname.c_str();
wc.hIconSm;
if (!RegisterClassEx(&wc))
throw GetLastError();
}
m_window = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_LAYERED,
classname.c_str(), L"User Interface",
WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,
HWND_DESKTOP, 0, instance, this);
if (!m_window)
throw GetLastError();
update_window();
The update_window() function looks like this:
void user_interface::update_window()
{
RECT r;
GetClientRect(m_window, &r);
Bitmap buf(r.right - r.left, r.bottom - r.top, PixelFormat32bppARGB);
Graphics gfx(&buf);
Rect rect(r.left, r.top, r.right - r.left, r.bottom - r.top);
SolidBrush b(Color(0x7f00ff00));
gfx.FillRectangle(&b, rect);
/* CLSID clsid;
UINT numbytes = 0, numenc = 0;
GetImageEncodersSize(&numenc, &numbytes);
std::vector<char> encoders(numbytes, 0);
ImageCodecInfo *encoderptr = (ImageCodecInfo *)&encoders[0];
GetImageEncoders(numenc, numbytes, encoderptr);
clsid = encoderptr[4].Clsid;
buf.Save(L"test.png", &clsid);
*/
HDC gfxdc = gfx.GetHDC();
HDC scrndc = GetDC(HWND_DESKTOP);
BLENDFUNCTION blend;
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
POINT src = POINT(), dst;
SIZE size;
GetWindowRect(m_window, &r);
dst.x = r.left;
dst.y = r.top;
size.cx = buf.GetWidth();
size.cy = buf.GetHeight();
if (!UpdateLayeredWindow(m_window, scrndc, &dst, &size, gfxdc, &src, 0, &blend, ULW_ALPHA)) {
throw GetLastError();
}
ReleaseDC(HWND_DESKTOP, scrndc);
gfx.ReleaseHDC(gfxdc);
}
The commented piece of code saves the Bitmap object to a PNG, which I wrote just to confirm the bitmap is drawn properly.
No errors occur, however the result on screen is not what I intended. Instead of a nice 50% transparent green square I get a barely visible white square: .
Another weird thing is that clicks on the window fall through to whatever is underneath, eventhough it is slightly visible...
What am I doing wrong here?
Managed to solve this myself by rewriting the update_window() method as such:
void user_interface::update_window()
{
RECT r;
GetClientRect(m_window, &r);
HDC scrndc = GetDC(HWND_DESKTOP);
HDC memdc = CreateCompatibleDC(scrndc);
HBITMAP bmp = CreateCompatibleBitmap(scrndc, r.right - r.left, r.bottom - r.top);
HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, bmp);
Graphics gfx(memdc);
Rect rect(r.left, r.top, r.right - r.left, r.bottom - r.top);
SolidBrush b(Color(0x7f00ff00));
gfx.FillRectangle(&b, rect);
BLENDFUNCTION blend;
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
POINT src = POINT(), dst;
SIZE size;
size.cx = r.right - r.left;
size.cy = r.bottom - r.top;
GetWindowRect(m_window, &r);
dst.x = r.left;
dst.y = r.top;
if (!UpdateLayeredWindow(m_window, scrndc, &dst, &size, memdc, &src, 0, &blend, ULW_ALPHA)) {
throw GetLastError();
}
SelectObject(memdc, oldbmp);
DeleteObject(bmp);
DeleteDC(memdc);
ReleaseDC(HWND_DESKTOP, scrndc);
}
Probably not the most efficient way to do it, but it works. Could probably keep the HBITMAP, memdc and Graphics object around for longer. Figuring this out is left as an exercise for the reader. ;)
The source bitmap for UpdateLayeredWindow() must be compatible to the screen, otherways your visual result is hardware-dependent. GDI and GDIplus seem to be able to draw into any bitmap, but bitblt operations generally cannot handle different (i.e. incompatible) formats. The reason is speed.
Unluckily, Windows documentation doesn't point out these facts very clearly.

How can I take a screenshot in a windows application?

How can I take a screenshot of the current screen using Win32?
HDC hScreenDC = GetDC(nullptr); // CreateDC("DISPLAY",nullptr,nullptr,nullptr);
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int width = GetDeviceCaps(hScreenDC,HORZRES);
int height = GetDeviceCaps(hScreenDC,VERTRES);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC,width,height);
HBITMAP hOldBitmap = static_cast<HBITMAP>(SelectObject(hMemoryDC,hBitmap));
BitBlt(hMemoryDC,0,0,width,height,hScreenDC,0,0,SRCCOPY);
hBitmap = static_cast<HBITMAP>(SelectObject(hMemoryDC,hOldBitmap));
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);
Use GetDC(NULL); to get a DC for the entire screen.
Use CreateCompatibleDC to create a DC compatible with the screen DC.
Use CreateCompatibleBitmap to create a bitmap compatible with the screen DC to hold the result.
Use SelectObject to select the compatible bitmap into the compatible DC.
Use BitBlt to copy from the screen DC to the compatible DC.
Use SelectObject to deselect the compatible bitmap from the compatible DC.
Use DeleteDC to delete the compatible DC.
When you create the compatible bitmap, you want it compatible with the screen DC, not the compatible DC.
For example:
HDC dcScreen = GetDC(0);
HDC dcTarget = CreateCompatibleDC(dcScreen);
HBITMAP bmpTarget = CreateCompatibleBitmap(dcScreen);
HGDIOBJ oldBmp = SelectObject(dcTarget, bmpTarget);
BitBlt(dcTarget, 0, 0, cx, cy, dcDesktop, x, y, SRCCOPY | CAPTUREBLT);
SelectObject(dcTarget, oldBmp);
DeleteDC(dcTarget);
ReleaseDC(dcScreen);
The other important part is to get the size, and location, of the entire virtual screen:
int x = GetSystemMetrics(SM_XVIRTUALSCREEN); //left (e.g. -1024)
int y = GetSystemMetrics(SM_YVIRTUALSCREEN); //top (e.g. -34)
int cx = GetSystemMetrics(SM_CXVIRTUALSCREEN); //entire width (e.g. 2704)
int cy = GetSystemMetrics(SM_CYVIRTUALSCREEN); //entire height (e.g. 1050)
void GetScreenShot(void)
{
int x1, y1, x2, y2, w, h;
// get screen dimensions
x1 = GetSystemMetrics(SM_XVIRTUALSCREEN);
y1 = GetSystemMetrics(SM_YVIRTUALSCREEN);
x2 = GetSystemMetrics(SM_CXVIRTUALSCREEN);
y2 = GetSystemMetrics(SM_CYVIRTUALSCREEN);
w = x2 - x1;
h = y2 - y1;
// copy screen to bitmap
HDC hScreen = GetDC(NULL);
HDC hDC = CreateCompatibleDC(hScreen);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, w, h);
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
BOOL bRet = BitBlt(hDC, 0, 0, w, h, hScreen, x1, y1, SRCCOPY);
// save bitmap to clipboard
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap);
CloseClipboard();
// clean up
SelectObject(hDC, old_obj);
DeleteDC(hDC);
ReleaseDC(NULL, hScreen);
DeleteObject(hBitmap);
}
Full code for saving a raw 24-bit lossless bitmap of all monitors at the current window station using Windows API:
BOOL WINAPI SaveBitmap(WCHAR *wPath)
{
BITMAPFILEHEADER bfHeader;
BITMAPINFOHEADER biHeader;
BITMAPINFO bInfo;
HGDIOBJ hTempBitmap;
HBITMAP hBitmap;
BITMAP bAllDesktops;
HDC hDC, hMemDC;
LONG lWidth, lHeight;
BYTE *bBits = NULL;
HANDLE hHeap = GetProcessHeap();
DWORD cbBits, dwWritten = 0;
HANDLE hFile;
INT x = GetSystemMetrics(SM_XVIRTUALSCREEN);
INT y = GetSystemMetrics(SM_YVIRTUALSCREEN);
ZeroMemory(&bfHeader, sizeof(BITMAPFILEHEADER));
ZeroMemory(&biHeader, sizeof(BITMAPINFOHEADER));
ZeroMemory(&bInfo, sizeof(BITMAPINFO));
ZeroMemory(&bAllDesktops, sizeof(BITMAP));
hDC = GetDC(NULL);
hTempBitmap = GetCurrentObject(hDC, OBJ_BITMAP);
GetObjectW(hTempBitmap, sizeof(BITMAP), &bAllDesktops);
lWidth = bAllDesktops.bmWidth;
lHeight = bAllDesktops.bmHeight;
DeleteObject(hTempBitmap);
bfHeader.bfType = (WORD)('B' | ('M' << 8));
bfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
biHeader.biSize = sizeof(BITMAPINFOHEADER);
biHeader.biBitCount = 24;
biHeader.biCompression = BI_RGB;
biHeader.biPlanes = 1;
biHeader.biWidth = lWidth;
biHeader.biHeight = lHeight;
bInfo.bmiHeader = biHeader;
cbBits = (((24 * lWidth + 31)&~31) / 8) * lHeight;
hMemDC = CreateCompatibleDC(hDC);
hBitmap = CreateDIBSection(hDC, &bInfo, DIB_RGB_COLORS, (VOID **)&bBits, NULL, 0);
SelectObject(hMemDC, hBitmap);
BitBlt(hMemDC, 0, 0, lWidth, lHeight, hDC, x, y, SRCCOPY);
hFile = CreateFileW(wPath, GENERIC_WRITE | GENERIC_READ, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
WriteFile(hFile, &bfHeader, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
WriteFile(hFile, &biHeader, sizeof(BITMAPINFOHEADER), &dwWritten, NULL);
WriteFile(hFile, bBits, cbBits, &dwWritten, NULL);
CloseHandle(hFile);
DeleteDC(hMemDC);
ReleaseDC(NULL, hDC);
DeleteObject(hBitmap);
return TRUE;
}
There is a MSDN sample, Capturing an Image, for capturing an arbitrary HWND to a DC (you could try passing the output from GetDesktopWindow to this). But how well this will work under the new desktop compositor on Vista/Windows 7, I don't know.