I have a win program that sits in the system tray, watches for new files in a folder, and then processes and appends info to them. I am using the file system watcher control in VB.net 2005. Everything works fine when you just copy a file into the folder for testing. However, when I use the program that will be creating the file, it throws an error saying that the file is in use by another process. Is there anyway I can have my app check if the file is in use, and wait until it isn't before it does it's processing Is there something else I'm missing Right now all my processing code is triggered from the file system watcher control's file created event.

File system watcher
BobishKindaGuy
I would simply add changed files to a list of files that get's processed every x milliseconds, if needed. This would require adding a background thread...
Paul1975
I don't see the need to add a special string to signify a special wait of 500ms. Just process the list when it has changed, putting the thread in a wait state when the list is empty. If a file cannot be processed (it's locked), reprocess the file(s) after a timeout of arbitrary length. For example:
Public Class Form1 Inherits System.Windows.Forms.Form
' ...
Private files As New ArrayList
Private terminateEvent As New AutoResetEvent(False)
Private fileAddedEvent As New AutoResetEvent(False)
Private Sub OnFileChanged(ByVal source As Object, ByVal eventArguments As FileSystemEventArgs)
SyncLock (files)
files.Add(eventArguments.FullPath)
End SyncLock
fileAddedEvent.Set()
End Sub
Private Shared Sub BackgroundThread(ByVal state As Object)
Dim this As Form1 = state
Dim timeout As Integer = 0
Dim events() As AutoResetEvent = New AutoResetEvent() {this.terminateEvent, this.fileAddedEvent}
While (True)
Dim wait As Integer
If timeout = 0 Then
wait = WaitHandle.WaitAny(events)
Else
wait = WaitHandle.WaitAny(events, timeout, False)
End If
Select Case wait
Case 0 ' terminate
Exit While
Case 1, WaitHandle.WaitTimeout ' file added to list or file still needs processing
Debug.WriteLine("Processing List")
' do something with list
Dim index As Integer = 0
SyncLock (this.files)
Do
Dim stream As FileStream
Try
stream = New FileStream(this.files(index), FileMode.Open)
' TODO: process file instead of this fake
stream.Lock(0, stream.Length)
' if we got here, the entire file could be processed
' this.InvokeFileChanged(this.files(index))
this.files.Remove(index)
Catch ex As FileNotFoundException
this.files.RemoveAt(index)
Catch ex As UnauthorizedAccessException
Debug.WriteLine("Could not process " + this.files(index) + ", deferring...")
timeout = 500 ' check the list every 1/2 second
index = index + 1 ' process the next file
If (index > this.files.Count) Then
' exit if no next file
Exit While
End If
Catch ex As IOException
Debug.WriteLine("Could not process " + this.files(index) + ", deferring...")
timeout = 500 ' check the list every 1/2 second
index = index + 1 ' process the next file
If (index > this.files.Count) Then
' exit if no next file
Exit While
End If
Finally
if(Not stream Is Nothing) Then
stream.Close()
End If
End Try
Loop While (this.files.Count > 0)
End SyncLock
End Select
End While
End Sub
' ...
End Class
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf BackgroundThread), Me)
To exit the thread:
terminateEvent.Set()
WaitAny puts the thread into a wait state. Since we're waiting for either a terminate request or a list changed notification the thread does no processing until either of those conditions are met. If any of the files in the list cannot be processed, we skip past it and set a timeout. When WaitAny is given a timeout value, it only waits for a maximum amount of time. This is better than Sleep() because we can still come out of the wait state when one of our two conditions are met; for better performance.
PhilippeMoison
DavWein
Private Const WaitString As String = "::WAIT::"
Private filesToProcess As New Queue(Of String)
Public Sub scratchpad()
Dim curFile As String
Dim waitLoaded As Boolean
If filesToProcess.Count > 0 Then
SyncLock filesToProcess
curFile = filesToProcess.Dequeue
End SyncLock
Try
If curFile = WaitString Then
System.Threading.Thread.Sleep(500)
Else
Using fs As New IO.FileStream(curFile, IO.FileMode.Open)
'process file here
End Using
End If
Catch ex As Exception
'processing failed
'insert this file to be reprocessed
SyncLock filesToProcess
If Not waitLoaded Then
filesToProcess.Enqueue(WaitString)
waitLoaded = True
End If
filesToProcess.Enqueue(curFile)
End SyncLock
End Try
End If
'wait 1 second after the queue empties to check again
System.Threading.Thread.Sleep(1000)
End Sub
Private Sub fsw_Created(ByVal sender As Object, ByVal e As System.IO.FileSystemEventArgs) Handles fsw.Created
If e.ChangeType <> IO.WatcherChangeTypes.Created Then Exit Sub
SyncLock filesToProcess
filesToProcess.Enqueue(e.FullPath)
End SyncLock
End Sub
This is, of course, only one way to do something like this. You could take out the wait condition or implement it differently, but I'm not aware of any way to check the file's availability from .NET without using a Try...Catch block. The SyncLock statements are important as they allow safe access to a shared resource (filesToProcess) from multiple threads.
lex23456
System.Threading.Thread.Sleep(500)