C# Get image source from project resources creates memory leak - c#

Ok, in my project resources i have some images (.png). Whenever the user clicks on a Button a new image will be shown in an ImageBox. Because all of my images are stored in my project resources i have to get the Image.Source by code. I managed to do it by using a Method like this :
public void ImageSource()
{
Bitmap someImage;
BitmapSource someImageSource;
someImage= new Bitmap(Properties.Resources.Image1);
someImageSource = getBitmapSourceFromBitmap(someImage);
ImageBox.Source = someImageSource;
}
public static BitmapSource getBitmapSourceFromBitmap(Bitmap bmp)
{
BitmapSource returnSource = null;
try
{
returnSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
}
catch { returnSource = null; }
return returnSource;
}
In my app everything works fine. No errors , no warnings, images change fine every time i push the Button. After some monitoring in the memory , i noticed that every time i call the getBitmapSourceFromBitmap my memory explodes 100MB EVERY TIME.
Does anyone have any idea why is this happening?
Sorry for my English.

You are using unmanaged resources to create bitmap from your image. GC doesn't takes care of memory allocated to unmanaged resources. You need to dispose of your memory that you are using in that method.
After you have converted your image to bitmap you need to release all the unmanaged resources.
my memory explodes 100MB EVERY TIME
As a general statement if your memory increases by 100MB doesn't means that you have a memory leak. In case of managed resources GC will collect all the memory that is collectible whenever it will feel feasible.
Your Problem:
As stated in the MSDN
You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object
You need to change your code like this
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
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)
}
}

Related

Explorer Wont Release Files After Using My Thumbnail Provider

I've set up a thumbnail provider for a file type.
The project is made with
C#
.NET 4.5
And I am running Windows x64
My provider successfully generates the thumbnail as expected and I can move, delete, copy, ect, the file. The issue of the locking seems to be caused by the file being placed in a folder. At that point, moving, deleting, ect, the folder all show the error, "File In Use".
I have confirmed Explore locking the file using Sysinternal's Process Explorer, if you are familiar with it.
I've tried 2 approaches to try to resolve this...
implemented IThumbnailProvider and IInitializeWithStream myself.
used 3rd party Sharpshell
Both suffer from this same issue, the file not being released.
On Sharpshell's github, an issue has been started specifying this too.
https://github.com/dwmkerr/sharpshell/issues/78
I associate the file type in the registry like so
HKEY_CLASSES_ROOT
---- .qb
----shellex
----{e357fccd-a995-4576-b01f-234630154e96} : my CLSID...
I have also tried instead...
HKEY_CLASSES_ROOT
---- .qb
-----PersistentHandler : my CLSID...
Both result in this issue being created.
If I was to implement IExtractImage instead... will I see the same issue?
I know C# isn't "officially" supported to do this, is that where my issue lies? If I was to implement this in C++ would I wind up with the same issue?
EDIT:
I'd like to mention the file after around 1 minute seems to get freed, and things go back to normal.
Thumbnail Creation
Some bytes are read into a buffer... then then image is generated from that.
public void GetThumbnail(int cx, out IntPtr hBitmap, out WTS_ALPHATYPE bitmapType)
{
... bunch of other code
using (MemoryStream steam = new MemoryStream(buffer))
using (var image = new Bitmap(steam))
using (var scaled = new Bitmap(image, cx, cx))
{
hBitmap = scaled.GetHbitmap();
hBitmap = (IntPtr)(hBitmap.ToInt64());
}
}
EDIT 2:
Doing some more testing, I called DeleteObject(hBitmap), (even though this destroys the thumbnail), and the file is still locked. I even removed all the code from GetThumbnail... just gives the same result, file locked. There has to be something more going on?
Turns out you need to release the COM IStream object that you get from IInitializeWithStream.
I came to this conclusion by reading more about disposing COM objects.
Proper way of releasing COM objects?
I followed MS's example on how to wrap IStream
https://msdn.microsoft.com/en-us/library/jj200585%28v=vs.85%29.aspx
public class StreamWrapper : Stream
{
private IStream m_stream;
// initialize the wrapper with the COM IStream
public StreamWrapper(IStream stream)
{
if (stream == null)
{
throw new ArgumentNullException();
}
m_stream = stream;
}
// .... bunch of other code
protected override void Dispose(bool disposing)
{
if (m_stream != null)
{
Marshal.ReleaseComObject(m_stream); // releases the file
m_stream = null;
}
}
}
Here's a sample.
Follow the link above to see StreamWrapper's implementation...
[ComVisible(true), ClassInterface(ClassInterfaceType.None)]
[ProgId("mythumbnailer.provider"), Guid("insert-your-guid-here")]
public class QBThumbnailProvider : IThumbnailProvider, IInitializeWithStream
{
#region IInitializeWithStream
private StreamWrapper stream{ get; set; }
public void Initialize(IStream stream, int grfMode)
{
// IStream passed to our wrapper which handles our clean up
this.stream = new StreamWrapper(stream);
}
#endregion
#region IThumbnailProvider
public void GetThumbnail(int cx, out IntPtr hBitmap, out WTS_ALPHATYPE bitmapType)
{
hBitmap = IntPtr.Zero;
bitmapType = WTS_ALPHATYPE.WTSAT_ARGB;
try
{
//... bunch of other code
// set the hBitmap somehow
using (MemoryStream stream = new MemoryStream(buffer))
using (var image = new Bitmap(stream))
using (var scaled = new Bitmap(image, cx, cx))
{
hBitmap = scaled.GetHbitmap();
}
}
catch (Exception ex)
{
}
// release the IStream COM object
stream.Dispose();
}
#endregion
}
Basically it comes down to two lines of code
Marshal.ReleaseComObject(your_istream); // releases the file
your_istream = null;
Side Note
The GDI Bitmap created with scaled.GetHbitmap(); probably needs to disposed of, but I can't find a way to do it without loosing the created thumbnail.

