How can i capture the screen without the Form? - c#

This is a class i'm using to capture my screen and mouse cursour as screenshot.
But i want to make somehow that if the Form is in the middle of the screen don't capture it captrue the screen and the area behind the Form but not the Form it self.
Even if the form is in the front and i click on buttons or change something in the Form while the application is running do not capture it just keep capture the screen the area behind the Form like the Form is not there.
using System;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace ScreenShotDemo
{
public class ScreenCapture
{
[StructLayout(LayoutKind.Sequential)]
struct CURSORINFO
{
public Int32 cbSize;
public Int32 flags;
public IntPtr hCursor;
public POINTAPI ptScreenPos;
}
[StructLayout(LayoutKind.Sequential)]
struct POINTAPI
{
public int x;
public int y;
}
[DllImport("user32.dll")]
static extern bool GetCursorInfo(out CURSORINFO pci);
[DllImport("user32.dll")]
static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon);
const Int32 CURSOR_SHOWING = 0x00000001;
public static Bitmap CaptureScreen(bool CaptureMouse)
{
Bitmap result = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
try
{
using (Graphics g = Graphics.FromImage(result))
{
g.CopyFromScreen(0, 0, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
if (CaptureMouse)
{
CURSORINFO pci;
pci.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(CURSORINFO));
if (GetCursorInfo(out pci))
{
if (pci.flags == CURSOR_SHOWING)
{
DrawIcon(g.GetHdc(), pci.ptScreenPos.x, pci.ptScreenPos.y, pci.hCursor);
g.ReleaseHdc();
}
}
}
}
}
catch
{
result = null;
}
return result;
}
}
What i mean is that i will see the Form when it's running and i will be able to change things click buttons but the captured screenshot if i will edit it with Paint i will not see the Form .
This is in Form1 how i make the capture:
private void StartRecording_Click(object sender, EventArgs e)
{
timer1.Enabled = true;
}
And timer1 tick event:
private void timer1_Tick(object sender, EventArgs e)
{
using (bitmap = (Bitmap)ScreenCapture.CaptureScreen(true))
{
ffmp.PushFrame(bitmap);
}
}
This line make the actual capture: using (bitmap = (Bitmap)ScreenCapture.CaptureScreen(true))

Um.. Hide the form?
this.Visible = false; and THEN run the screenshot method.
Like this:
protected Bitmap TakeScreenshot(bool cursor)
{
Bitmap bitmap;
this.Visible = false;
bitmap = CaptureScreen(cursor);
this.Visible = true;
return bitmap;
}
and use it in your code the way you wanted:
private void timer1_Tick(object sender, EventArgs e)
{
using (bitmap = (Bitmap)ScreenCapture.TakeScreenshot(true))
{
ffmp.PushFrame(bitmap);
}
}

Related

c# color extractor frequently returning white

I have a program that has a small graphics portion to it, where you can look at and store the color of specific pixels of the screen.
The problem is that when I extract color, sometimes I get the color I want... sometimes I just get White, or some other random color (usually white, but I'm also using a white background to test with).
Can you spot any reason why that would be? I'm also totally open to possible improvements.
I have a picturebox called Canvas, and I can either click "Capture Image" which does a screenshot as follows:
private void CaptureImage_Click(object sender, EventArgs e)
{
this.Visible = false;
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap as Image);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
Canvas.SizeMode = PictureBoxSizeMode.AutoSize;
Canvas.Image = bitmap;
graphics.Dispose();
this.Visible = true;
}
Or I can load an image file, though for this test I'm just doing the capture.
When I right-click somewhere on the image, in my mousedown event I do:
if (e.Button == MouseButtons.Right)
{
pointPoint = e.Location;
Debug.WriteLine("right mouse: " + e.Location.ToString());
ContextMenuStrip cm = new ContextMenuStrip();
cm.Items.Add("Flag this as Point");
cm.Items.Add("Extract Color");
Canvas.ContextMenuStrip = cm;
cm.ItemClicked += new ToolStripItemClickedEventHandler(Cm2Click);
}
(pointPoint is a Point object)
As you can see, I verify the location where I clicked using the debug window. In my context menu, I also verify it again to make sure it didn't change before getting called. The context menu does:
private void Cm2Click(object sender, ToolStripItemClickedEventArgs e)
{
//var clickedMenuItem = sender as MenuItem;
ToolStripItem menuText = e.ClickedItem;
Debug.WriteLine(menuText);
if (menuText.ToString() == "Extract Color")
{
string input = "Default";
bool testBox = false;
do
{
if (InputBox.inputBox("ColorName", "You must provide a unique name for this color", ref input) == DialogResult.OK)
{
if (!String.IsNullOrWhiteSpace(input) && input != "Default")
{
Debug.WriteLine("location changed to: " + pointPoint);
colorExtractor = Functions.GetColorAt(pointPoint);
ColorData clr = new ColorData(input, colorExtractor);
string expression;
expression = $"Name = '{clr.Name}'";
DataRow[] foundRows;
DataTable dt = _allEventData.Tables["ColorData"];
foundRows = dt.Select(expression);
if (foundRows.Length > 0) {
testBox = false;
Debug.WriteLine("Color Found");
}
else {
dt.Rows.Add(clr.Name, clr.R, clr.G, clr.B);
dt.AcceptChanges();
testBox = true;
}
}
} else
{
return;
}
} while (testBox == false);
}
}
and ColorData is just a class I defined to help store data:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
namespace EventClicker
{
public class ColorData : EventArgs
{
public string Name { get; set; }
public int R { get; set; }
public int G { get; set; }
public int B { get; set; }
public bool Found { get; set; }
public ColorData(String name, Color color)
{
this.Name = name;
this.R = color.R;
this.G = color.G;
this.B = color.B;
}
public ColorData(String name, String r, String g, String b)
{
this.Name = name;
this.R = Int32.Parse(r);
this.G = Int32.Parse(g);
this.B = Int32.Parse(b);
}
}
}
Last but not least, the method I call (Functions.GetColorAt) is defined here (and I definitely stole this from someone on the web) Edit: That somewhere is here, How to read the Color of a Screen Pixel
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace EventClicker
{
public static class Functions
{
private static Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
public static Color GetColorAt(Point location)
{
using (Graphics gdest = Graphics.FromImage(screenPixel))
{
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
}
return screenPixel.GetPixel(0, 0);
}
}
}

