2014-10-15 3 views
3

У меня есть 2 программы: приложение C# GUI и служба Windows C#, обращающиеся к одному и тому же текстовому файлу;Процесс не может получить доступ к файлу, потому что он используется другим процессом с использованием streamwriter

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. 

Когда действие происходило одновременно, я получил сообщение об ошибке, как показано ниже:

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) 

---- часть C# Windows сервис, как показано ниже ----------

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# код окна приложения GUI как ниже -------

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(); 
    } 

Сообщение об ошибке высылается приложением GUI. Какая у вас ошибка или плохая практика?

------------ модифицированный код к следующему согласно совету Петра --------------

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); 
     } 
    } 
+0

Как служба windows принимает решение о начале копирования файла? Пожалуйста, объясните процедуру или покажите больше кода. –

+0

Ошибка говорит, что файл использовался. Не так ли? Возможно, он копируется, когда GUI хочет добавить что-то еще. – luk32

+0

Привет luk32, да, ошибка произошла, когда графический интерфейс добавляется к файлу, а служба Windows пыталась его скопировать. Могу ли я избежать этого исключения? как определить, используется ли файл перед добавлением? также, используя оператор C# 'using', исключение будет обработано уже? – sqr

ответ

3

основе Peter Duniho's answer и собственных правок это должно быть правильный подход:

// 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. 
     } 
    } 
} 

это дает возможность определить, как долго вы повторить ваши попытки записи.

+0

спасибо, вы не только показали мне правильный код, но и элегантный и надежный код. – sqr

1

У вас есть по крайней мере, здесь есть несколько вариантов. Простейшим концептуальным является просто перенос ввода/вывода файлов в try/catch, и если возникает событие IOException (и только если возникает событие IOException), задержите операцию ненадолго (например, Thread.Sleep(), установите таймер) и повторите попытку ,

Альтернативным подходом было бы использовать именованный мьютекс, чтобы позволить сервису и графическому интерфейсу осуществлять координацию доступа. Каждый из них должен был получить мьютекс, прежде чем пытаться выполнить их соответствующую операцию; если другой в настоящее время обращается к файлу, то попытка получить мьютекс заставит процесс ждать, пока другой не будет выполнен.

код Retry иногда может запутаться и многословным, поэтому, когда я думаю, что повторная попытка подход легче понять, ИМХО мьютекс подход на самом деле более читаемым и проще получить правильно (как только вы пошли в беде узнать, как создать именованный mutex & hellip, это не сложно, и в MSDN есть примеры).

+0

привет, Питер, спасибо. Вы хотите, чтобы преобразовать код в блок try/catch? с использованием (StreamWriter sw = new StreamWriter (logfile, true)) { sw.WriteLine (tick + "" + "KYEC" + Environment.MachineName + "" + msg); sw.Close(); } – sqr

+0

http://stackoverflow.com/questions/4590490/try-catch-using-right-syntax @Peter Duniho: Я читал приведенные выше ссылки только сейчас. Я всегда считал, что ключевое слово «using» эквивалентно try/catch. Наверное, я ошибся. оператор using не вызывает исключения, возникающего из вызывающего конструктора потокового потока. так я должен изменить его на следующее: попробовать { используя (вар MyObject = новый MyClass()) { // что-то здесь ... }} поймать (Exception ех) { // Handle exception } – sqr

+0

Я имею в виду, что вы бы обернули весь блок кода в try/catch. В любом случае, это вообще хорошая идея для любого кода ввода-вывода. В I/O почти всегда есть возможность бросать исключения, поэтому стоит подумать о том, что вы хотите, чтобы ваша программа выполняла, если это произойдет, а не просто оставить ее в .NET. Что касается обсуждаемой дискуссии, в этом случае вы хотите, чтобы весь блок «использования» полностью содержался в блоке try/catch, потому что выражение «новый StreamWriter ...» может генерировать исключение (действительно, именно там, где это возможно,). –

5

Вы можете использовать FileStream в режиме Shared ReadWrite, чтобы одновременно записывать и копировать файл.

Попробуйте следующий код:

//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()); 
      } 
     } 
    } 

Таким образом, вы можете достичь своей задачи без каких-либо используется другой ошибки процесса.

+0

не могли бы вы объяснить мне немного? Благодарю. – sqr

+0

Пока файл копируется, он заблокирован, поэтому другой процесс не может читать/писать одновременно. Таким образом, в примере, который я привел, используется ReadWrite FileShare, в котором несколько процессов могут одновременно читать/записывать файл. Следовательно, вместо использования File.Copy для копирования файла вы можете использовать эту технику для копирования содержимого файла. – prem

+0

спасибо за объяснение! еще один вопрос, чтобы это работало, я должен убедиться, что оба приложения реализованы в FIleShare? – sqr

Смежные вопросы

 Смежные вопросы