-3

I am trying to Write to a text file after this code block checks for the last time the PC was restarted. The code below reads from a text file, the last time the PC was resarted, and from there it determines whether to show a splash-screen. However, After this method runs, i need to write to the text file what the current "System Up-Time" is. But i keep getting an error that says the text file is in use. This has driven me insane. I have made sure all StreamWriters and StreamReaders are closed. I have tried Using Statements. I have tried GC.Collect. I feel like i have tried everything.

Any help would be appreciated.

    private void checkLastResart()
    {
        StreamReader sr = new StreamReader(Path.GetDirectoryName(Application.ExecutablePath) + @"\Settings.txt");
        if (sr.ReadLine() == null)
        {
            sr.Close();
            MessageBox.Show("There was an error loading 'System UpTime'. All settings have been restored to default.");
            StreamWriter sw = new StreamWriter(Path.GetDirectoryName(Application.ExecutablePath) + @"\Settings.txt", false);
            sw.WriteLine("Conversion Complete Checkbox: 0");
            sw.WriteLine("Default Tool: 0");
            sw.WriteLine("TimeSinceResart: 0");
            sw.Flush();
            sw.Close();
        }
        else
        {
            try
            {
                StreamReader sr2 = new StreamReader(Path.GetDirectoryName(Application.ExecutablePath) + @"\Settings.txt");
                while (!sr2.EndOfStream)
                {
                    string strSetting = sr2.ReadLine();
                    if (strSetting.Contains("TimeSinceResart:"))
                    {
                        double lastTimeRecorded = double.Parse(strSetting.Substring(17));

                        //If the lastTimeRecorded is greater than timeSinceResart (computer has been resarted) OR 2 hours have passed since LVT was last run
                        if (lastTimeRecorded > timeSinceRestart || lastTimeRecorded + 7200 < timeSinceRestart)
                        {
                            runSplashScreen = true;
                        }
                        else
                        {
                            runSplashScreen = false;
                        }
                    }
                }
                sr2.Close();
                sr2.Dispose();
            }
            catch (Exception e) { MessageBox.Show("An error has occured loading 'System UpTime'.\r\n\r\n" + e); }
        }
    }               

Below is a sample of writing to the Text file, after the above code has been run. It doesnt matter if i open a StreamWriter, or use File.WriteAllLines, an error is thrown immediately.

StreamWriter sw = new StreamWriter(Path.GetDirectoryName(Application.ExecutablePath) + @"\Settings.txt");
            string[] lines = File.ReadAllLines(Path.GetDirectoryName(Application.ExecutablePath) + @"\Settings.txt");
            lines[2] = "TimeSinceResart: " + timeSinceRestart;
            foreach (string s in lines)
                sw.WriteLine(s);
Michael
  • 739
  • 12
  • 27
  • You're opening a second version of the `StreamReader` before you close the first instance of it in your `else` block. – entropic Jul 07 '14 at 21:02
  • possible duplicate of [Proper use of the IDisposable interface](http://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface) – Rune FS Jul 07 '14 at 21:15
  • Thank you @entropic That was the problem! I was confused because i closed that StreamReader immediately after the if statement opens. Why do i need to close it again? furthermore why does it allow me to open an instance of a StreamWriter to access that file, if the sr actually isnt closed until the if statement closes? – Michael Jul 08 '14 at 13:16
  • What do you mean? If you're in the `else` block, anything in the `if` block never executes - so you never actually close the first `StreamReader`.... – entropic Jul 08 '14 at 13:21
  • @entropic ohh thats right! I see. Thank you for pointing that all out. I wish i had better eyes for those stupid mistakes. Hopefully, i'll get better with practice. – Michael Jul 08 '14 at 14:28

2 Answers2

2

Your writing code should be changed in this way

string file = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath),"Settings.txt");

// First read the two lines in memory
string[] lines = File.ReadAllLines(file);

// then use the StreamWriter that locks the file
using(StreamWriter sw = new StreamWriter(file))
{
     lines[2] = "TimeSinceResart: " + timeSinceRestart;
     foreach (string s in lines)
         sw.WriteLine(s);
}

In this way the lock on the StreamWriter doesn't block the reading with FileReadAllLines.
Said that, please note a couple of things. Do not create path strings with string concatenation, use the static methods of the Path class. But most important, when you create a disposable object like a stream be sure to use the using statement to close correctly the file

To complete the answer in response to your comment. Using statement also for the first part of your code

private void checkLastResart()
{
    string file = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath),"Settings.txt");
    using(StreamReader sr = new StreamReader(file))
    {
        if (sr.ReadLine() == null)
        {
            sr.Close();
            MessageBox.Show(...)
            using(StreamWriter sw = new StreamWriter(file, false))
            {
                sw.WriteLine("Conversion Complete Checkbox: 0");
                sw.WriteLine("Default Tool: 0");
                sw.WriteLine("TimeSinceResart: 0");
                sw.Flush();
            }
        }
        else
        {
            ....
        }
    } // exit using block closes and disposes the stream
}
Steve
  • 213,761
  • 22
  • 232
  • 286
  • This is not the correct answer to my actual question; however, it is the correct solution to an error waiting to happen. (It did happen after i fixed the initial error in the code block that executes before it). – Michael Jul 08 '14 at 13:08
  • This is not the correct answer to my actual question; however, it is the correct solution to an error waiting to happen. The actual solution to the error i was getting was because i did not close the StreamReader sr AFTER the 'if statement' Finished. I was confused because i did close that sr immediately after the if statement opened up, but it needs to be closed after the if statement finishes as well. The error you're talking about happened to me after i fixed the initial error in the code block that executes before it. Thank you very much :) – Michael Jul 08 '14 at 13:14
  • I see, another reason to use the [using statement](http://msdn.microsoft.com/en-us/library/yh598w02.aspx) that helps to avoid this intricated situation – Steve Jul 08 '14 at 13:18
  • I'm fairly new to c# from what i have gathered is the using statement basically a way to use an object/method/etc within a set scope? Will the using statement automatically close/dispose everything without me having to manually specify that? – Michael Jul 08 '14 at 14:25
  • Yes, but you could get more infos here http://stackoverflow.com/questions/911408/does-stream-dispose-always-call-stream-close-and-stream-flush and here http://stackoverflow.com/questions/707336/will-a-using-clause-close-this-stream and here http://stackoverflow.com/questions/278902/using-statement-vs-try-finally – Steve Jul 08 '14 at 14:51
0

Where you create sr2, sr still has settings.txt open.

Tim Coker
  • 6,484
  • 2
  • 31
  • 62