3

I made a very simple program that could find pictures on an external-HDD, and place them into folders. That sounds very simple, but for some reason, I get a 'Out of Memory'-exception when doing so.

I've tested it on a 64-Bit Win10 with 4 GB of ram, and a 64-Bit Win10 with 32 GB of ram. Yet, I still get the 'Out of Memory'-exception on both systems.

My Platform-target is x64.

Here's the code where the error-occurs in:

string[] filePaths = Directory.GetFiles(Stien, "*.*", SearchOption.AllDirectories);

        foreach (string file in filePaths)
        {
            string[] TempValue1 = file.Split(new[] { @"\" }, StringSplitOptions.None);
            string FileName = TempValue1[TempValue1.Length - 1];

            if (FileName.Contains(SøgeTerm)) //Checks if the name contains the search-term.
            {
                if (!SortDate) //If the program was told not to sort by date.
                {
                    try
                    {
                        File.Copy(file, destination + @"\" + FileName, true);
                        Console.WriteLine(FileName + " => " + destination + @"\" + FileName);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("Fejl: " + e.ToString());
                    }
                }
                else
                {
                    Image Billede = Bitmap.FromFile(file);
                    string date;
                    try
                    {
                        PropertyItem propItem = Billede.GetPropertyItem(36867);
                        date = r.Replace(Encoding.UTF8.GetString(propItem.Value), "-", 2);
                        date = date.Split(new[] { ' ' }, StringSplitOptions.None)[0];
                        propItem = null;
                    }
                    catch
                    {
                        date = "UKENDT";
                    }
                    //


                    if (!Directory.Exists(destination + @"\" + date))
                    {
                        Directory.CreateDirectory(destination + @"\" + date);
                        Console.WriteLine(destination + @"\" + date);
                    }

                    File.Copy(file, destination + @"\" + date + @"\" + FileName, true); //Copies the file, and places it into the fitting folder.
                    Console.WriteLine(FileName + " => " + destination + @"\" + "" + date + @"\" + FileName);
                    date = null; //I thought this might helped clearing some memory.
                    Billede = null;
                }
            }

        }

So my problem: What causes the exception, and how can I fix it?

  • exactly at what line does the exception occurs ? – niceman Feb 24 '17 at 11:24
  • Check the stacktrace of the exception to find the line, where it occurs. My bet is on Bitmap.FromFile that there is some image file with large pixel dimensions on your disk. – Ralf Bönning Feb 24 '17 at 11:27
  • Do you close file after opening them? – Nikhil Agrawal Feb 24 '17 at 11:27
  • Corrupt/invalid images can throw that exception presumably @ Bitmap.FromFile(), you are not filtering to check the file is actually an image. – Alex K. Feb 24 '17 at 11:27
  • Possible duplicate of [ImageList / Image OutOfMemoryException](http://stackoverflow.com/questions/882619/imagelist-image-outofmemoryexception) – niceman Feb 24 '17 at 11:27
  • Use .EnumerateFiles instead of .GetFiles – Slai Feb 24 '17 at 11:28
  • apart from `Dispose`, you could use a format string in your `WriteLine` instead of string concatenation (or better string interpolation if you have C# 6) – niceman Feb 24 '17 at 11:29
  • And I'm sure .Net already has a function that get a filename from a path so you don't need to use `TempValue1` – niceman Feb 24 '17 at 11:30

3 Answers3

5

You are not only copying files, but also loading bitmaps to memory, that's why it eventually runs out of memory.

Image Billede = Bitmap.FromFile(file);

Just setting the value to null is not enough, you need to dispose of the object.

So, instead of

Billede = null;

do

Billede.Dispose();

EDIT: this way you'll do the least amount of changes in code. However, using a USING statement is a better practice (other answers exemplify).

PS - There are ways to load image metadata without loading the whole image to memory.

PPS - Read this for a way to load date taken info without loading the whole image to memory. It should also make your code run a lot faster: How can I find out when a picture was actually taken in C# running on Vista?

Community
  • 1
  • 1
Mihai Ovidiu Drăgoi
  • 1,307
  • 1
  • 10
  • 16
2

Image and Bitmap implement IDisposable. Therefore you must wrap their usage in a using statement to free up their resources.

 using (Image Billede = Bitmap.FromFile(file)){
     ...
 }

Although the images will get finalized and released eventually, you are probably running out of memory before this point.

Tim Rogers
  • 21,297
  • 6
  • 52
  • 68
1

As noted in other answers, you could use the using statement to call the Dispose method on the object in the correct way.

using (Image Billede = Bitmap.FromFile(file)){
     ...
 }

For your code, however, you might need to check the image format because the Image.FromFile function will throw an OutOfMemoryException if (from MSDN):

The file does not have a valid image format.

-or-

GDI+ does not support the pixel format of the file.

Have a look at this

Community
  • 1
  • 1
S.Dav
  • 2,436
  • 16
  • 22