GDI+ Game Engine (What can I do to make my engine run faster?) [duplicate]

This question already exists:
How to make my game engine faster (GDI+, C#)
Closed 6 years ago.
I'm currently working on a 2D Game Engine using GDI+. Everything is running quite smoothly, however I know there must be a few more things I can do to make my engine render graphics faster.
At the moment if I rendered an empty bitmap that's 800x600 pixels to the screen, I get about 130fps. But once I draw an image that's 800x600 pixels on the bitmap, I get about 70fps. That's not an issue at all, I'm actually proud of the results, I just know it can be better.
I was just wondering if anyone had any suggestions as to what might make the engine run even faster?
Here's the code to my window class:
using System;
using System.Windows.Forms;
using GameEngine.Graphics;
using System.Threading;
using System.Drawing;
namespace GameEngine.Core
{
public class Game : Form
{
// Just a class that checks the fps and such
private GameTime gameTime;
private GraphicsEngine graphicsEngine;
private bool isLoaded = false;
private bool isRunning = false;
public Game(int width, int height, string title)
{
this.Width = width;
this.Height = height;
this.Text = title;
this.MaximizeBox = false;
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.Paint += Game_Paint;
this.FormClosing += Game_FormClosing;
gameTime = new GameTime();
graphicsEngine = new GraphicsEngine(this);
Show();
}
public void Run()
{
Initialize();
Load();
isRunning = true;
while (isRunning && isLoaded)
{
// Yes I know, this is frowned upon
Application.DoEvents();
gameTime.Update();
// Only update if the form is focused and if the GameTime class says we're allowed
if (this.Focused && gameTime.Cycled)
{
BeginUpdate();
UpdateGame();
LateUpdate();
Render();
}
else
{
Thread.Sleep(1);
}
}
Exit();
}
public void Exit()
{
Unload();
this.Close();
// Using console application, so lets exit the console
Environment.Exit(0);
}
private void Initialize()
{
gameTime.Initialize();
}
private new void Load()
{
isLoaded = true;
}
private void Unload()
{
}
private void BeginUpdate()
{
}
private void UpdateGame()
{
this.Title = "FPS: " + gameTime.FPS;
}
private void LateUpdate()
{
}
Bitmap bmp = new Bitmap("Test.jpg");
private void Render()
{
// Draw the nice tree to the window
graphicsEngine.DrawBitmap(bmp, 0, 0);
// Cause the window to redraw it's self
this.Invalidate();
}
private void Game_Paint(object sender, PaintEventArgs e)
{
// Lets just make sure once again that we're allowed to draw
if (gameTime.Cycled)
{
// Render the graphics to the screen
graphicsEngine.Render(e.Graphics);
}
}
private void Game_FormClosing(object sender, FormClosingEventArgs e)
{
isLoaded = false;
}
public string Title
{
get { return Text; }
set { Text = value; }
}
}
}
and here's the graphics engine class:
using System.Drawing;
using GameEngine.Core;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
namespace GameEngine.Graphics
{
public class GraphicsEngine
{
private Game game;
private System.Drawing.Graphics backBuffer;
private System.Drawing.Bitmap backBufferBitmap;
public GraphicsEngine(Game game)
{
this.game = game;
backBufferBitmap = new Bitmap(800, 600);
backBuffer = System.Drawing.Graphics.FromImage(backBufferBitmap);
backBuffer.CompositingMode = CompositingMode.SourceOver;
backBuffer.CompositingQuality = CompositingQuality.HighSpeed;
backBuffer.SmoothingMode = SmoothingMode.None;
backBuffer.InterpolationMode = InterpolationMode.Low;
backBuffer.TextRenderingHint = TextRenderingHint.SystemDefault;
backBuffer.PixelOffsetMode = PixelOffsetMode.Half;
}
public void Render(System.Drawing.Graphics g)
{
g.CompositingMode = CompositingMode.SourceCopy;
g.CompositingQuality = CompositingQuality.AssumeLinear;
g.SmoothingMode = SmoothingMode.None;
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.TextRenderingHint = TextRenderingHint.SystemDefault;
g.PixelOffsetMode = PixelOffsetMode.HighSpeed;
g.DrawImage(backBufferBitmap, new Rectangle(0, 0, game.Width, game.Height), new Rectangle(0, 0, game.Width, game.Height), GraphicsUnit.Pixel);
backBuffer.Clear(Color.Black);
}
public void DrawString(string text, int x, int y)
{
backBuffer.DrawString(text, SystemFonts.DefaultFont, Brushes.White, x, y);
}
public void DrawBitmap(Bitmap bitmap, int x, int y)
{
backBuffer.DrawImage(bitmap, x, y);
}
public void DrawBitmap(Bitmap bitmap, int x, int y, int width, int height)
{
backBuffer.DrawImageUnscaled(bitmap, x, y, width, height);
}
}
}
You should try to operate a kind of Logical thread separated from your drawing thread. Maybe replace the thread sleeping in your logical by a (tick management).
As asked in comments below, the fact to separate UI & logic in different thread could help you to have a better flow in both tasks. The hard part is to keep UI and logic Synced, but you're less exposed to slow methods/function which would slow down UI.

Deleting JPEG Not Allowed(Used By Another Process)

Question Edited!! This is an example of compilabile files to help you find my problem!
I save some screenshots in a specific file.
When i use function DeleteFile() it should delete first the contents and then the folder but then i get "that icon.jpg is used by another process"!
(Either way to delete a folder it has to have no content!)
ImageExtensions.cs
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace name
{
static class ImageExtensions
{
public static void SaveToFile(Image image,string path,ImageFormat format)
{
using (var stream = File.OpenWrite(path))
{
image.Save(stream, format);
}
}
}
}
ScreenCapture.cs
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace name
{
public class ScreenCapture
{
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);
[StructLayout(LayoutKind.Sequential)]
private struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GetDesktopWindow();
public static Image CaptureDesktop()
{
return CaptureWindow(GetDesktopWindow());
}
public static Bitmap CaptureActiveWindow()
{
return CaptureWindow(GetForegroundWindow());
}
public static Bitmap CaptureWindow(IntPtr handle)
{
var rect = new Rect();
GetWindowRect(handle, ref rect);
var bounds = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
var result = new Bitmap(bounds.Width, bounds.Height);
using (var graphics = Graphics.FromImage(result))
{
graphics.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
}
return result;
}
}
}
AnotherFile.cs (Uses PrintScreen() )
public void PrintScreen()
{
using (var image = ScreenCapture.CaptureDesktop())
{
ImageExtensions.SaveToFile(image, (file_images + ".jpg"), ImageFormat.Jpeg);
}
}
Form1.cs (Main)
using ...
using ...
namespace name{
public partial class Form1 : Form
{
const int MaxRetries = 3;
const int DelayBetweenRetries = 1000;
const int ERROR_SHARING_VIOLATION = unchecked((int)0x80070020);
string file_images = my_path_of_images_file;
Public Form1()
{
InitializeComponent();
CreateFile();
}
private void Form1_Load(object sender, EventArgs e){}
public void CreateFile()
{
if (!Directory.Exists(file_images))
{
DirectoryInfo di = Directory.CreateDirectory(file_images);
}
}
public void DeleteFiles()
{
string[] filesToDelete = Directory.GetFiles(file_images);
if (filesToDelete != null)
{
var files = filesToDelete.Where(x => Path.GetExtension(x).Contains(".jpg"));
foreach (var file in files)
{
var temp = file;
DeleteFile(temp); // Delete JPEG
}
}
Directory.Delete(file_images); // Delete Folder
}
public static void DeleteFile(string path)
{
for (int i = 0; i < MaxRetries; ++i)
{
try
{
File.Delete(path);
}
catch (IOException e)
{
// You may also sleep whatever the error is...
if (Marshal.GetHRForException(e) == ERROR_SHARING_VIOLATION)
{
Thread.Sleep(DelayBetweenRetries);
continue;
}
throw;
}
}
}
} // End Of Main.cs
} // End Of Namespace
I have had this problem before and I solved it by copying the image data from the filestream into a memorystream, then loading the image from it. Similarly with the saving the image. This way, even if Image keeps a reference to the source or saved stream, it is not a physical file.
A more complete solution: https://siderite.dev/blog/imagefromstream-or-imagefromfile.html
try:
using (var ms=new MemoryStream()) {
image.Save(ms,format);
File.WriteAllBytes(path,ms.ToArray());
}
I wrote this inline, so tweak it if it doesn't work exactly.

