How can I get the pixel of the covered window? - c++

I am using Visual Studio 2008 on Windows XP and studying C++.
I am curious about the getting pixel from desktop.
Can I get the screen pixel with GetPixel function in the case that the window is covered with another window? In the status of my window being covered, I was trying to call the GetPixel:
GetPixel(hdc, x, y);
But this call dosen't retrieve the pixel of the original window but the covering window or other color one.
How can I get the pixel of the covered window?
I add my code as the following ...
VOID GetDisplay(HWND& hwnd, RECT& area, COLORREF (*display)[1000]) {
HDC hdc=GetDC(hwnd);
for(INT x=area.left;x<=area.right;x++) {
for(int y=area.top;y<=area.bottom;y++) {
display[x][y]=GetPixel(hdc, x, y);
}
}
ReleaseDC(hwnd, hdc);
}
...
SetWindowPos(hwnd, HWND_TOPMOST, 300, 300, 500, 350, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
SetForegroundWindow(hwnd);
SetActiveWindow(hwnd);
//EnableWindow(hwnd, TRUE);
SetFocus(hwnd);
GetDisplay(hwnd, within_wall, display);//trouble
The problem is causing on the GetPixel in GetDisplay. GetPixel is returning the pixel color of the covering window.

There's no 100% way to do it, as windows are not required to, and generally don't, draw obscured areas. You will not be able to make the desktop a foreground window of course. And focus has no effect on visibility, as someone has mentioned SetFocus. That would be ineffective.
I have seen proposals to use WM_PRINT to access obscured pixels. Again this is not guaranteed, but the desktop window is pretty specific so if you get that to work, I guess it'll be somewhat reliable.
For more on this, see: PrintWindow.
For what it's worth, GetPixel is extremely slow. You'll probably notice a performance problem in your code. You'll get much better performance by using methods like GetDIBits.

Related

Failing to bitblt() to the centre of a transparent window c++/winapi

When I bitblt onto my transparent window, a bmp smaller than the client area, it draws a fussy surrounding area instead of transparent like the set colorkey from:
SetLayeredWindowAttributes(hwnd, RGB(0,255,0), 120, LWA_COLORKEY | LWA_ALPHA); //colorkey & win alpha
The blit looks like this:
The candle-like test image makes its own area transparent like it should, but the surrounding client area gets grayed out according to the window-alpha value (120). Any ideas how to fix this? I've tried:
SetBkColor(hdc, crKey); and generating a background with
FillRect(background_hdc, &background_rect, hbrush); and blitting that first
Where neither work.
I have the feeling this should be alot easier than I'm making it out to be, and I still feel something like SetBkColor(hdc,crKey); should do the trick, but it doesnt. Maybe there is a similar command that does that I'm looking for?
Isnt it funny how you solve the problem just after you formulate it properly?
Obviously the hbrBackground in the WNDCLASSEX wc;
defines the background of the window. So by setting the brush background when registering the window like so:
wc.hbrBackground=CreateSolidBrush(RGB(240, 20, 120)); //COLORREF==crKey
it gets crKey cancelled out during the blit along with the bmps crKey color. Thanks.

Capturing window's screenshot excluding certain HWND's styles (WS_SYSMENU)

I'm trying to periodically compare snapshots of the same window every few of seconds. Even though that technically the window doesn't change, pictures remains the same, i still get notified that something has changed with in the picture, I'm using good old BitBlt function to capture a specific window. and Zlib's CRC32 to compare results.
Here's an example of 2 identical pictures, the sole difference is the windows' caption(untitled paint) color. Whenever the window has the focus it's black, and gray otherwise. Since I'm not the owner of the HWND, Is there a better way of taking a picture without calculating the actual size of the window minus GetSystemMetrices(SM_CYSIZEFRAME / SM_CXSIZE) than changing the style.
My code:
WINDOWPLACEMENT rect;
::GetWindowPlacement(windowDesc.hWnd, &rect);
if (SW_SHOWMINIMIZED == rect.showCmd)
{
return;
}
CImage img;
img.Create(
rect.rcNormalPosition.right - rect.rcNormalPosition.left,
rect.rcNormalPosition.bottom - rect.rcNormalPosition.top,
32);
HWND hWnd = windowDesc.hWnd;
std::shared_ptr<HDC__> spSrcHdc(::GetDC(hWnd), [hWnd](HDC hdc) {::ReleaseDC(hWnd, hdc); });
//::BitBlt(img.GetDC(), 0, 0, img.GetWidth(), img.GetHeight(), spSrcHdc.get(), 0, 0, SRCCOPY);
::PrintWindow(hWnd, img.GetDC(), 0x2);
BITMAP bmp = { 0 };
if (!::GetObject((HBITMAP)img, sizeof(BITMAP), &bmp))
{
throw std::exception("Failed to retrieve raw bmp buffer");
}
unsigned long ulbmpCRC = crc32(0,
(BYTE*)bmp.bmBits, bmp.bmWidthBytes * bmp.bmHeight);
if (0 != ulbmpCRC && ulbmpCRC == windowDesc.crc)
{
}
And another issue i failed to solve is when textboxes are present. The very fact that i have the cursor flickering, it generates different CRC32 values. yet again, can i use BitBlt with the ability to ignore the phenomenon?
To determine the client area of the target window, you can send it a WM_NCCALCSIZE message. This should enable you to determine the size of the caption bar reliably for most applications, something like:
RECT r;
GetWindowRect (hTargetWnd, &r);
SendMessage (hTargetWnd, WM_NCCALCSIZE, FALSE, (LPARAM) &r);
As for the flashing caret problem, you could try remembering the checksums for the last two different screens you have seen. Then you have a chance of working out when the window is, in effect, switching back and forth between two states.
That, and more sophisticated comparison logic (such as walking the list of child windows to look for edit controls to see if any changes are confined to the contents of such controls), should make it possible to achieve most of what you want.

Draw semitransparently in invisible layered window

My goal is to have a fullscreen overlaying invisible "canvas" on which I can draw using win32's various drawing functions.
The way I am currently attempting it is this:
WNDCLASSA myclass = { 0 };
myclass.lpfnWndProc = WindowProc3;
myclass.hInstance = GetModuleHandle(0);
myclass.lpszClassName = "MyCanvas";
myclass.hbrBackground = CreateSolidBrush(0xFEEDBEEF);
myclass.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassA(&myclass);
...
HWND wnd = CreateWindowExA(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT, "MyCanvas", 0, WS_POPUP | WS_VISIBLE, 0, 0, screen_width, screen_height, 0, 0, GetModuleHandle(0), 0);
SetLayeredWindowAttributes(wnd, 0xFEEDBEEF, 0, LWA_COLORKEY);
Although this serves as a canvas, hours of googling later, I am still unable to draw on it semitransparently.
I have added a screenshot of what my program is currently displaying as I am writing this. What I would like to be able to do is, for example, make the black box in the top right corner (drawn with Rectangle) semitransparent so as to reveal the stackoverflow page content below it.
This is a question I found that I was hopeful about, but the resulting text is just a blended combination of the background color ((COLORREF)0xFEEDBEEF) and text color. Other things I have found have either just made the element fully invisible, done nothing at all, or required some library like MFC. I want to only use win32 functions if at all possible, as I would like to be able to achieve the highest FPS possible.
I do not care if this doesn't work on all Windows versions as long as it does on 7 up to 10.
If you only need transparency for a rectangular area where all pixels either have the same transparency (aka alpha) value or are completely transparent, you can use SetLayeredWindowAttributes() with a combination of alpha value and/or color key.
UpdateLayeredWindow() is the way to go if you need to be able to define transparency per-pixel.
For that you have to create memory DC and select a 32bpp bitmap into it. You may use the buffered paint API to ease the task. Raymond Chen has a blog post with a code sample about that.
You can draw into the memory DC, but you can't use most of GDI API for that, because GDI ignores the alpha channel (transparency). I suggest using GDI+ which allows you to specify the alpha values.
After you have completed drawing into the memory DC, you would call UpdateLayeredWindow() and pass that memory DC as the argument for the hdcSrc parameter to make the result visible on screen.
Illustration of possible effects:
SetLayeredWindowAttributes( hwnd, 0, 176, LWA_ALPHA );
SetLayeredWindowAttributes( hwnd, colorkey, 0, LWA_COLORKEY );
SetLayeredWindowAttributes( hwnd, colorkey, 176, LWA_ALPHA|LWA_COLORKEY );
UpdateLayeredWindow( ... )
Note the antialiased edge of the shape and the transparency gradient in the last example. Things like that are only possible with UpdateLayeredWindow().

SetWindowPos not setting window to the correct size [duplicate]

I'm working on an app that positions windows on the screen in a grid style. When Running this on Windows 10, there is a huge gap between the windows. Further investigation shows that GetWindowRect is returning unexpected values, including an invisible border, but I can't get it to return the real values with the visible border.
1) This thread suggests this is by design and you can "fix" it by linking with winver=6. My environment does not allow this but I've tried changing the PE MajorOperatingSystemVersion and MajorSubsystemVersion to 6 with no affect
2) That same thread also suggests using DwmGetWindowAttribute with DWMWA_EXTENDED_FRAME_BOUNDS to get the real coordinates from DWM, which works, but means changing everywhere that gets the window coordinates. It also doesn't allow the value to be set, leaving us to reverse the process to be able to set the window size.
3) This question suggests it's lack of the DPI awareness in the process. Neither setting the DPI awareness flag in the manifest, or calling SetProcessDpiAwareness had any result.
4) On a whim, I've also tried adding the Windows Vista, 7, 8, 8.1 and 10 compatibility flags, and the Windows themes manifest with no change.
This window is moved to 0x0, 1280x1024, supposedly to fill the entire screen, and when querying the coordinates back, we get the same values.
The window however is actually 14 pixels narrower, to take into account the border on older versions of Windows.
How can I convince Windows to let me work with the real window coordinates?
Windows 10 has thin invisible borders on left, right, and bottom, it is used to grip the mouse for resizing. The borders might look like this: 7,0,7,7 (left, top, right, bottom)
When you call SetWindowPos to put the window at this coordinates:
0, 0, 1280, 1024
The window will pick those exact coordinates, and GetWindowRect will return the same coordinates. But visually, the window appears to be here:
7, 0, 1273, 1017
You can fool the window and tell it to go here instead:
-7, 0, 1287, 1031
To do that, we get Windows 10 border thickness:
RECT rect, frame;
GetWindowRect(hwnd, &rect);
DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame, sizeof(RECT));
//rect should be `0, 0, 1280, 1024`
//frame should be `7, 0, 1273, 1017`
RECT border;
border.left = frame.left - rect.left;
border.top = frame.top - rect.top;
border.right = rect.right - frame.right;
border.bottom = rect.bottom - frame.bottom;
//border should be `7, 0, 7, 7`
Then offset the rectangle like so:
rect.left -= border.left;
rect.top -= border.top;
rect.right += border.left + border.right;
rect.bottom += border.top + border.bottom;
//new rect should be `-7, 0, 1287, 1031`
Unless there is a simpler solution!
How can I convince Windows to let me work with the real window coordinates?
You are already working with the real coordinates. Windows10 has simply chosen to hide the borders from your eyes. But nonetheless they are still there. Mousing past the edges of the window, your cursor will change to the resizing cursor, meaning that its still actually over the window.
If you want your eyes to match what Windows is telling you, you could try exposing those borders so that they are visible again, using the Aero Lite theme:
http://winaero.com/blog/enable-the-hidden-aero-lite-theme-in-windows-10/
AdjustWindowRectEx (or on Windows 10 and later AdjustWindowRectExForDpi) might be of use. These functions will convert a client rectangle into a window size.
I'm guessing you don't want to overlap the borders though, so this probably isn't a full solution--but it may be part of the solution and may be useful to other people coming across this question.
Here's a quick snippet from my codebase where I've successfully used these to set the window size to get a desired client size, pardon the error handling macros:
DWORD window_style = (DWORD)GetWindowLong(global_context->window, GWL_STYLE);
CHECK_CODE(window_style);
CHECK(window_style != WS_OVERLAPPED); // Required by AdjustWindowRectEx
DWORD window_style_ex = (DWORD)GetWindowLong(global_context->window, GWL_EXSTYLE);
CHECK_CODE(window_style_ex);
// XXX: Use DPI aware version?
RECT requested_size = {};
requested_size.right = width;
requested_size.bottom = height;
AdjustWindowRectEx(
&requested_size,
window_style,
false, // XXX: Why always false here?
window_style_ex
);
UINT set_window_pos_flags = SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER;
CHECK_CODE(SetWindowPos(
global_context->window,
nullptr,
0,
0,
requested_size.right - requested_size.left,
requested_size.bottom - requested_size.top,
set_window_pos_flags
));
There are still two ambiguities in the above use case:
My window does have a menu, but I have to pass in false for the menu param or I get the wrong size out. I'll update this answer with an explanation if I figure out why this is!
I haven't yet read about how Windows handles DPI awareness so I'm not sure when you want to use that function vs the non DPI aware one
You can respond to the WM_NCCALCSIZE message, modify WndProc's default behaviour to remove the invisible border.
As this document and this document explain, when wParam > 0, On request wParam.Rgrc[0] contains the new coordinates of the window and when the procedure returns, Response wParam.Rgrc[0] contains the coordinates of the new client rectangle.
The golang code sample:
case win.WM_NCCALCSIZE:
log.Println("----------------- WM_NCCALCSIZE:", wParam, lParam)
if wParam > 0 {
params := (*win.NCCALCSIZE_PARAMS)(unsafe.Pointer(lParam))
params.Rgrc[0].Top = params.Rgrc[2].Top
params.Rgrc[0].Left = params.Rgrc[0].Left + 1
params.Rgrc[0].Bottom = params.Rgrc[0].Bottom - 1
params.Rgrc[0].Right = params.Rgrc[0].Right - 1
return 0x0300
}

