Screenshot of the main WPF window under second monitor/TV - c#

I use my application on the second monitor and sometimes at the primary monitor of the computer.
How I can get screenshot of the second monitor?
The following code doesn't work for the second monitor...
Graphics gfx;
Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
gfx = Graphics.FromImage(bmp);
gfx.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
MemoryStream ms = new MemoryStream();
bmp.Save(ms, ImageFormat.Jpeg);
byte[] bitmapData = ms.ToArray();

Use Screen.AllScreens[1].Bounds isntead of Screen.PrimaryScreen.Bounds.
Or more reliable to get the first non Primary Screen.
var secondScreen = Screen.AllScreens.Where(screen => !screen.Primary).FirstOrDefault();
check for secondScreen == null to know if you have a secondScreen.
Edit:
You might also be interested in Screen.FromControl that gives the screen that the application is currently running on.

That code does not work for your second screen because it is explicitly using Screen.PrimaryScreen.
If you want to pull from the second display explicitly (ignoring the case where you have 3…n displays), you can replace PrimaryScreen with AllScreens[1].
Keep in mind that this will break if you ever disconnect that second display.
It sounds like maybe you want to be capturing your application window instead of the screen, in case the application isn't taking up the whole screen or straddles two screens. WPF has this capability natively: Get System.Drawing.Bitmap of a WPF Area using VisualBrush

Related

Win32 exception is being thrown while taking a screenshot [c#]

My application is recording the screen (taking screenshots) using Graphics.CopyFromScreen(...) function. The problem is that sometimes an ,,Invalid handle win32" exception is being thrown for some unknown reason. The program is running in the background (it's a console app project with hidden console). I was looking for a solution with google, but I haven't found anything helpful in my case.
The function I wrote:
static Bitmap takeScreenshot()
{
Bitmap bmpScreenCapture = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height);
using (Graphics g = Graphics.FromImage(bmpScreenCapture))
{
g.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
Screen.PrimaryScreen.Bounds.Y,
0, 0,
bmpScreenCapture.Size,
CopyPixelOperation.SourceCopy);
}
return bmpScreenCapture;
}
I've just found out that the app was trying to create files that already existed. Somehow that caused an exception, but I don't know why. I deleted old files and now program seems to work properly...

BitBlt failing on window rendered by OpenGL

