Windows Service - Out of Memory Exception

I have created a windows service using vb.net which uses a filesystemwatcher to monitoe files in a specifc directory, if images are added to the directory they are resized and saved in a remote directory. The original file is then backed up and deleted from the image directory.

The code is as follows

Friend Shared Sub ResizeImage(ByVal path As String, ByVal filename As String)

Dim asr As AppSettingsReader = New AppSettingsReader

Dim sRemoteDirectory As String = CType(asr.GetValue("RemoteDirectory", GetType(String)), String)

Dim sBackupDirectory As String = CType(asr.GetValue("BackupDirectory", GetType(String)), String)

' Dim img As Image = ImageFromFile(path)

(This was put in as an alternative but produced the same problem

//

Dim fs As FileStream = New FileStream(filename, FileMode.Open, FileAccess.Read)

Return Image.FromStream(fs)

fs.Dispose()

//

Dim img As Image = Image.FromFile(path) << It fails here

Dim imgFormat As Imaging.ImageFormat = GetImageFormat(path)

'Maintain aspect ratio

Dim scale As Double = 0

Dim width, height As Integer

width = CType(asr.GetValue("ImageWidth", GetType(String)), Integer)

height = CType(asr.GetValue("ImageHeight", GetType(String)), Integer)

'Caters for the user specifying 0 for either width or height

If width = 0 Then

width = 1

ElseIf height = 0 Then

height = 1

End If

If img.Height < img.Width Then

scale = width / img.Width

Else

scale = height / img.Height

End If

Dim newwidth As Integer = CInt(scale * img.Width)

Dim newheight As Integer = CInt(scale * img.Height)

Dim bmp As New Bitmap(img, newwidth, newheight)

img.Dispose()

'Add a log entry

' SaveToLog(filename)

Dim sRemotePath As String = String.Format("{0}\{1}", sRemoteDirectory, filename)

bmp.Save(sRemotePath, imgFormat)

bmp.Dispose()

' Dim sBackupPath As String = String.Format("{0}\{1}", sBackupDirectory, filename)

' File.Copy(path, sBackupPath, True)

' File.Delete(path)

Whether I copy a couple of images or lots, the error is always the same

The call stack is as follows

[External Code]
> GeminiPictureService.exe!GeminiPictureService.GeneralRoutines.ResizeImage(string path = "C:\\Picture Test 1\\IMGP0353.JPG", string filename = "IMGP0353.JPG") Line 18 + 0x9 bytes Unknown
GeminiPictureService.exe!GeminiPictureService.GeminiPictureService.fswGemini_Created(object sender = {System.IO.FileSystemWatcher}, System.IO.FileSystemEventArgs e = {System.IO.FileSystemEventArgs}) Line 42 + 0x20 bytes Unknown
[External Code]

Exception

- $exception {System.OutOfMemoryException: Out of memory.
at System.Drawing.Image.FromFile(String filename, Boolean useEmbeddedColorManagement)
at System.Drawing.Image.FromFile(String filename)
at GeminiPictureService.GeneralRoutines.ResizeImage(String path, String filename)
at GeminiPictureService.GeminiPictureService.fswGemini_Created(Object sender, FileSystemEventArgs e)
at System.IO.FileSystemWatcher.OnCreated(FileSystemEventArgs e)
at System.IO.FileSystemWatcher.NotifyFileSystemEventArgs(Int32 action, String name)
at System.IO.FileSystemWatcher.CompletionStatusChanged(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* overlappedPointer)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)} System.Exception {System.OutOfMemoryException}

Memory - I checked the process and the memory usage spiked from 8,000 to (40-46k), it stayed at around 40k before giving an exception

System Pentium 1.8GHz 1GB Ram Windows XP Pro SP2

Thanks for any help in advance




Answer this question