Unusual Physical memory usage when huge number of bitmap images are created and saved as one bit per pixel image

I am trying to create large number of 1 bit per pixel bmp image from base 64 string and saving. As per the requirement a very huge number of images being created in a short period of time( an average of 50,000 to 1,00,000 in a short duration). I am using the below code.
public void CreateoneBppImageAndSave(String base64ImageString,String ImagePathToSave)
{
byte[] byteArray = Convert.FromBase64String(base64ImageString);
System.Drawing.Image img = byteArrayToImage(byteArray);
Bitmap objBitmap = new Bitmap(img);
BitmapData bmpData = objBitmap.LockBits(new Rectangle(0, 0, objBitmap.Width, objBitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format1bppIndexed);
Bitmap oneBppBitmap = new Bitmap(objBitmap.Width, objBitmap.Height, bmpData.Stride, System.Drawing.Imaging.PixelFormat.Format1bppIndexed, bmpData.Scan0);
oneBppBitmap.Save(ImagePathToSave, ImageFormat.Bmp);
img.Dispose();
objBitmap.Dispose();
objBitmap.UnlockBits(bmpData);
oneBppBitmap.Dispose();
}
private Image byteArrayToImage(byte[] byteArrayIn)
{
using (MemoryStream ms = new MemoryStream(byteArrayIn))
{
return Image.FromStream(ms);
}
}
Here the physical memory usage going very high. Usually the images are generated with size of 200x200 to 754x1024 . After certain duration physical memory usage reaching to the extreme and out of memory exception is being thrown.The physical memory is getting increased by 0.01 GB by every 5-10 seconds. Please help me to optimize the code in terms of memory usage.
You call LockBits on objBitmap however you call UnlockBits on oneBppBitmap. You should be calling unlock on the same object you called lock on.
As for using statements like I mentioned in the comments, a using statement turns this
using(SomeType obj = new SomeType())
{
// Some code
}
in to the equivalent of this
SomeType obj = new SomeType())
try
{
// Some code
}
finally
{
obj.Dispose();
}
That guarantees that even if a exception is thrown in // Some Code the dispose action will still happen. Your code, as it is right now, will not dispose any of its objects if any of your functions between the creation and dispose throws an exception.
Here is a re-written version with all the corrections I mentioned plus a few others..
public void CreateoneBppImageAndSave(String base64ImageString,String ImagePathToSave)
{
byte[] byteArray = Convert.FromBase64String(base64ImageString);
using(Image img = byteArrayToImage(byteArray))
using(Bitmap objBitmap = new Bitmap(img))
{
BitmapData bmpData = objBitmap.LockBits(new Rectangle(0, 0, objBitmap.Width, objBitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format1bppIndexed);
try
{
using(Bitmap oneBppBitmap = new Bitmap(objBitmap.Width, objBitmap.Height, bmpData.Stride, System.Drawing.Imaging.PixelFormat.Format1bppIndexed, bmpData.Scan0))
{
oneBppBitmap.Save(ImagePathToSave, ImageFormat.Bmp);
}
}
finally
{
//put the unlock in a finally to make sure it happens.
objBitmap.UnlockBits(bmpData);
}
}
}
EDIT: If this really is in your code
objBitmap.Dispose();
objBitmap.UnlockBits(bmpData);
That is the source of your problem, you should not call any methods on a class after you dispose. That is another benefit of using, you can't call methods late because the variable goes out of scope when you leave the using block.

Memory leak in my SharpDX application

This code runs every 100 ms. The memory usage just keeps increasing until it hits 1.5 GB and then it crashes.
void takeScreenShot()
{
Surface s;
s = CaptureScreen();
pictureBox1.Image = new Bitmap(Surface.ToStream(s, ImageFileFormat.Bmp));
s.Dispose();
}
public Surface CaptureScreen()
{
int width = Screen.PrimaryScreen.Bounds.Width;
int height = Screen.PrimaryScreen.Bounds.Height;
Device device = new Device(new Direct3D(), 0, DeviceType.Hardware, IntPtr.Zero, CreateFlags.HardwareVertexProcessing, new PresentParameters(width, height));
DisplayMode disp = device.GetDisplayMode(0);
Surface s = Surface.CreateOffscreenPlain(device, disp.Width, disp.Height, Format.A8R8G8B8, Pool.Scratch);
device.GetFrontBufferData(0, s);
return s;
}
You are creating a new device every time.
You should create the device only once, create it in your startup code once and then keep using it.
Furthermore I suspect a memory leak in Surface.ToStream() the returned stream probably needs disposing too.
var stream = Surface.ToStream(s, ImageFileFormat.Bmp);
pictureBox1.Image = new Bitmap(stream);
stream.Dispose();
As Hans Passant mentioned, Bitmap needs disposing as well.
You can very easily debug memory leaks in SharpDX by a helper to make diagnostic about unreleased COM resources. Setup at the beginning of your application this variable:
SharpDX.Configuration.EnableObjectTracking = true;
When your application exit, It will a print a report of COM objects that were not properly released with the stacktrace. The class behind this is ObjectTracker.
ObjectTracker.ReportActiveObjects() can be called to print the currently used resources at runtime (even with stack trace).

OutOfMemoryException after CameraCaptureDialog - Compact Framework [duplicate]

I'm developing an application that uses a mobile device to take a photo and send it using a webservice. But after I've taken 4 photos I am getting an OutOfMemoryException in the code below. I tried calling GC.Collect() but it didn't help either. Maybe someone here could be give me an advice how to handle this problem.
public static Bitmap TakePicture()
{
var dialog = new CameraCaptureDialog
{
Resolution = new Size(1600, 1200),
StillQuality = CameraCaptureStillQuality.Default
};
dialog.ShowDialog();
// If the filename is empty the user took no picture
if (string.IsNullOrEmpty(dialog.FileName))
return null;
// (!) The OutOfMemoryException is thrown here (!)
var bitmap = new Bitmap(dialog.FileName);
File.Delete(dialog.FileName);
return bitmap;
}
The function is called by an event handler:
private void _pictureBox_Click(object sender, EventArgs e)
{
_takePictureLinkLabel.Visible = false;
var image = Camera.TakePicture();
if (image == null)
return;
image = Camera.CutBitmap(image, 2.5);
_pictureBox.Image = image;
_image = Camera.ImageToByteArray(image);
}
I suspect you are holding onto references. As a minor cause, note that dialogs don't dispose themselves when using ShowDialog, so you should be using the dialog (although I would expect GC to still collect an undisposed but non-referenced dialog).
Likewise, you should probably be using the image, but again: not sure I'd expect this to make-or-break; worth a try, though...
public static Bitmap TakePicture()
{
string filename;
using(var dialog = new CameraCaptureDialog
{
Resolution = new Size(1600, 1200),
StillQuality = CameraCaptureStillQuality.Default
}) {
dialog.ShowDialog();
filename = dialog.FileName;
}
// If the filename is empty the user took no picture
if (string.IsNullOrEmpty(filename))
return null;
// (!) The OutOfMemoryException is thrown here (!)
var bitmap = new Bitmap(filename);
File.Delete(filename);
return bitmap;
}
private void _pictureBox_Click(object sender, EventArgs e)
{
_takePictureLinkLabel.Visible = false;
using(var image = Camera.TakePicture()) {
if (image == null)
return;
image = Camera.CutBitmap(image, 2.5);
_pictureBox.Image = image;
_image = Camera.ImageToByteArray(image);
}
}
I'd also be a little cautious of the CutBitmap etc, to ensure that things are released ASAP.
Your mobile device usually does not have any memory swapping to disk option, so since you choose to store your images as bitmaps in memory rather than files on disk, you quickly consume your phone's memory. Your "new Bitmap()" line allocates a large chunk of memory, so it is very likely to throw the exception there. Another contender is your Camera.ImageToByteArray that will allocate a large amount of memory. This probably isn't large to what you're used to with your computer, but for your mobile this is gigantic
Try keeping the pictures on disk until you use them, i.e. until sending them to the webservice. For displaying them, use your built-in controls, they are probably the most memory efficient and you can usually point them to the image files.
Cheers
Nik

Image.SelectActiveFrame memory problem

I'm writing a control to show images.
My problem comes out using Image class on multipage TIFFs.
I use this (I post only relevant code) at the beginning:
Image img;
int pages;
img = Bitmap.FromFile(filename);
pages = img.GetFrameCount(FrameDimension.Page);
then, when the user wants to show a different page:
public override Image GetPage(int page)
{
if (page < 1 || page > pages) return null;
try
{
#if !TEST
img.SelectActiveFrame(FrameDimension.Page, page - 1);
return new Bitmap(img);
#else
MemoryStream ms = new MemoryStream();
img.SelectActiveFrame(FrameDimension.Page, page - 1);
img.Save(ms, ImageFormat.Jpeg);
Image ret = Image.FromStream(ms);
ms.Flush();
ms.Dispose();
return ret;
#endif
}
catch (Exception ex)
{
"Tiff GetPage error: {0}".ToDebug(ex.Message);
return null;
}
}
Using img.SelectActiveFrame(FrameDimension.Page, page - 1); (in both versions) about 7MB are allocated in memory and those are never freed (even exiting the method)!!!
If I goes to next page 7MB are allocated and not freed everytime, while going back (on an already visited pages) previously allocated memory is used.
To give you an example: think Task Manager reports my app is using x MB; going one page forward memory increases to x + y (after SelectActiveFrame()) + z (Image ret = ...). Well, I should have x + z (y part should be zero or GC collected exiting the method), but obviously that's not what happens, even calling GC.Collect manually.
Going back to a previously visited page, memory increases effectively only with z, as expected.
I find it terrible (think about a file with 80 pages...), but how can I force img object to free allocated memory? Am I doing something wrong?
I've already thought closing and reopening img, but speed is not good.
Thanks to everybody
Don't use new Bitmap(img) because it will force the system to create new memory for a new Bitmap object using 32-bit color by default.
You can just use var bitmap = (Bitmap)img; to retrieve a Bitmap object for that page.
If I'm not mistaken you're not destroying the used controls at every possible point.
I think you might need to test this - but make sure that you're disposing all the used controls.
ie. ImageControlUsed.Dispose();
I answer my question after trying different solutions and accept soluzion given by user965487 because at the end he was right (thanks to Hans Passant too).
If you have a class (call it QV) similar to this
public class QV
{
Image img;
int pages;
public QV(filename) {
img = Bitmap.FromFile(filename);
pages = img.GetFrameCount(FrameDimension.Page);
}
~QV() {
img.Dispose();
img = null;
}
public Image GetPage(int page) {
if (page < 1 || page > pages) return null;
img.SelectActiveFrame(FrameDimension.Page, page - 1);
return new Bitmap(img);
}
}
then every time you call GetPage(...) your memory will grow not only for the size of returned image, but also for img.SelectActiveFrame(...) statement. I don't know why and how, but it happens. Releasing returned image and calling frees memory for image size, not for the amount taken from SelectActiveFrame() (anyway this memory is not duplicated if you return on a previously seend page).
So you'd better open and close the image everytime, like this:
public class QV
{
Image img;
int pages;
public QV(filename) {
img = Bitmap.FromFile(filename);
pages = img.GetFrameCount(FrameDimension.Page);
img.Dispose();
}
public Image GetPage(int page) {
if (page < 1 || page > pages) return null;
img = Bitmap.FromFile(filename);
img.SelectActiveFrame(FrameDimension.Page, page - 1);
Image ret = Bitmap(img);
img.Dispose();
return ret;
}
}
Payload for opening and disposing image everytime user requests a new page is really nothing compared to dangerous memory allcation done with first solution.
I hope someone needs this.

Categories

Resources