views:

334

answers:

3

Ok, so to explain; I am developing for a system that can suffer a power failure at any point in time, one point that I am testing is directly after I have written a file out using a StreamWriter. The code below:

// Write the updated file back out to the Shell directory.
using (StreamWriter shellConfigWriter =
        new StreamWriter(@"D:\xxx\Shell\Config\Game.cfg.bak"))
{
    for (int i = 0; i < configContents.Count; i++)
    {
        shellConfigWriter.WriteLine(configContents[i]);
    }
    shellConfigWriter.Close();
}

FileInfo gameCfgBackup = new FileInfo(@"D:\xxx\Shell\Config\Game.cfg.bak");
gameCfgBackup.CopyTo(@"D:\xxx\Shell\Config\Game.cfg", true);

Writes the contents of shellConfigWriter (a List of strings) out to a file used as a temporary store, then it is copied over the original. Now after this code has finished executing the power is lost, upon starting back up again the file Game.cfg exists and is the correct size, but is completely blank. At first I thought that this was due to Write-Caching being enabled on the hard drive, but even with it off it still occurs (albeit less often).

Any ideas would be very welcome!

Update: Ok, so after removing the .Close() statements and calling .Flush() after every write operation the files still end up blank. I could go one step further and create a backup of the original file first, before creating the new one, and then I have enough backups to do a integrity check, but I don't think it'll help to solve the underlying issue (that when I tell it to write to, flush and close a file... It doesn't!).

+6  A: 

First of all, you don't have to call shellConfigWriter.Close() there. The using statement will take care of it. What you might want to do instead to guard against power failure is call shellConfigWriter.Flush().


Update
Something else you might want to consider is that if a power failure can really happen at any time, it could happen in the middle of a write, such that only some of the bytes make it to a file. There's really no way to stop that.

To protect against these scenarios, a common procedure is to use state/condition flag files. You use the existence or non-existence on the file system of a zero-byte file with a particular name to tell your program where to pick up again when it resumes. Then you don't create or destroy the files that trigger a particular state until you are sure you've reached that state and completed the previous.

The downside here is that it might mean throwing a lot of work away now and then. But the benefit is that it means the functional part of your code looks like normal: there's very little extra work to do to make the system sufficiently robust.

Joel Coehoorn
Agreed, I didn't have the .Close() method in there originally for that exact reason, but I was running out of things to try! I'll try flushing after every write call, see what happens then.
Siyfion
No luck, still didn't write a single line.
Siyfion
A: 

You want to set AutoFlush = true;

leppie
Yeh I could, but for the purpose of this example code it's 1 line either way; flush every for iteration or set AutoFlush to true. It still produces the same result, which is that it still produces a blank file.
Siyfion
Are you sure you are not just writing blank lines to the file? ;p
leppie
+3  A: 

Keep the OS from buffering the output using the FileOptions parameter of the FileStream object's constructor:

    using (Stream fs = new FileStream(@"D:\xxx\Shell\Config\Game.cfg.bak", FileMode.Create, FileAccess.Write, FileShare.None, 0x1000, FileOptions.WriteThrough))
    using (StreamWriter shellConfigWriter = new StreamWriter(fs))
    {
        for (int i = 0; i < configContents.Count; i++)
        {
            shellConfigWriter.WriteLine(configContents[i]);
        }
        shellConfigWriter.Flush();
        shellConfigWriter.BaseStream.Flush();
    }
Jesse C. Slicer
Fantastic! Great find ;)
Siyfion
An alternative to "new FileStream" is the static File.Create method: "File.Create(@"D:\xxx\Shell\Config\Game.cfg.bak", 0x1000, FileOptions.WriteThrough, null)". It has a few less parameters and might look cleaner in the code.
Jesse C. Slicer