Every second, I capture my screen with the following code. The first 40/50 times work, after that I get an InvalidArgumentException at the first and third line of code.
Bitmap bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics g = Graphics.FromImage(bmpScreenshot);
g.CopyFromScreen(0, 0, 0, 0, Screen.PrimaryScreen.Bounds.Size);
bmpScreen = bmpScreenshot;
Probably you need to dispose of some objects.
It's hard to tell from just the code shown, but my guess would be that you're not properly disposing of objects and are running out of memory. I know for sure that Graphics objects should be disposed, and the bitmap likely needs to be disposed of as well when you're done with it. Depending on how your error catching is set up, if you swallow an out of memory exception and keep going then new objects that don't fit in the available memory will not be instantiated and their constructors will return null. If you then pass null to a method that doesn't want to receive null, an InvalidArgumentException is likely to result.
Try wrapping your Disposable objects in using statements. I was able to repro your problem using the code below:
public static void Main()
{
var i = 1;
while (true)
{
var screenSize = Screen.PrimaryScreen.Bounds.Size;
try
{
var bmpScreenshot = new Bitmap(screenSize.Width, screenSize.Height);
var g = Graphics.FromImage(bmpScreenshot);
g.CopyFromScreen(0, 0, 0, 0, screenSize);
}
catch (Exception e)
{
Console.WriteLine("Exception ignored: {0}", e.Message);
}
finally
{
Console.WriteLine("Iteration #{0}", i++);
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
}
By wrapping the disposables with using statements, the issue did not happen again:
public static void Main()
{
var i = 1;
while (true)
{
var screenSize = Screen.PrimaryScreen.Bounds.Size;
try
{
using (var bmpScreenshot = new Bitmap(screenSize.Width, screenSize.Height))
using (var g = Graphics.FromImage(bmpScreenshot))
{
g.CopyFromScreen(0, 0, 0, 0, screenSize);
}
}
catch (Exception e)
{
Console.WriteLine("Exception ignored: {0}", e.Message);
}
finally
{
Console.WriteLine("Iteration #{0}", i++);
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
}
Related
I make screenshot of a window in a infinite loop
it works good.
But I hope it can be improved.
When i run my project, it has a litter delay to copy "Myscreen" from "PictureBox".
Anyone have a idea?
Or did you know any other solutions?
I just want to get Screen's BITMAP constantly.(faster..)
Below source is screen capture function.
public static Bitmap ScreenCapture_B(System.Windows.Forms.Screen MyScreen)
{
try
{
Rectangle rect = MyScreen.Bounds;
// 2nd screen = Screen.AllScreens[1]
int bitsPerPixel = MyScreen.BitsPerPixel;
PixelFormat pixelFormat = PixelFormat.Format32bppArgb;
if (bitsPerPixel <= 16)
{
pixelFormat = PixelFormat.Format16bppRgb565;
}
if (bitsPerPixel == 24)
{
pixelFormat = PixelFormat.Format24bppRgb;
}
else
{
pixelFormat = PixelFormat.Format24bppRgb;
}
Bitmap bmp = new Bitmap(rect.Width, rect.Height, pixelFormat);
using (Graphics gr = Graphics.FromImage(bmp))
{
// 화면을 그대로 카피해서 Bitmap 메모리에 저장
gr.CopyFromScreen(rect.Left, rect.Top, 0, 0, rect.Size);
}
return bmp;
}
catch(Exception)
{
throw;
}
}
Bellow source is Thread.
public void ThreadLoop()
{
try
{
//infinite loop
while (true)
{
bm = null;
Screen MyScreen = Screen.PrimaryScreen;
bm = ScreenCapture_B(MyScreen);
setImage(bm, pictureEdit1);
Bitmap btimage = new Bitmap(pictureEdit1.Image);
ProcessImage_new(btimage);
}
}
catch (Exception ex)
{
throw;
}
}
I have a function which draws a list of objects onto a bitmap to create a crude map. Code analysis throws a warning (CA2000) which says the object 'drawPen' isn't disposed along all exception paths. As far as I can see it is disposed at the end of the function and there is no inaccessible code where it could be missed.
Does anyone know why the compiler thinks it is not being disposed of properly?
public void drawUpdates(List<areaObjects> objectLocations)
{
Rectangle areaToClone = new Rectangle(0, 0, writeOnceMap.Width, writeOnceMap.Height);
var pixelFormat = writeOnceMap.PixelFormat;
areaBitMap = writeOnceMap.Clone(areaToClone, pixelFormat);
Pen drawPen = new Pen(Color.Black, 2);
drawPen.Width = 2;
foreach(areaObjectsop2d in objectLocations)
{
int xPosition = (int)(op2d.XYZ.xPos * mapScale);
int yPosition = (int)(op2d.XYZ.yPos * mapScale);
Point[] crossMarker = getCrossShape(xPosition, yPosition);
using (var graphics = Graphics.FromImage(areaBitMap))
{
graphics.DrawPolygon(drawPen, crossMarker);
}
}
drawPen.Dispose();
}
You get the warning because in the case of an exception in your function the drawPen will not be disposed.
You can wrapp your code in a try finally and in the finally call the .Dispose() or better - use the using which does precisely that.
public void drawUpdates(List<areaObjects> objectLocations)
{
Rectangle areaToClone = new Rectangle(0, 0, writeOnceMap.Width, writeOnceMap.Height);
var pixelFormat = writeOnceMap.PixelFormat;
areaBitMap = writeOnceMap.Clone(areaToClone, pixelFormat);
using(Pen drawPen = new Pen(Color.Black, 2))
{
foreach(areaObjectsop2d in objectLocations)
{
int xPosition = (int)(op2d.XYZ.xPos * mapScale);
int yPosition = (int)(op2d.XYZ.yPos * mapScale);
Point[] crossMarker = getCrossShape(xPosition, yPosition);
using (var graphics = Graphics.FromImage(areaBitMap))
{
graphics.DrawPolygon(drawPen, crossMarker);
}
}
}
}
The above is equivalent to:
Pen drawPen = new Pen(Color.Black, 2);
try
{
/*Your code*/
}
finally
{
drawPen.Dispose();
}
Imagine what happens if the code between creating the pen and disposing it throws an exception. The pen will not be disposed. The compiler warns you to make sure that the pen will get disposed even when an exception occurs. You have two ways to do so: using and try...finally (which is in fact the implementation of using).
using (Pen drawPen = ...)
{
} // now the code makes sure it gets disposed
I have next function (makes screenshot)
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
private Screen SavedScreen { get; } = Screen.PrimaryScreen;
private BitmapSource CopyScreen()
{
try
{
BitmapSource result;
using (
var screenBmp = new Bitmap(SavedScreen.Bounds.Width, SavedScreen.Bounds.Height, PixelFormat.Format32bppArgb))
{
using (Graphics bmpGraphics = Graphics.FromImage(screenBmp))
{
bmpGraphics.CopyFromScreen(SavedScreen.Bounds.X, SavedScreen.Bounds.Y, 0, 0, screenBmp.Size,
CopyPixelOperation.SourceCopy);
IntPtr hBitmap = screenBmp.GetHbitmap();
//********** Next line do memory leak
result = Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
DeleteObject(hBitmap);
}
}
return result;
}
catch (Exception ex)
{
//ErrorReporting ($"Error in CopyScreen(): {ex}");
Debugger.Break();
return null;
}
}
And cannot avoid memory leak which is a result of calling Imaging.CreateBitmapSourceFromHBitmap. As I call this function in a cycle this memory leak is very important for me.
Called in WPF application (Windows, c#)
As you already know, you have to Dispose() screenBmp.
You are actually calling it by an using statement, so that should be fine, but I suspect the try/catch could interfere.
Do you have a chance to move the try/catch so that only the CopyFromScreen and CreateBitmapSourceFromHBitmap are surrounded?
From comments
Since only after that closing brace of the using statement you are sure that the screenBmp can be disposed, I'm forcing a GC collect there
GC.Collect();
return result;
and it doesn't seem leaking.
Here is my demo
class Program
{
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
private static Screen SavedScreen { get; } = Screen.PrimaryScreen;
private static BitmapSource CopyScreen()
{
//try
//{
BitmapSource result;
using (
var screenBmp = new Bitmap(200, 100))
{
using (Graphics bmpGraphics = Graphics.FromImage(screenBmp))
{
bmpGraphics.CopyFromScreen(SavedScreen.Bounds.X, SavedScreen.Bounds.Y, 0, 0, screenBmp.Size,
CopyPixelOperation.SourceCopy);
IntPtr hBitmap = screenBmp.GetHbitmap();
bmpGraphics.Dispose();
//********** Next line do memory leak
result = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
DeleteObject(hBitmap);
//result = null;
}
}
GC.Collect();
return result;
//}
//catch (Exception ex)
//{
// //ErrorReporting ($"Error in CopyScreen(): {ex}");
// Console.WriteLine(ex.Message);
// Debugger.Break();
// return null;
//}
}
static void Main(string[] args)
{
for (int i = 0; i < 100000; i++)
{
Thread.Sleep(100);
var test = CopyScreen();
}
}
}
As you are working with bitmaps (screen size) it means expected data size is bigger than 85000 bytes. The objects of such sizes are treated differently by GC.
It is called LOH. See https://blogs.msdn.microsoft.com/maoni/2016/05/31/large-object-heap-uncovered-from-an-old-msdn-article/, it was improved in 4.5 https://blogs.msdn.microsoft.com/dotnet/2011/10/03/large-object-heap-improvements-in-net-4-5/
But the problem is still here. Accounting huge objects with high frequency leads to significant increase of memory usage of your application. There're 2 problem leads to it: 1) GC does not work immediatly, it takes time before it started freeing memory; 2) fragmentation of LOH (see the first article), this is why it is not freed and this is why you can see the memory usage is increased.
Possible solutions:
1) Use server GC and concurent GC; force GC manually. Most likely it does not help greatly.
2) Re-use existing object(allocated memory) instead of creating new Bitmap and Graphics all the time in a loop.
3) Switch to use Windows API directly and handle allocations manually.
I have a problem, and i don't know how resolved.. I'm using the drive twain to do scanner some documents, and i don't get create a new BitMap when i'm scanning a big number of the files.. So.. i need help to solution this.. my code is:
public static Bitmap FromHDib(IntPtr dibPtrArg, out ServiceError error)
{
error = null;
try
{
Win32.BITMAPINFOHEADER bmi;
IntPtr pixptr;
var ptr = Win32.GlobalLock(dibPtrArg);
GetPixelInfo(ptr, out pixptr, out bmi);
Bitmap bitMap = new Bitmap(bmi.biWidth, bmi.biHeight);
Graphics scannedImageGraphics = Graphics.FromImage(bitMap);
**IntPtr hdc = scannedImageGraphics.GetHdc();** //parameter is not valid
SetDIBitsToDevice(
hdc,
0, // XDest
0, // YDest
bmi.biWidth,
bmi.biHeight,
0, // XSrc
0, // YSrc
0, // uStartScan
bmi.biHeight, // cScanLines
pixptr, // lpvBits
ptr, // lpbmi
0); // 0 = literal RGB values rather than palette
scannedImageGraphics.ReleaseHdc(hdc);
const float inchPerMeter = 39.3700787F;
bitMap.SetResolution(bmi.biXPelsPerMeter / inchPerMeter, bmi.biYPelsPerMeter / inchPerMeter);
Win32.GlobalUnlock(dibPtrArg);
Win32.GlobalFree(dibPtrArg);
Win32.DeleteDC(hdc);
bitMap.Dispose();
return bitMap;
}
catch (OutOfMemoryException memoryEx)
{
return new Bitmap(0, 0);
}
catch (ArgumentException argEx)
{
return new Bitmap(0, 0);
}
}
I need to draw an image pixel by pixel and display it inside a WPF. I am attempting to do this by using a System.Drawing.Bitmap then using CreateBitmapSourceFromHBitmap() to create a BitmapSource for a WPF Image control. I have a memory leak somewhere because when the CreateBitmapSourceFromBitmap() is called repeatedly the memory usage goes up and does not drop off until the application is ended. If I don't call CreateBitmapSourceFromBitmap() there is no noticeable change in memory usage.
for (int i = 0; i < 100; i++)
{
var bmp = new System.Drawing.Bitmap(1000, 1000);
var source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
source = null;
bmp.Dispose();
bmp = null;
}
What can I do to free the BitmapSource memory?
MSDN for Bitmap.GetHbitmap() states:
Remarks
You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object.
So use the following code:
// at class level
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
// your code
using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(1000, 1000))
{
IntPtr hBitmap = bmp.GetHbitmap();
try
{
var source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(hBitmap);
}
}
I also replaced your Dispose() call by an using statement.
Whenever dealing with unmanaged handles it can be a good idea to use the "safe handle" wrappers:
public class SafeHBitmapHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[SecurityCritical]
public SafeHBitmapHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle)
{
SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle()
{
return GdiNative.DeleteObject(handle) > 0;
}
}
Construct one like so as soon as you surface a handle (ideally your APIs would never expose IntPtr, they would always return safe handles):
IntPtr hbitmap = bitmap.GetHbitmap();
var handle = new SafeHBitmapHandle(hbitmap , true);
And use it like so:
using (handle)
{
... Imaging.CreateBitmapSourceFromHBitmap(handle.DangerousGetHandle(), ...)
}
The SafeHandle base gives you an automatic disposable/finalizer pattern, all you need to do is override the ReleaseHandle method.
I had the same requirement and issue (memory leak). I implemented the same solution as marked as answer. But although the solution works, it caused an unacceptable hit to performance. Running on a i7, my test app saw a steady 30-40% CPU, 200-400MB RAM increases and the garbage collector was running almost every millisecond.
Since I'm doing video processing, I'm in need of much better performance. I came up with the following, so thought I would share.
Reusable Global Objects
//set up your Bitmap and WritableBitmap as you see fit
Bitmap colorBitmap = new Bitmap(..);
WriteableBitmap colorWB = new WriteableBitmap(..);
//choose appropriate bytes as per your pixel format, I'll cheat here an just pick 4
int bytesPerPixel = 4;
//rectangles will be used to identify what bits change
Rectangle colorBitmapRectangle = new Rectangle(0, 0, colorBitmap.Width, colorBitmap.Height);
Int32Rect colorBitmapInt32Rect = new Int32Rect(0, 0, colorWB.PixelWidth, colorWB.PixelHeight);
Conversion Code
private void ConvertBitmapToWritableBitmap()
{
BitmapData data = colorBitmap.LockBits(colorBitmapRectangle, ImageLockMode.WriteOnly, colorBitmap.PixelFormat);
colorWB.WritePixels(colorBitmapInt32Rect, data.Scan0, data.Width * data.Height * bytesPerPixel, data.Stride);
colorBitmap.UnlockBits(data);
}
Implementation Example
//do stuff to your bitmap
ConvertBitmapToWritableBitmap();
Image.Source = colorWB;
The result is a steady 10-13% CPU, 70-150MB RAM, and the garbage collector only ran twice in a 6 minute run.
This is a great(!!) post, although with all the comments and suggestions, it took me an hour to figure out the pieces. So here is a call to get the BitMapSource with SafeHandles, and then an example usage of it to create a .PNG image file. At the very bottom are the 'usings' and some of the references. Of course, none of the credit is mine, I am just the scribe.
private static BitmapSource CopyScreen()
{
var left = Screen.AllScreens.Min(screen => screen.Bounds.X);
var top = Screen.AllScreens.Min(screen => screen.Bounds.Y);
var right = Screen.AllScreens.Max(screen => screen.Bounds.X + screen.Bounds.Width);
var bottom = Screen.AllScreens.Max(screen => screen.Bounds.Y + screen.Bounds.Height);
var width = right - left;
var height = bottom - top;
using (var screenBmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
BitmapSource bms = null;
using (var bmpGraphics = Graphics.FromImage(screenBmp))
{
IntPtr hBitmap = new IntPtr();
var handleBitmap = new SafeHBitmapHandle(hBitmap, true);
try
{
bmpGraphics.CopyFromScreen(left, top, 0, 0, new System.Drawing.Size(width, height));
hBitmap = screenBmp.GetHbitmap();
using (handleBitmap)
{
bms = Imaging.CreateBitmapSourceFromHBitmap(
hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
} // using
return bms;
}
catch (Exception ex)
{
throw new ApplicationException($"Cannot CopyFromScreen. Err={ex}");
}
} // using bmpGraphics
} // using screen bitmap
} // method CopyScreen
Here is the usage, and also the "Safe Handle" class:
private void buttonTestScreenCapture_Click(object sender, EventArgs e)
{
try
{
BitmapSource bms = CopyScreen();
BitmapFrame bmf = BitmapFrame.Create(bms);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(bmf);
string filepath = #"e:\(test)\test.png";
using (Stream stm = File.Create(filepath))
{
encoder.Save(stm);
}
}
catch (Exception ex)
{
MessageBox.Show($"Err={ex}");
}
}
public class SafeHBitmapHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern int DeleteObject(IntPtr hObject);
[SecurityCritical]
public SafeHBitmapHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle)
{
SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle()
{
return DeleteObject(handle) > 0;
}
}
And finally a look at my 'usings':
using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Media.Imaging;
using System.Windows.Interop;
using System.Windows;
using System.IO;
using Microsoft.Win32.SafeHandles;
using System.Security;
The DLLs referenced included:
* PresentationCore
* System.Core
* System.Deployment
* System.Drawing
* WindowsBase
In my case it did not work directly with this method. I had to add a clean garbage collector in addition
using (PaintMap p = new PaintMap())
{
System.Drawing.Image i = p.AddLineToMap("1");
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(i, 8419, 5953);
IntPtr hBitmap = bmp.GetHbitmap();
var bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
Image2.ImageSource = bitmapSource;
DeleteObject(hBitmap);
System.GC.Collect();
}
I have a solution for someone who want to load image from memory or other class
public static InteropBitmap Bitmap2BitmapImage(Bitmap bitmap)
{
try
{
var source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
return (InteropBitmap)source;
}
catch (Exception e)
{
MessageBox.Show("Convertion exception: " + e.Message + "\n" +e.StackTrace);
return null;
}
}
And then I use it the set the source of an image
CurrentImage.Source = ImageConverter.Bitmap2BitmapImage(cam.Bitmap);
Image is the following definition
<Image x:Name="CurrentImage" Margin="5" StretchDirection="Both"
Width="{Binding Width}"
Height="{Binding Height}">
</Image>