Windows Service - Out of Memory Exception

  • ArcSend

    That seems to work in the fact that it no longer falls over, however, if I paste say 50 files in to the watched directory, only 20 or so are copied.


  • NewToVba

    Thank you very much for all your help
  • andris11

    Yeah, you could. They could do the Sleep() too. It is risky though, you could easily starve the thread pool when you dump 50 files into the folder.


  • Jay K

    OK, here's what I cooked up. I didn't do a whole lot of testing but it looked good:

    Public Class ResizeService
    Private WithEvents mWatcher As IO.FileSystemWatcher
    Private WithEvents mPoller As Timers.Timer
    Private mQueue As Queue(Of QEntry)

    Private Sub mWatcher_Created(ByVal sender As Object, ByVal e As System.IO.FileSystemEventArgs) Handles mWatcher.Created
    '--- File created, put on queue and process 5 seconds later
    Dim q As New QEntry
    q.Path = e.FullPath
    q.Due = Now + New TimeSpan(0, 0, 5)
    mQueue.Enqueue(q)
    End Sub

    Protected Overrides Sub OnStart(ByVal args() As String)
    '--- Start the watcher
    mWatcher = New IO.FileSystemWatcher("c:\temp")
    mWatcher.NotifyFilter = IO.NotifyFilters.FileName
    mWatcher.EnableRaisingEvents = True
    If mPoller Is Nothing Then
    '--- Start the timer
    mPoller = New Timers.Timer
    mPoller.AutoReset = False
    mPoller.Interval = 1000
    mPoller.Enabled = True
    mPoller.Start()
    '--- Allocate the queue
    mQueue = New Queue(Of QEntry)
    End If
    End Sub

    Protected Overrides Sub OnStop()
    '--- Stop the watcher
    mWatcher = Nothing
    End Sub

    Private Sub mPoller_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles mPoller.Elapsed
    '--- Check if there is anything to process
    Do While mQueue.Count > 0
    Dim q As QEntry = mQueue.Peek
    If q.Due > Now Then Exit Do
    mQueue.Dequeue()
    Try
    '--- Put processing code here
    '===> ResizeImage(q.Path)
    '--- Test:
    'My.Computer.FileSystem.MoveFile(q.Path, IO.Path.GetDirectoryName(q.Path) & "\bak\" & IO.Path.GetFileName(q.Path))
    'Threading.Thread.Sleep(5000)
    Catch ex As Exception
    My.Application.Log.WriteException(ex)
    End Try
    Loop
    mPoller.Start()
    End Sub
    End Class

    Public Class QEntry
    '--- Object stored on the queue
    Public Path As String ' Path to the file
    Public Due As DateTime ' When to process file
    End Class



  • John M. Cruz

    Ok, that's not it. Another "I wonder if that could really work" issue I saw is that you read the file on the FileSystemWatcher.Created event. At that point, the application has just created the file but is probably still in the process of writing it. I'm not sure why that would cause an out-of-memory exception but try a couple of seconds delay before you read the file.



  • nmfl

    Yes. System.Threading.Thread.Sleep(5000) is good.



  • MagedSalah

    Private Sub fswGemini_Created(ByVal sender As Object, ByVal e As System.IO.FileSystemEventArgs) Handles fswGemini.Created

    Dim asr As System.Configuration.AppSettingsReader = New System.Configuration.AppSettingsReader

    Do you suggest I put a dummy loop here

    Dim sImage As String = e.Name.ToLower

    If sImage.EndsWith(".bmp") OrElse sImage.EndsWith(".jpg") OrElse sImage.EndsWith(".jpeg") OrElse sImage.EndsWith(".png") Then

    GeneralRoutines.ResizeImage(e.FullPath, e.Name)

    End If

    End Sub


  • shifter

    I know this is a bit cheeky, but how would I implement the queue code in the resize image, so that the original thread could return. Also, surely it needs some form of delay to ensure the file is there
  • popit

    I tried pasting in 50 files and it only did 20, I then deleted and pasted in again it worked fine. I did this process a few times and it seems to work on the second paste


  • Tim F. Fischer

    Private Shared Function GetImageFormat(ByVal imgPath As String) As Imaging.ImageFormat

    Dim sImagePath As String = imgPath.ToLower()

    If sImagePath.EndsWith(".bmp") Then

    Return System.Drawing.Imaging.ImageFormat.Bmp

    ElseIf sImagePath.EndsWith(".gif") Then

    Return System.Drawing.Imaging.ImageFormat.Gif

    ElseIf sImagePath.EndsWith(".jpg") OrElse imgPath.EndsWith(".jpeg") Then

    Return System.Drawing.Imaging.ImageFormat.Jpeg

    ElseIf sImagePath.EndsWith(".png") Then

    Return System.Drawing.Imaging.ImageFormat.Png

    Else

    Return System.Drawing.Imaging.ImageFormat.Gif

    End If

    End Function


  • Joe Horton

    Great. Now improve the code to put the filename on a queue, together with a timestamp, so that the event quickly returns and can get another notification. Empty the queue with a timer that pays attention to the timestamp and starts the conversion when enough time expired.




  • ron8888

    You can't leave Sleep() in the code, I just asked you to put it in to check if the problem was related to trying to convert a file that still being written to. The FileSystemWatcher is not going to generate a new Created event if the previous one is still executing.



  • Docpro777

    It smells like the code is leaking file handles. You carefully dispose the img and bmp objects so that's (probably) not it. Please show us the code for your GetImageFormat() function.



  • Kevin Jacobson

    I understand, could I spawn a new thread everytime I called ResizeImage. I am slightly unsure how I would implement your solution, I do appreciate your help so far


  • Windows Service - Out of Memory Exception