MFC Feature Pack applications have wrong size when restored to maximized in secondary monitor

I've noticed that applications developed using MFC Feature Pack (VS2008), when restored from minimized to maximized in the secondary monitor, are sized as if they were in the primary monitor, leaving space for the Windows taskbar.
I've found this behaviour on my own programs, but also in the MFC Feature Pack samples, so I guess it's some bug in the MFC Feature Pack classes.
So, for example, I build the Visual Studio sample, run it, move it to the secondary monitor, maximize it there, minimize it, restore it, and I get this:
Is there any known workaround for this? Has it been solved for more recent versions of MFC?
UPDATE:
It seems to happen only if the secondary monitor is on the right of the main one, but not if it's on the left.
Answering my own question, for future reference:
After further research, I've found it seems to be a bug and has already been reported to Microsoft:
This caused by the fact that CFrameImpl::OnGetMinMaxInfo uses the
window rectangle to determine which monitor should be used to
determine the maximized size. When the window is minimized the
top-left point of the rectangle returned by GetWindowRect is (-32000,
-32000). When this rectangle is used to determine the monitor the left-most monitor is returned.
It says "Resolved - It will not be fixed", so I've looked for a workaround and found this seems to work:
void CMainFrame::OnSize(UINT nType, int cx, int cy)
{
CMDIFrameWndEx::OnSize(nType, cx, cy);
if (nType == SIZE_MAXIMIZED)
{
CRect rectWindow;
GetWindowRect(&rectWindow);
CRect rect(0, 0, 0, 0);
MONITORINFO mi;
mi.cbSize = sizeof(MONITORINFO);
if (GetMonitorInfo(MonitorFromWindow(this->m_hWnd, MONITOR_DEFAULTTONEAREST), &mi))
{
rect = mi.rcWork;
}
else
{
::SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
}
MoveWindow(rect);
}
}