Take screenshot of desktop when Windows is locked (Win+L) - c

I want to take a screenshot of desktop while Windows is locked (with Win+L). Standard methods make a black screen, with code of pixel:
COLORREF color = GetPixel(hdc, x, y);
equal -1. Neither a user mode program nor a service could capture a useful image. Any ideas?

GetPixel and BitBlt won't work when the desktop isn't physically displayed on the monitor.
You may have some luck capturing individual windows with PrintWindow. However, not all applications respond to PrintWindow calls the same way, so you may see glitches and/or blackness. You'll also need to capture each window individually.

Related

WS_EX_LAYERED window chroma key (LWA_COLORKEY) doesn't work in Windows 7 with Aero Glass enabled

I'm trying to make a DirectX8 application (Game Maker) chroma keyed using layered windows (WS_EX_LAYERED extended style flags, and LWA_COLORKEY method) for transparency and correct hit testing on a borderless window. However, out of all platforms I've tested it on, Windows 7 fails but specifically only when Aero Glass is turned on. The effect works for apparently one frame, and then stops working - the chroma key then comes out to the screen and the window is a full rectangle, including mouse hit testing. Turning off Aero Glass in advanced appearance, or changing to a Basic or Classic theme fixes the issue, but that's undesirable.
__declspec(dllexport) double __cdecl __gm82core_setchromakey(double enable, double color) {
if (enable>=0.5) {
SetWindowLong(window_handle,GWL_EXSTYLE,GetWindowLong(window_handle,GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(window_handle,((DWORD)color)&0x00ffffff,0xff,LWA_COLORKEY);
} else {
SetWindowLong(window_handle,GWL_EXSTYLE,GetWindowLong(window_handle,GWL_EXSTYLE) & ~WS_EX_LAYERED);
}
return 0;
}
broken effect in Win7 with Aero Glass enabled
(The variable types are all double because the develoment platform (Game Maker) only allows for doubles to be passed to native code)
I've read that there are issues using colors that have the same value on multiple channels like white or yellow for chroma key, and tried using colors such as rgb (253,254,255) or (201,202,203), to similar results.
I've read that chroma keying apparently broke in Windows 7 RC1, and we can just assume that wasn't fixed for RTM due to the experienced behavior.
I've read that in Win 7 and earlier, child windows cannot be chroma keyed, and I've attempted to remove the window's parent using SetParent(), to similar results (and introducing other issues in the process), despite the window's parent being disabled and invisible when the application is launched in borderless mode.
The effect works fine until the window is updated, and then never again, which is an issue as the program has animated controls.
Was verified to work in various versions of Win 10 dated as far back as 1903:
effect working as intended in Win10
I've used an alternate method using DWM direct compositing using a 32 bit ARGB surface format, which allows per-pixel alpha transparency in all platforms, however does not allow for mouse hit testing, which is undesirable for this application due to its free-form window.
Even though the effect works properly in Win10, I'd like to support Win7 as well since that's a common target for the extension package this code is from.

Why this BitBlt example doesn't work anymore?

I'm currently getting back to some Windows Programming using Petzold's book (5th edition).
I compiled the following example using BitBlt and it doesn't work as it is supposed to.
It should copy the Window's icon of (CxSource, CySource) size and replicate it on the whole window's surface.
What happens, in reality, using Windows 7 is that the bitmap below the window gets sourced and copied into the drawing surface i.e. hdcClient.
I don't understand why it behaves like this knowing that it's clear the DC passed to BitBlt is hdcWindow, which refers to a device context obtained via a GetWindowDC(hwnd) of the current application.
I first thought it was due to the fact the transparency mode is enabled by default, but deactivating it doesn't change anything. BitBlt seems to always take the surface below the application Window!
I don't get it! :)
Anyone knows why it works that way and how to fix it?
Making screenshots with BitBlt() did not exactly get any easier since the addition of the DWM (Desktop Window Manager, aka Aero). Petzold's sample code suffers from a subtle timing issue, it is making the screenshot too soon. It does so while Aero is still busy animating the frame, fading it into view. So you see what is behind the window, possibly already partly faded depending on how quickly the first WM_PAINT message is generated.
You can easily fix it by disabling the effect:
#include <windows.h>
#include <dwmapi.h>
#pragma comment(lib, "dwmapi.lib")
And after the CreateWindow() call:
BOOL disabled = TRUE;
DwmSetWindowAttribute(hwnd, DWMWA_TRANSITIONS_FORCEDISABLED, &disabled, sizeof(disabled));
Another tricky detail is that the first BitBlt matters, the DWM returns a cached copy afterwards that is not correctly invalidated by the animation.
This gets grittier when you need a screenshot of a window that belongs to another process. But that was already an issue before Aero, you had to wait long enough to ensure that the window was fully painted. Notable perhaps is the perf of BitBlt(), it gets bogged-down noticeably by having to do job of composing the final image from the window back-buffers. Lots of questions about that at SO, without happy answers.
It is not supposed to copy the windows icon, it is supposed to copy the windows titlebar part where the icon is located.
There are some issues with this (now 20 year old code):
GetSystemMetrics values cannot be used for window related dimensions anymore since GetSystemMetrics returns the classic sizes, not the Visual Style sizes.
Depending on the Windows version, the DWM might define the window size as something larger than your window (where it draws the window shadow and other effects).
Your example works OK on XP:
(There is a small problem because the titlebar is not square (unlike Windows 98/2000 that this example was designed for) so you see a issue in the top left where it is just white. I also modified the example slightly so it varies the HDC source location)
On a modern version of Windows it seems like the DWM or something is not able to properly emulate a simple window DC and parts of the shadow/border/effects area is part of the DC:
I don't know how to fix this but the example is pretty useless anyway, if you want to draw the window icon you should draw the HICON with DrawIconEx. If you want to draw custom non-client area stuff then you need to find more recent examples, not something that only supports the classic theme.

wpf resize a window to to consume all monitors

I am trying to resize a window that covers entire screen.
I tried out following
win.Left = SystemParameters.VirtualScreenLeft;
win.Top = SystemParameters.VirtualScreenTop;
win.Width = SystemParameters.VirtualScreenWidth;
win.Height = SystemParameters.VirtualScreenHeight;
This works perfectly on a monitor of 1080p (Full HD monitor). It also works when I have two monitors attached and one of which is full HD where as the other is an HD monitor. I understand that in such case some of the part of the window will never become visible as it will go out of the bounds of total screen asset.
The problem starts with 4k monitor. On a 4k monitor, it consumes a smaller rectangle on screen and does not span to cover the entire screen. I understand that this is due to scaling (wpf automatically scales window) but not able to find a way to overcome this issue.
The issue is just with the 4k monitor. I assume that it has something to do with DPI as the 4k monitor has higher DPI.
My question is, how to make the application use entire screen asset irrespective of monitor resolution and DPI.
(Please note that setting window maximized will not work as it will not span it over multiple monitors.)
It turned out that the issue was not with resizing of window at all. Window was properly resizing after specifying the co-ordinates. However, there is one thing which might help those who want to do similar thing. Always make sure that you compare the values retrieved form API with the other values from API and WPF with WPF as WPF yields scaled values.

PrintScreen contents are larger than what I see

I would happily provide a screenshot of this, however the problem is the captured image, is much larger than my actual desktop.
I am completely frustrated with this as I have tried using BitBlt with the desktop hdc AND the new "Graphics" commands.
My actual desktop resolution is 1920x1080 - 1080p .
BitBlt and "Graphics" both return that my resolution is 1536x864 # 96 DPI.
A form (WinForm), Maximized, borderless, and irrelevant of scaling mode the form is set to, also shows 1536x864 # 96 DPI.
Now the image that is captured, is like it is being done from 1920x1080, but clipping the region 1536x864 as the screenshot.
If I do PrintScreen directly using Prtscn button, I get the entire image, but still it is about 1.5-2x larger than what I actually see.
What I am looking for -- is a resolution for how I can take a picture of what is on my screen in the scale/dpi/whatever is going on here that it visually looks like. I have written a screen capture program, and using a few different examples for the RubberBand form (overlay form to select a region of the screen by drawing a box), and as you can imagine, this scaling crap is causing those box captures to be offset, and the contents are zoomed.
This is very annoying -- even to explain, however I am positive that most of you are familiar with the terms I use, and also know what to expect from taking a screenshot, so my explanation above should be pretty clear as to what my problem is.
Example/Consideration
Imagine, taking a picture of a window that is 300x300, and getting the top left 150x150 of that zoomed to 300x300 completely skipping the remainder of the window. Resulting image is still 300x300, but it's not what you selected.
Now imagine, you grab a picture of your screen by the only dimensions you can get programmatically, and then put the image into a picturebox. Even though both your screen and the picturebox claim to be the same dimensions and dpi, the image in the picturebox requires scrolling even if the picturebox is maximized to fullscreen on a borderless with no borders / etc. -- again, the picture is zoomed, but how is it still reporting that it's the same size as the form XD (comparing Graphics or BitBlt dimensions with the actual form. also tried comparing picturebox contents, and still same effect)
This, is EXACTLY what the effect is that is happening. When I try to capture a region or segment of the screen. I am not sure why windows api/crl is lying about this seemingly trivial stuff, however there must be a way to accurately obtain screenshots/capture regions without this faux zoom effect -- across all resolutions.
Thank you Hans Passant for pointing me in the right direction.
To add "true" dpi scaling support to a winforms application, you can make it so by adding the following block to your manifest :
Project > Add New Item > Visual C# Items > Application Manifest File
One the file has been added, open it up and look for a line like
</asmv1:assembly>
Whatever the "asmv" number is, (in the example above it is 1), use that to format the code:
<asmv1:application>
<asmv1:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv1:windowsSettings>
</asmv1:application>
Paste the above code (changing the asmv1 to whatever version the manifest is), just above the final closing line for the ""
Also, make sure your forms are set to AutoScale to dpi (and all sub-elements).

How can I copy contents of one window to another using Xlib?

I want to copy the contents of an existing Window to my own Window using Xlib. I have tried XCopyArea and it refuses to copy between two Windows. I have also tried XGetImage and XPutImage and it's also failing.
What's the best way to copy the graphics contents of a Window to my own?
Part II:
Based on information below, I was able to get XCopyArea and XGetImage to work. The reason it wasn't working was difference in depth of source and destination Window. I was surprised to learn that different Windows have different depth on my desktop.
But I still have limited success with XCopyArea. If I start copying from the top of certain Windows, like Google Chrome, it doesn't copy the full area, just the title bar. XGetImage works fine in those cases. Any clue on why XCopyArea won't copy beyond the title bar of some Windows?
XCopyArea should be fine.
Note that this will only copy into the foreground of the destination - maybe it is being drawn then erased?
Without code I can speculate:
If it is failing, have you checked that the windows definitely have the same root and depth?
Also make sure you review the X Window coordinate system. Maybe try copying so that the corner of your Copy is in the centre of the Destination to see if you can get anything.
You normally want some way of handling Expose events in the destination window so you can do a refresh.
I'd recommend creating a Pixmap as an intermediate. Both Pixmap and Window are Drawable.
Use XCopyArea to copy into the Pixmap.
Then use XSetWindowBackgroundPixmap to actually render the image. Setting the background means you can then ignore any need for handling Expose events to redraw the image.

Resources