The process cannot access the file because it is being used by another process using streamwriter

I have 2 programs: a C # GUI application and a C # Windows service accessing the same text file;

a) the C# GUI application will write/append to the text file
b) the windows service will copy the file to a network location every 20 mins.

      

When the action happened at the same time, I got an error like below:

2014/09/08 21:15:56 mscorlib
The process cannot access the file 'C:\09082014.log' because it is being used by another process.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
   at System.IO.StreamWriter.CreateFile(String path, Boolean append)
   at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encoding, Int32 bufferSize)
   at System.IO.StreamWriter..ctor(String path, Boolean append)
   at DataloggerUI.DataHelper.WriteDataLog(String msg, Int64& downTimeSince)
   at DataloggerUI.Form1.ReceiveData(IAsyncResult asyncResult)

      

---- the c # windows service part is like below ----------

  if (File.Exists(destination + @"\" + fi.Name))
            {
                FileInfo fi_dest = new FileInfo(destination + @"\" + fi.Name);
                if (fi.LastWriteTime > fi_dest.LastWriteTime)
                {
                    File.Copy(fi.FullName, destination + @"\" + fi.Name, true);
                    WriteLog("Send " + fi.FullName + " to server");
                }
            }
            else
            {
                File.Copy(fi.FullName, destination + @"\" + fi.Name, true);
                WriteLog("Send " + fi.FullName + " to server");
            }  
}

      

------- C # Windows GUI application code looks like this: -------

    string logfile = DataHelper.GetAppConfigString("MPRS_LogDir") + @"\" + DateTime.Now.ToString("MMddyyyy") + ".log";
    using (StreamWriter sw = new StreamWriter(logfile, true))
    {
        sw.WriteLine(tick + " " + "KYEC" + Environment.MachineName + " " + msg);
        sw.Close();
    }

      

The error message is thrown by the GUI application. Was there any mistake or bad practice in my code?

------------ modified code to the following as recommended by Peter --------------

    try
    {
        using (StreamWriter sw = new StreamWriter(logfile, true))
        {
            sw.WriteLine(tick + " " + "KYEC" + Environment.MachineName + " " + msg);
        }
    }
    catch (IOException ex)
    {
        WriteErrorLog("IOException " + ex.Message);
        System.Threading.Thread.Sleep(2000); //2 secs                
        using (StreamWriter sw = new StreamWriter(logfile, true))
        {
            sw.WriteLine(tick + " " + "KYEC" + Environment.MachineName + " " + msg);
        }
    }

      

+3


source to share


3 answers


Based on Peter Dunyho's answer and your own changes, this should be the correct approach:

// try to write maximum of 3 times
var maxRetry = 3;
for (int retry = 0; retry < maxRetry; retry++)
{
    try
    {
        using (StreamWriter sw = new StreamWriter(logfile, true))
        {
            sw.WriteLine("{0} KYEC{1} {2}", tick, Environment.MachineName, msg);

            break; // you were successfull so leave the retry loop
        }
    }
    catch (IOException)
    {
        if(retry < maxRetry - 1)
        {
            System.Threading.Thread.Sleep(2000); // Wait some time before retry (2 secs)
        }
        else
        {
            // handle unsuccessfull write attempts or just ignore.
        }
    }
}

      



this gives you the ability to specify how long you will repeat your recording attempts.

+3


source


You can use FileStream in Shared ReadWrite mode to write and copy files at the same time.

Try the below code:



 //To write file use
    using (FileStream fs = new FileStream(fileToReadPath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)) 
    {
        using (StreamWriter StreamWriter = new StreamWriter(fs)) 
        {
            StreamWriter.WriteLine(tick + " " + "KYEC" + Environment.MachineName + " " + msg);
            StreamWriter.Close();
        }
    }

//To copy file use
    using (FileStream inStream = new FileStream(fileToReadPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
    {
        using (FileStream outStream = File.Create(fileToWritePath)) 
        {
            while (inStream.Position < inStream.Length) 
            {
                outStream.WriteByte((byte)inStream.ReadByte());
            }
        }
    }

      

This way, you can achieve your goal without using another process error.

+5


source


You have at least a couple of options. The simplest concept is to just wrap the file I / O in try / catch, and if an IOException event occurs (and only if an IOException event occurs), delay the operation for a while (e.g. Thread.Sleep (), set a timer) and try again.

An alternative approach would be to use a named mutex to allow the service and GUI to coordinate access. Each of them had to acquire a mutex before attempting their respective operation; if another is currently accessing a file, then trying to acquire a mutex will cause the process to wait until the other is done.

The repetition code can sometimes be messy and verbose, so while I find the repetition approach is easier to understand, IMHO the mutex approach is actually more readable and easier to get the correct value (once you have difficulty learning how to create named mutex & hellip, it's not hard and MSDN has examples).

+1


source







All Articles