Cursor doesn't hide while taking a screenshot

Simple question. I want to take a screenshot of a part of my screen without the mouse pointer in it. I tried this but it doesn't work.
private void button1_Click(object sender, EventArgs e)
{
Cursor.Hide();
gfxScreenshot.CopyFromScreen(//...);
Cursor.Show();
}
The code above was on a button_click event. I transferred the code in a timer_tick event except from the Cursor.Hide() and made the timer's interval 1000. The timer starts when the button is clicked.
private void button1_Click(object sender, EventArgs e)
{
Cursor.Hide();
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
gfxScreenshot.CopyFromScreen(//...);
Cursor.Show();
timer1.Stop();
}
It works this way but I have to wait 1 sec. When I reduce the interval to 100 the pointer is visible on the image.
I can only assume that the hide method is slower than the CopyFromScreen method...
Is there any way to make it work without the 1 sec delay??
Get cursor location, move to (0,0), take screenshot, put back cursor. The code works using APIs:
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Test1
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
public static extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll")]
public static extern bool GetCursorPos(out POINT lpPoint);
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public static implicit operator Point(POINT point)
{
return new Point(point.X, point.Y);
}
}
public Form1()
{
InitializeComponent();
POINT lpPoint;
//Get current location of cursor
GetCursorPos( out lpPoint );
//Move to (0,0)
SetCursorPos( 0, 0 );
//Take screenshot
//gfxScreenshot.CopyFromScreen(//...);
MessageBox.Show("just for create a delay", "Debug", MessageBoxButtons.OK, MessageBoxIcon.Information);
//Put back cursor
SetCursorPos( lpPoint.X, lpPoint.Y );
}
}
}

