.NET 4.0 Graphics library - How can I capture the "whole" screen? - c#

I'm coding a small app to take a screenshot every X seconds and I've ran into a small but annoying roadblock. Take this image, for example:
Screen captured using the default 'Print Screen' function on Windows 7
If I try to take the same screenshot by using the default .NET 4 Graphics library, the circled area doesn't show up. Same happens with Visual Studio tabbed menus and some other apps I can't remember. The rest of the image comes out fine, tho.
This is the code I'm using. I might be screwing something up but I can't figure it for the life of me. Any help would be appreciated:
memoryImage = new Bitmap(resolution.Width, resolution.Height);
Size s = new Size(memoryImage.Width, memoryImage.Height);
// Create graphics
Console.WriteLine("Creating Graphics...");
Console.WriteLine();
Graphics memoryGraphics = Graphics.FromImage(memoryImage);
// Copy data from screen
Console.WriteLine("Copying data from screen...");
Console.WriteLine();
memoryGraphics.CopyFromScreen(0, 0, 0, 0, s);

This test saves correctly the whole screen on my configuration (Windows 10, VS 2015) =>
Rectangle screenBounds = Screen.GetBounds(System.Drawing.Point.Empty);
using (Bitmap bitmap = new Bitmap(screenBounds.Width, screenBounds.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(0, 0, 0, 0, bitmap.Size, CopyPixelOperation.SourceCopy);
}
bitmap.Save("e:\\ScreenCopy.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
}

Related

How can I create a screenshot?

How can i create screenshot in c# Windows Forms App?
I tried:
ScreenCapture sc = new ScreenCapture();
Image img = sc.CaptureScreen();
this.imageDisplay.Image = img;
sc.CaptureWindowToFile(this.Handle, "C:\\temp2.png", ImageFormat.png);`
Second Attempt
using (var bitmap = new Bitmap(1680, 1050))
{
DrawToBitmap(bitmap, new Rectangle(0, 0, bitmap.Width, bitmap.Height));
bitmap.Save(#"c:\TEST.bmp");
}
I would have refered you to this post on the same topic Capture screenshot of active window? but it looks like the accepted answer is where you copied your original code from. That code relies on an 3rd party library from 2004 (https://www.developerfusion.com/code/4630/capture-a-screen-shot/) so you need to add that to your project in order to use your code.
But the same question has 10 other answers which does not rely on 3rd party libraries and most of them should work just fine.
Like this one for example:
Rectangle bounds = Screen.GetBounds(Point.Empty);
using(Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height)) {
using(Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
}
bitmap.Save("test.jpg", ImageFormat.Jpeg);
}
https://stackoverflow.com/a/1163770/612620
This question should probably be marked as a duplicate of Capture screenshot of active window?

C# CopyFromScreen takes a screenshot of whats underneath the active window

I'm trying to make an application that I can open and minimize or just leave it there, then with the help of a hotkey I want it to capture a portion of a screen, I got all that covered and working fine except for when I hit the hotkey and I have a game window opened, instead of taking a screenshot of the game it takes a screenshot of what's underneath that window, I've searched literally everywhere and couldn't find an answer, this is the closest I got to:
c# screenshot of active window but doesn't caputre the window
Here's my code:
Bitmap printscreen = new Bitmap(40, 20);
Graphics graphics = Graphics.FromImage(printscreen as Image);
graphics.CopyFromScreen(775, 515, 0, 0, printscreen.Size);
printscreen = Transform(printscreen);
Bitmap newImage = new Bitmap(100, 50);
using (Graphics gr = Graphics.FromImage(newImage))
{
gr.SmoothingMode = SmoothingMode.HighQuality;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
gr.DrawImage(printscreen, new Rectangle(0, 0, 100, 50));
newImage.Save("code.jpg");
}
What I'm trying to achieve is capturing a portion of the screen of the active window or a specific window, thanks!

C# WinForm - SharpDX.Toolkit.Graphics draw 2D Texture

I would like to implement hardware acceleration for a C # WinForms application. Reason is that i have to draw 150 x 720p images and the 5 picturebox control's takes too long (scaling+drawing of images) and therefore are problems with disposing and reloading.
So I dealt with ShapeDX.
But now I 'm stuck and do not know how to draw the 2D Texture. To test the code i just have a Test Button and one PictureBox.
When I run the code in the PictureBox also DirectX ( Draw or 3D ) is loaded. I acknowledge the black background. But I do not understand how the Texture must be drawn.
String imageFile = "Image.JPG";
Control TargetControl = this.pictureBoxCurrentFrameL;
int TotalWidth = TargetControl.Width;
int TotalHeight = TargetControl.Height;
SharpDX.Direct3D11.Device defaultDevice = new SharpDX.Direct3D11.Device(SharpDX.Direct3D.DriverType.Hardware, SharpDX.Direct3D11.DeviceCreationFlags.Debug);
SharpDX.Toolkit.Graphics.GraphicsDevice graphicsDevice = SharpDX.Toolkit.Graphics.GraphicsDevice.New(defaultDevice);
SharpDX.Toolkit.Graphics.PresentationParameters presentationParameters = new SharpDX.Toolkit.Graphics.PresentationParameters();
presentationParameters.DeviceWindowHandle = this.pictureBoxCurrentFrameL.Handle;
presentationParameters.BackBufferWidth = TotalWidth;
presentationParameters.BackBufferHeight = TotalHeight;
SharpDX.Toolkit.Graphics.SwapChainGraphicsPresenter swapChainGraphicsPresenter = new SharpDX.Toolkit.Graphics.SwapChainGraphicsPresenter(graphicsDevice, presentationParameters);
SharpDX.Toolkit.Graphics.Texture2D texture2D = SharpDX.Toolkit.Graphics.Texture2D.Load(graphicsDevice, imageFile);
//Now i should draw. But how?
swapChainGraphicsPresenter.Present();/**/
Using Microsoft Visual Studio Community 2015 (.Net 4, C# WinForm) on Windows 10 an SharpDX-SDK-2.6.3!
Thank you for assistance.
I solved the problem by simply switching to SlimDX (SlimDX Runtime .NET 4.0 x64 January 2012.msi, .Net4, Win10, MS Visual Studio Community 2015, Winforms App.). There are several useful tutorials .
to use SlimDX just link the only one DLL to ur project! after installing SlimDX u will find this SlimDX.dll file somewhere on ur C Drive.
it is important to understand that you need at least one factory and an render target for Direct2D . The RenderTarget points to the object to be used ( Control / form / etc ) and takes over the drawing.
a swap chain is not needed. probable used by the render target internally. the biggest part is to convert an bitmap into usefull Direct2D Bitmap (for drawing). Otherwise, you can process the bitmap data also from a MemoryStream .
For Those Who are looking for a solution too:
Control targetControl = this.pictureBoxCurrentFrameL;
String imageFile = "Image.JPG";
//Update control styles, works for forms, not for controls. I solve this later otherwise .
//this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
//this.SetStyle(ControlStyles.Opaque, true);
//this.SetStyle(ControlStyles.ResizeRedraw, true);
//Get requested debug level
SlimDX.Direct2D.DebugLevel debugLevel = SlimDX.Direct2D.DebugLevel.None;
//Resources for Direct2D rendering
SlimDX.Direct2D.Factory d2dFactory = new SlimDX.Direct2D.Factory(SlimDX.Direct2D.FactoryType.Multithreaded, debugLevel);
//Create the render target
SlimDX.Direct2D.WindowRenderTarget d2dWindowRenderTarget = new SlimDX.Direct2D.WindowRenderTarget(d2dFactory, new SlimDX.Direct2D.WindowRenderTargetProperties() {
Handle = targetControl.Handle,
PixelSize = targetControl.Size,
PresentOptions = SlimDX.Direct2D.PresentOptions.Immediately
});
//Paint!
d2dWindowRenderTarget.BeginDraw();
d2dWindowRenderTarget.Clear(new SlimDX.Color4(Color.LightSteelBlue));
//Convert System.Drawing.Bitmap into SlimDX.Direct2D.Bitmap!
System.Drawing.Bitmap bitmap = (System.Drawing.Bitmap)Properties.Resources.Image_720p;//loaded from embedded resource, can be changed to Bitmap.FromFile(imageFile); to load from hdd!
SlimDX.Direct2D.Bitmap d2dBitmap = null;
System.Drawing.Imaging.BitmapData bitmapData = bitmap.LockBits(new Rectangle(new Point(0, 0), bitmap.Size), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);//TODO: PixelFormat is very important!!! Check!
SlimDX.DataStream dataStream = new SlimDX.DataStream(bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, true, false);
SlimDX.Direct2D.PixelFormat d2dPixelFormat = new SlimDX.Direct2D.PixelFormat(SlimDX.DXGI.Format.B8G8R8A8_UNorm, SlimDX.Direct2D.AlphaMode.Premultiplied);
SlimDX.Direct2D.BitmapProperties d2dBitmapProperties = new SlimDX.Direct2D.BitmapProperties();
d2dBitmapProperties.PixelFormat = d2dPixelFormat;
d2dBitmap = new SlimDX.Direct2D.Bitmap(d2dWindowRenderTarget, new Size(bitmap.Width, bitmap.Height), dataStream, bitmapData.Stride, d2dBitmapProperties);
bitmap.UnlockBits(bitmapData);
//Draw SlimDX.Direct2D.Bitmap
d2dWindowRenderTarget.DrawBitmap(d2dBitmap, new Rectangle(0, 0, bitmap.Width, bitmap.Height));/**/
d2dWindowRenderTarget.EndDraw();
//Dispose everything u dont need anymore.
//bitmap.Dispose();//......
So it is super simple to use Direct2D, all the code can be compressed to 2 main Lines + drawing:
SlimDX.Direct2D.Factory d2dFactory = new SlimDX.Direct2D.Factory(SlimDX.Direct2D.FactoryType.Multithreaded, SlimDX.Direct2D.DebugLevel.None);
SlimDX.Direct2D.WindowRenderTarget d2dWindowRenderTarget = new SlimDX.Direct2D.WindowRenderTarget(d2dFactory, new SlimDX.Direct2D.WindowRenderTargetProperties() { Handle = targetControl.Handle, PixelSize = targetControl.Size, PresentOptions = SlimDX.Direct2D.PresentOptions.Immediately });
d2dWindowRenderTarget.BeginDraw();
d2dWindowRenderTarget.Clear(new SlimDX.Color4(Color.LightSteelBlue));
d2dWindowRenderTarget.DrawRectangle(new SlimDX.Direct2D.SolidColorBrush(d2dWindowRenderTarget, new SlimDX.Color4(Color.Red)), new Rectangle(20,20, targetControl.Width-40, targetControl.Height-40));
d2dWindowRenderTarget.EndDraw();

Taking a screenshot using Graphics.CopyFromScreen with 150% scaling

I'm trying to create a screenshot/bitmap of my screen. I wrote this function:
public static Bitmap CreateScreenshot(Rectangle bounds)
{
var bmpScreenshot = new Bitmap(bounds.Width, bounds.Height,
PixelFormat.Format32bppArgb);
var gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(bounds.X, bounds.Y,
0, 0,
new Size(bounds.Size.Width, bounds.Size.Height),
CopyPixelOperation.SourceCopy);
return bmpScreenshot;
}
This function is being called in my overlay form that should draw the bitmap onto itself. I'm currently using GDI+ for the whole process.
private void ScreenshotOverlay_Load(object sender, EventArgs e)
{
foreach (Screen screen in Screen.AllScreens)
Size += screen.Bounds.Size;
Location = Screen.PrimaryScreen.Bounds.Location;
_screenshot = BitmapHelper.CreateScreenshot(new Rectangle(new Point(0, 0), Size));
Invalidate(); // The screenshot/bitmap is drawn here
}
Yep, I dispose the bitmap later, so don't worry. ;)
On my laptop and desktop computer this works fine. I've tested this with different resolutions and the calculations are correct. I can see an image of the screen on the form.
The problem starts with the Surface 3. All elements are being scaled by a factor of 1.5 (150%). This consequently means that the DPI changes. If I try to take a screenshot there, it does only capture like the upper-left part of the screen but not the whole one.
I've made my way through Google and StackOverflow and tried out different things:
Get the DPI, divide it by 96 and multiply the size components (X and Y) of the screen with this factor.
Add an entry to application.manifest to make the application DPI-aware.
The first way did not bring the desired result. The second one did, but the whole application would have to be adjusted then and this is quite complicated in Windows Forms.
Now my question would be: Is there any way to capture a screenshot of the whole screen, even if it is has a scalation factor higher than 1 (higher DPI)?
There must be a way to do this in order to make it working everywhere.
But at this point I had no real search results that could help me.
Thanks in advance.
Try this, which is found within SharpAVI's library. It works well on devices regardless of resolution scale. And I have tested it on Surface 3 at 150%.
System.Windows.Media.Matrix toDevice;
using (var source = new HwndSource(new HwndSourceParameters()))
{
toDevice = source.CompositionTarget.TransformToDevice;
}
screenWidth = (int)Math.Round(SystemParameters.PrimaryScreenWidth * toDevice.M11);
screenHeight = (int)Math.Round(SystemParameters.PrimaryScreenHeight * toDevice.M22);
SharpAVI can be found here: https://github.com/baSSiLL/SharpAvi It is for videos but uses a similar copyFromScreen method when getting each frame:
graphics.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(screenWidth, screenHeight));
Before taking your screen shot, you can make the process DPI aware:
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool SetProcessDPIAware();
private static Bitmap Screenshot()
{
SetProcessDPIAware();
var screen = System.Windows.Forms.Screen.PrimaryScreen;
var rect = screen.Bounds;
var size = rect.Size;
Bitmap bmpScreenshot = new Bitmap(size.Width, size.Height);
Graphics g = Graphics.FromImage(bmpScreenshot);
g.CopyFromScreen(0, 0, 0, 0, size);
return bmpScreenshot;
}

How can I take a picture from screen?

I want to write a program that shows one PC's screen to the others... something like presentation systems. how can i take a picture from current screen?
.NET exposes this functionality via the Screen (System.Windows.Forms) class.
// the surface that we are focusing upon
Rectangle rect = new Rectangle();
// capture all screens in Windows
foreach (Screen screen in Screen.AllScreens)
{
// increase the Rectangle to accommodate the bounds of all screens
rect = Rectangle.Union(rect, screen.Bounds);
}
// create a new image that will store the capture
Bitmap bitmap = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(bitmap))
{
// use GDI+ to copy the contents of the screen into the bitmap
g.CopyFromScreen(rect.X, rect.Y, 0, 0, rect.Size, CopyPixelOperation.SourceCopy);
}
// bitmap now has the screen capture

Categories

Resources