I am using this code to take screen shot every second with timer, but it seems to be filling up my memory (about 30 MB each take)... This is the code:
Bitmap bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
Graphics gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(136, 93, 9, 0, new Size(1088-391, 1039-65), CopyPixelOperation.SourceCopy);
return bmpScreenshot;
Is there a way to clear memory?
You need to dispose of things properly by calling Dispose or wrapping with the using() statement which calls it for you, for example:
Bitmap bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
using(Graphics gfxScreenshot = Graphics.FromImage(bmpScreenshot)) {
gfxScreenshot.CopyFromScreen(136, 93, 9, 0, new Size(1088-391, 1039-65), CopyPixelOperation.SourceCopy);
}
return bmpScreenshot;
You should also dispose of the returned Bitmap instance once you're done with it.
I met this problem, too.
var g = CreateGraphics();
using (var brush = new SolidBrush(Color.Red))
{
Point point = new Point(0,0);
Rectangle sourceRect = new Rectangle(100,100,100,100);
var invoker = new MethodInvoker(() =>
{
// The following line will filling up the memory.
g.CopyFromScreen(new Point(0,0), sourceRect.Location, sourceRect.Size);
});
do
{
BeginInvoke(invoker);
} while (!mre.WaitOne(1));
}
It means the 'CopyFromScreen' method has some things that are not disposed.
But I have no idea how to dispose them.
Even through 'Diagnositics Tools' of VS2017, I can see the heap usage and point to ExecutionContext -> Queue, the Queue is going larger and larger, and mostly, the size of each item in the Queue is 44 bytes. Of cause, GC will collect them after a while, but it still grow up quickly.
Bitmap and Graphics implement IDisposable interface. To free unmanaged resources you should call Dispose method or wrap both objects with using statement.
Related
I am trying to make screenshots of my screen very fast.
So, I use this code in my main class
[STAThread]
static void Main(string[] args)
{
int x = 1;
int screenshotsAmount = 0;
List<Bitmap> screenshots = new List<Bitmap>();
while (x == 1)
{
screenshots.Add(FullsizeScreenshot.makeScreenshot());
Clipboard.SetImage(screenshots[screenshotsAmount]);
Console.WriteLine("Screenshot " + screenshotsAmount + " has been made and added to the Bitmap list!");
screenshotsAmount++;
}
}
And I have a method in my .dll, that creates screenshots
// Class for making standard screenshots
public struct FullsizeScreenshot
{
// Making fullscreen screenshot
public static Bitmap makeScreenshot()
{
Bitmap screenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
Graphics gfxScreenshot = Graphics.FromImage(screenshot);
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
gfxScreenshot.Dispose();
return screenshot;
}
}
Everything works correct, but when the number of screenshots becomes bigger than 109, my program crash with System.ArgumentException
An unhandled exception of type "System.ArgumentException" in System.Drawing.dll
Additional information: Invalid parameter.
This line throws it:
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
or this:
Bitmap screenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
I tried to use using (Bitmap screenshot....) and .Dispose(), but it doesn't work correct, because these classes (Bitmap and Graphics) are classes and they just create links instead of making copies. As a result, when I dispose the Bitmap in makeScreenshot() it broke the Bitmap in my List.
So, what should I do? Maybe I should make a copy, but I don't know how.
Suppose you have a display of 1920x1080, that's 2,073,600 pixels, there are 4 bytes per pixel in a PixelFormat.Format32bppArgb So that's 8,294,400 bytes, or around 8 MB. 109 images would be 872 MB. Surprised that's crashing there, but you get the idea, it's too much memory.
If you want to make an animated gif, think how large that's going to be, full screen? Well I hope you're not planing on that, that's not practical for a gif. After you take the screenshot, resize it down immediately to the target resolution so it takes less memory.
I'm trying to capture screenshots as long as the user interrupts, but I've gone far to get a single screenshot and save it using a hard-coded filename. It goes like this:
Bitmap 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);
bmpScreenshot.Save("Screenshot.jpeg", ImageFormat.Jpeg);
The problem is this enables me to take a single screenshot, and I need multiple screenshots to be saved at the same time. I can't find a suitable way to do this without hard-coding and jeopardizing the code at the same time. I would really appreciate help on this.
Thanks.
The taking a screenshot part is probably fast and needs to happen no matter what.
You can take many screenshots and save them later. You can use a ConcurrentQueue to do that:
Bitmap bmpScreenshot = Bitmap 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);
_queue.Enqueue(bmpScreenshot);
And then have a thread that dequeues and saves:
index = 0;
while (_queue.Count > 0)
{
Bitmap bitmap = null;
if (_queue.TryDequeue(out bitmap))
{
bitmap.Save("Screenshot" + index + ".jpeg", ImageFormat.Jpeg);
index++;
}
}
An even more robust solution would be to use TPL Dataflow, but that may be an overkill.
I had made an app in C# that will perform screen capture continuously and display it in a PictureBox using timer. After running for a few seconds, there was an ArgumentException.
Below is the code and the line that has the ArgumentException
private void timer1_Tick(object sender, EventArgs e)
{
Rectangle bounds = Screen.GetBounds(Point.Empty);
Graphics graphics;
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
using (graphics = Graphics.FromImage(bitmap))
{
graphics.CopyFromScreen(0, 0, 0, 0, new Size(bounds.Width , bounds.Height )); // ArgumentException
pictureBox1.Image = bitmap;
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
}
}
Besides that, I had notices that an alert saying low memory from Windows after running the app for a few seconds.
Any tips on resolving this problem?
You keep setting a new bitmap to the picturebox, and the previous bitmap is never disposed. After a while, the system runs short of GDI handles and/or memory (running your code, I consumed one gig of memory in under 15 seconds).
You can simply reuse your existing bitmap:
Rectangle bounds = Screen.GetBounds(Point.Empty);
Image bitmap = pictureBox1.Image ?? new Bitmap(bounds.Width, bounds.Height);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.CopyFromScreen(0, 0, 0, 0, new Size(bounds.Width, bounds.Height));
if (pictureBox1.Image == null)
{
pictureBox1.Image = bitmap;
}
else
{
pictureBox1.Refresh();
}
}
You also don't have to reset pictureBox1.SizeMode on each iteration.
Alternatively, you can dispose the previous bitmap manually:
Rectangle bounds = Screen.GetBounds(Point.Empty);
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.CopyFromScreen(0, 0, 0, 0, new Size(bounds.Width, bounds.Height));
using (Image prev_bitmap = pictureBox1.Image)
{
pictureBox1.Image = bitmap;
}
}
pictureBox1.Image = bitmap;
Yes, your program won't last long when you frequently update the picture box. The Bitmap class is the singular .NET class where IDisposable can't easily be ignored. It is like an iceberg, bitmaps can use massive amounts of unmanaged memory but very little managed memory. You must dispose bitmaps when you no longer use them to prevent them from consuming all available unmanaged memory for their pixel data. The garbage collector tends to hide that problem but it can't do so when it doesn't run frequently enough. And the managed portion of Bitmap is too small to trigger a collection often enough. Fix:
if (pictureBox1.Image != null) pictureBox1.Image.Dispose();
pictureBox1.Image = bitmap;
The following KB can help understanding this issue:
Bitmap and Image constructor dependencies
GDI+, and therefore the System.Drawing namespace, may defer the
decoding of raw image bits until the bits are required by the image.
Additionally, even after the image has been decoded, GDI+ may
determine that it is more efficient to discard the memory for a large
Bitmap and to re-decode later. Therefore, GDI+ must have access to the
source bits for the image for the life of the Bitmap or the Image
object.
To retain access to the source bits, GDI+ locks any source file, and
forces the application to maintain the life of any source stream, for
the life of the Bitmap or the Image object.
One has to figure out when is the object ready for disposal.
For my program I need a method which takes a screenshot every half a minute. I googled and came up with this method:
public static Bitmap CaptureScreen()
{
Bitmap BMP = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Graphics GFX = Graphics.FromImage(BMP);
GFX.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Seen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
return BMP;
}
Well all works fine for the first 200 uses of the method or so. Then the function crashes at CopyFromScreen and it says that it caused an invalid argument exception. I'm slightly confused why that is because the parameters don't change.
Could it be that the function itself just has a bug? If so are there any alternatives to take a screenshot?
Probably two separate failures to dispose. Both Graphics and Image / Bitmap implement IDisposable, so the "obvious" of the two is here:
using(Graphics GFX = Graphics.FromImage(BMP)) {
GFX.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,Seen.PrimaryScreen.Bounds.Y,
0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
}
return BMP;
However: the caller of your method should also be using the result of CaptureScreen (to release the bitmap's GDI+ handle), i.e.
using(var screen = CaptureScreen()) {
// some work here
}
I have a image where I am doing resizing,drawString and FillEllipse.
There are many points(FillEllipse) that needs to shown n bitmap, so I am using for loop.
Here is the code:
using (System.Drawing.Graphics Gfx = System.Drawing.Graphics.FromImage(OrginalBitmap))
{
Gfx.SmoothingMode = SmoothingMode.HighQuality;
Gfx.CompositingQuality = CompositingQuality.HighQuality;
Gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
Gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;
foreach (var points in SelectedPoints)
{
Gfx.FillEllipse(
Brushes.Yellow,new Rectangle(points.X , points.Y, 8, 8));
Gfx.DrawString("M", new Font("Arial",8),
Brushes.Yellow, points.X, points.Y);
//points.X and points.X are the points that needs to be drawn on bitmap(particular location).
}
}
((IDisposable)OrginalBitmap).Dispose;
Loading of drawn bitmap takes very long time if there are many points in SelectedPoints.
Performance had drastically come down and loading takes too much memory.
Please let me know what to do.
Thanks in advance.
Drawing just 200 points should really not cause any performance problems, even at the highest quality setting. Using you code, I can draw around 40000 points in one second on my system.
Assuming that SelectedPoints is Point[] or List<Point> or some other efficient type, I would suspect the FontFacade.Large call. Is a new Font instance created each time around?
EDIT:
Running your modified code using new Font("Arial", 8) on 200 points takes around 20 milliseconds on my system, so there have to be something else that is causing your problems. How long does it take to run the code on your system?
Stopwatch timer = Stopwatch.StartNew();
[...]
Debug.WriteLine(timer.ElapsedMilliseconds);
The created font objects should be disposed when done, I would also move it outside the loop so that only one instance is created although that does not seem to be the source of your problems.
using(Font font = new Font("Arial", 8))
{
foreach(var point = SelectedPoints)
{
[...]
}
}
What dimensions is the OriginalBitmap, and what is it's PixelFormat?
What type is SelectedPoints?