tags:

views:

678

answers:

5

In C# I can use the FileSystemWatcher object to watch for a specific file and raise an event when it is created, modified, etc.

The problem I have with this class is that it raises the event the moment the file becomes created, even if the process which created the file is still in the process of writing. I have found this to be very problematic, especially if I'm trying to read something like an XML document where the file must have some structure to it which won't exist until it is completed being written.

Does .NET (preferably 2.0) have any way to raise an event after the file becomes accessible, or do I have to constantly try reading the file until it doesn't throw an exception to know it is available?

A: 

Not sure if there is a way of an event actually being raised by the standard class, but I eas experiencing similar problems on some recent work I was doing.

In short, I was trying to write to a file that was locked at the time. I ended up wrapping the write method up so it would automatically try the write again in a few ms after..

Thinking out loud, Can you probe the file for a ReadOnly status? May be worth then having a wrapper for file IO which can stack up delegates for pending file operations or something.. Thoughts?

Rob Cooper
A: 

I was thinking of writing an extension method of some kind if nothing exists, however the obvious solutions seem really bad when I think of "best practices" for coding.

For example, waiting a few ms and then trying again is what I typically do also. I just don't feel like it's the best solution to the problem.

Dan Herbert
A: 

Use CreateFile in a loop with OPEN_ EXISTING flag and FILE_ ALL_ ACCESS (or you might need only a subset, see http://msdn.microsoft.com/en-us/library/aa364399(VS.85).aspx

Examine the handle returned against -1 (INVALID_ HANDLE_ VALUE) for failure. It's still polling, but this will save the cost of an exception throw.

EDIT: this editor/markup can't handle underscores! bah!

Nick
+4  A: 

You can use a file system watcher to check when the file has been changed. It only becomes "changed" after whichever program had the file previously closes the file. I know you asked for C#, but my VB.Net is much better. Hope you or someone else can translate.

It tries to open the file, if it isn't available, it adds a watcher, and waits for the file to be changed. After the file is changed, it tries to open again. It throws an exception if it waits more than 120 seconds, because you may get caught in a situation where the file is never released. Also, I decided to add a timeout of waiting for the file change of 5 seconds, in case of the small possibility that the file was closed prior to the actual file watcher being created.

 Public Sub WriteToFile(ByVal FilePath As String, ByVal FileName As String, ByVal Data() As Byte)
        Dim FileOpen As Boolean
        Dim File As System.IO.FileStream = Nothing
        Dim StartTime As DateTime
        Dim MaxWaitSeconds As Integer = 120

        StartTime = DateTime.Now

        FileOpen = False

        Do
            Try
                File = New System.IO.FileStream(FilePath & FileName, IO.FileMode.Append)
                FileOpen = True

            Catch ex As Exception

                If DateTime.Now.Subtract(StartTime).TotalSeconds > MaxWaitSeconds Then
                    Throw New Exception("Waited more than " & MaxWaitSeconds & " To Open File.")
                Else
                    Dim FileWatch As System.IO.FileSystemWatcher

                    FileWatch = New System.IO.FileSystemWatcher(FilePath, FileName)
                    FileWatch.WaitForChanged(IO.WatcherChangeTypes.Changed,5000)
                End If

                FileOpen = False

            End Try

        Loop While Not FileOpen

        If FileOpen Then
            File.Write(Data, 0, Data.Length)
            File.Close()
        End If
    End Sub
Kibbee
A: 

Kibbe answer seems right but didn't worked for me. It seems that the FileSystemWatcher has a bug. So I wrote my own WaitForChanged:

using (var watcher = new FileSystemWatcher(MatlabPath, fileName))
{
    var wait = new EventWaitHandle(false, EventResetMode.AutoReset);
    watcher.EnableRaisingEvents = true;
    watcher.Changed += delegate(object sender, FileSystemEventArgs e)
    {
       wait.Set();
    };
    if (!wait.WaitOne(MillissecondsTimeout))
    {
        throw new TimeoutException();
    }
 }
Jader Dias
Please report this bug to Microsoft at http://connect.microsoft.com/visualstudio.
John Saunders
[John Aldrige](http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework/topic56372.aspx) already did [it](https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=240502)
Jader Dias