WinForms: Detect when cursor enters/leaves the form or its controls

I need a way of detecting when the cursor enters or leaves the form. Form.MouseEnter/MouseLeave doesn't work when controls fill the form, so I will also have to subscribe to MouseEnter event of the controls (e.g. panels on the form). Any other way of tracking form cursor entry/exit globally?
You can try this :
private void Form3_Load(object sender, EventArgs e)
{
MouseDetector m = new MouseDetector();
m.MouseMove += new MouseDetector.MouseMoveDLG(m_MouseMove);
}
void m_MouseMove(object sender, Point p)
{
Point pt = this.PointToClient(p);
this.Text = (this.ClientSize.Width >= pt.X &&
this.ClientSize.Height >= pt.Y &&
pt.X > 0 && pt.Y > 0)?"In":"Out";
}
The MouseDetector class :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
class MouseDetector
{
#region APIs
[DllImport("gdi32")]
public static extern uint GetPixel(IntPtr hDC, int XPos, int YPos);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool GetCursorPos(out POINT pt);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
#endregion
Timer tm = new Timer() {Interval = 10};
public delegate void MouseMoveDLG(object sender, Point p);
public event MouseMoveDLG MouseMove;
public MouseDetector()
{
tm.Tick += new EventHandler(tm_Tick); tm.Start();
}
void tm_Tick(object sender, EventArgs e)
{
POINT p;
GetCursorPos(out p);
if (MouseMove != null) MouseMove(this, new Point(p.X,p.Y));
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
X = x;
Y = y;
}
}
}
You can do it with win32 like in this answer:
How to detect if the mouse is inside the whole form and child controls in C#?
Or you could just hook up all the top level controls in OnLoad of the form:
foreach (Control control in this.Controls)
control.MouseEnter += new EventHandler(form_MouseEnter);

Categories

Resources