I'm making a program to take a screenshot of a game while I'm streaming, so it take a screenshot and save it automatically, but when I set the game to use OpenGL my function fail, it keep saving the same image over and over again, it just change the image after I restart the game.
It seems it works in the first run, but on the next ones it keep saving the first image.
Here is what I'm using:
public static Bitmap PrintWindow(IntPtr hwnd) {
try {
RECT rc;
GetClientRect(hwnd, out rc);
IntPtr hdcFrom = GetDC(hwnd);
IntPtr hdcTo = CreateCompatibleDC(hdcFrom);
//X and Y coordinates of window
int Width = rc.right;
int Height = rc.bottom;
Bitmap bmp = null;
IntPtr hBitmap = CreateCompatibleBitmap(hdcFrom, Width, Height);
if (hBitmap != IntPtr.Zero) {
// adjust and copy
IntPtr hLocalBitmap = SelectObject(hdcTo, hBitmap);
BitBlt(hdcTo, 0, 0, Width, Height, hdcFrom, 0, 0, CopyPixelOperation.SourceCopy);
SelectObject(hdcTo, hLocalBitmap);
//We delete the memory device context.
DeleteDC(hdcTo);
//We release the screen device context.
ReleaseDC(hwnd, hdcFrom);
//Image is created by Image bitmap handle and assigned to Bitmap variable.
bmp = System.Drawing.Image.FromHbitmap(hBitmap);
//Delete the compatible bitmap object.
DeleteObject(hBitmap);
bmp.Save("saving.png", System.Drawing.Imaging.ImageFormat.Png);
}
return bmp;
}
catch {
}
return new Bitmap(0, 0);
}
If I change the game graphic to use DirectX it works good, it's just happening while using OpenGL, so not sude if it must be different for openGL windows or if it's impossible to capture those kind of window.
Using double buffered OpenGL is mutually exclusive with using GDI operations¹. Use glReadPixels to take a screenshot.
¹: Well, technically if you know what you're doing and take the right precautions you can mix them. But it's more trouble than it's worth doing.
After a recent windows 10 update, DirectX rendered windows now suffer from the same problem. I found a workaround, but you're not going to like it...
If you set hwnd = 0, the BitBlt now refers to the whole screen, instead of just one window. Then you can change BitBlt's source offset values to grab only the target window.
Although this works, its much slower than the original way you had it. :(
On my laptop, grabbing a 1080p window used to take 3ms, but now, using this workaround, it takes 27ms, which really ruins the streaming performance. :(
Still, its better than nothing.

How do screenshot in c# (low level may be) when desktop blocked?

I try get screenshot (windows 8) with code and get black screen when desktop blocked:
public static Bitmap ImageFromScreen()
{
Graphics gr;
Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height,PixelFormat.Format32bppRgb);
gr = Graphics.FromImage(bmp);
gr.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y,
0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
return bmp;
}
You don't.
Apart from window cache (which isn't accessible, and doesn't really have to be there), the data simply isn't there. You'd have to fake WM_PAINT messages and force the application to draw to your surface (most controls will take the HDC from wParam; that still excludes a ton of badly written (or not Windows-native) applications - and even then, this will not work most of the time, like when the windows are minimized or the desktop is locked.
What is it that you're actually trying to do?
EDIT:
Okay, it's obvious that you're explicitly talking about the "locked screen" case - there's no way to get a screenshot of the desktop in that case - it doesn't really exist. The lock screen exists in a different session, so you no longer have any connection to the "hidden" user session. This is similar to trying to take screenshots on a server application after you've disconnected Remote Desktop - there's nothing to take shots of. It may be possible to force some applications to draw to your context, but nothing that would simply work.

How does the Graphics CopyFromScreen method copy into a bitmap?

private void startBot_Click(object sender, EventArgs e)
{
Bitmap bmpScreenshot = Screenshot();
this.BackgroundImage = bmpScreenshot;
}
private Bitmap Screenshot()
{
// This is where we will store a snapshot of the screen
Bitmap bmpScreenshot =
new Bitmap(Screen.PrimaryScreen.Bounds.Width,Screen.PrimaryScreen.Bounds.Height);
// Creates a graphic object so we can draw the screen in the bitmap (bmpScreenshot);
Graphics g = Graphics.FromImage(bmpScreenshot);
// Copy from screen into the bitmap we created
g.CopyFromScreen(0, 0, 0, 0, Screen.PrimaryScreen.Bounds.Size);
// Return the screenshot
return bmpScreenshot;
}
I've been recently playing around with C# and I'm just following some tutorial, I just don't understand how if I was to erase Graphics g it wouldn't put the image as the background, but at no point does the code assign any relation between the variables, other than Graphics g = Graphics.FromImage(bmpScreenshot), then g is given some parameters, but then we return bmpScreenshot which just doesn't make any sense, I would expect g to be returned?
Devices that can display graphics are virtualized in Windows. The concept is called a "device context" in the winapi, underlying representation is a "handle". The Graphics class wraps that handle, it does not itself store the pixels. Note the Graphics.GetHdc() method, a way to get to that handle.
The class otherwise just contains the drawing methods that produce graphics output on the device represented by that handle. Actual devices can be the screen, a printer, a metafile, a bitmap. With the big advantage in your own code that it can be used to produce output where ever you want it to go. So printing is just as easy as painting it to the screen or drawing to a bitmap that you store to a file.
So by calling Graphics.FromImage(), you associate the Graphics object to the bitmap. All of its draw methods actually set pixels in the bitmap. Like CopyFromScreen(), it simply copies pixels from the video adapter's frame buffer to the device context, in effect setting the pixels in the bitmap. So the expected return value of this code is the actual bitmap. The Graphics object should be disposed before that happens since it is no longer useful. Or in other words, the underlying handle needs to be released so the operating system de-allocates its own resources to represent the device context.
That's a bug in the code snippet. Repeated calls to this method can easily crash the program when Windows refuses to create more device contexts. And the garbage collector doesn't otherwise catch up fast enough. It should be written as:
using (var g = Graphics.FromImage(bmpScreenshot)) {
g.CopyFromScreen(0, 0, 0, 0, Screen.PrimaryScreen.Bounds.Size);
return bmpScreenshot;
}
The thing to understand is that Graphics g = Graphics.FromImage(bmpScreenshot) creates a Graphics context for drawing into the image that was passed as an argument (bmpScreenshot).
So, after you create the graphics content:
Graphics g = Graphics.FromImage(bmpScreenshot)
When you copy from the screen:
g.CopyFromScreen(0, 0, 0, 0, Screen.PrimaryScreen.Bounds.Size);
This manipulates the bmpScreenshot Bitmap which Graphics g holds a reference to.
From the documentation:
image [in]:
Type: Image*
Pointer to an Image object that will be associated with the new Graphics object.

Taking Screen shot continuously without slow down the PC - C#

I have to capture a screen shot continuously after every 250 milliseconds for my program (Similar to Netmeeting). I used the following code:
Image CaptureScreenShot()
{
bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
return bmpScreenshot;
}
To capture the screenshot but it slow the performance of the PC. Where, Netmeeting do not.
Is there any way to get screen shot without slowing the PC?
Code sample will be appreciated
You won't find a basic answer here. They use much more involved mechanisms for detecting changes on the screen and sending them.
Check out how terminal svcs work -
http://technet.microsoft.com/en-us/library/cc755399%28WS.10%29.aspx
ideally you are hooking into the GUI and monitoring events, etc. much more advanced than simply screen scraping. If you want to look at less advanced code check out http://www.tightvnc.com

Categories

Resources