0

I currently have the code below to save some data to a .csv file. Everything works properly, but the one problem I am having is if the .csv file is open, and you try to save a new set of data the app crashes. I think making it a read only will prevent this problem, but i am not sure how to do that. For example. if the last file is saved at C:\Users\Documents\data.csv, and the user has that data.csv file open. Then the user saves another set of data using the same path, the app then crashes.

SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.FileName = tbSave.Text;
saveFileDialog.Filter = ".csv|*.csv";
if(saveFileDialog.ShowDialog() == DialogResult.OK)
{
    File.WriteAllText(saveFileDialog.FileName, sb.ToString());
    MessageBox.Show("Save Complete!" + "\n" + "Number of skips recorded: " + skips.ToString() + "\n" + "Elasped time recorded (ms): " + time.ToString());
}
Sach
  • 10,091
  • 8
  • 47
  • 84
user9964422
  • 119
  • 3
  • 15
  • 2
    Use a Try-Catch – LarsTech Jul 10 '18 at 20:42
  • 1
    Which application has an open handle on the file? Why is it open? Does the user open it? Does the application not properly close it? – Dan Wilson Jul 10 '18 at 20:42
  • The user would have opened the .csv to look at the data. Then if they go to collect another data set without closing it, this error would occur – user9964422 Jul 10 '18 at 20:43
  • If you want to make a file read only, you can use `FileStream` and `StreamReader` or `StreamWriter` depending on whether you're reading or writing. – Sach Jul 10 '18 at 20:45
  • 1
    My $0.02...give the user the ability to view the collected data through the application so that access to the actual data file is restricted. – Dan Wilson Jul 10 '18 at 20:46
  • Possible duplicate of [Is there a way to check if a file is in use?](https://stackoverflow.com/questions/876473/is-there-a-way-to-check-if-a-file-is-in-use) – Bakri Bitar Jul 10 '18 at 20:50
  • 1
    This is not a realistic concern. Nobody intentionally overwrites a .csv file if it was not yet processed. That would always, always be bad because it causes data loss. You want that crash, but it doesn't happen enough. A typical strategy your customer will use is to give them names that are based on a work order or day. – Hans Passant Jul 10 '18 at 20:51

2 Answers2

1

I made the saved file a read only. This ensures it can't be written over, and the app won't crash if the excel file is open. Within the context of my purpose this solution works fine.

SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.FileName = tbSave.Text;
saveFileDialog.Filter = ".csv|*.csv";
if(saveFileDialog.ShowDialog() == DialogResult.OK)
{    
    File.WriteAllText(saveFileDialog.FileName, sb.ToString());
    File.SetAttributes(saveFileDialog.FileName, FileAttributes.ReadOnly);
    MessageBox.Show("Save Complete!" + "\n" + "Number of skips recorded: " + skips.ToString() + "\n" + "Elasped time recorded (ms): " + time.ToString());
}
user9964422
  • 119
  • 3
  • 15
0

You can't write data to a file opened by Excel. That's the core problem of the question.

Solution: Must close the file before write to it.

To do that, you must handle the case when the file is being opened by the user.

if(saveFileDialog.ShowDialog() == DialogResult.OK)
{
    try{
        File.WriteAllText(saveFileDialog.FileName, sb.ToString());
    }
    catch(Exception ex)
    {
        //file is being opened.
    }
}

In the catch section, there are 3 choices you can do: 1. You ask the user to close the file and then come back to your application.

catch(Exception ex)
{
     //file is being opened.
     MessageBox.Show("close file xxx please, then redo your work");
}

2. A solution more elegant is to alert the user that we will close Excel. Do we like that? Yes, we kill all process Excel and continue executing your code.

    catch(Exception ex)
    {
         //file is being opened.
         MessageBox.ShowDialog("Kill all Excel?") == No
         return;

         Process myProcess = new Process();
         myProcess.EnableRaisingEvents = true;
         myProcess.Exited += new EventHandler(myProcess_Exited);
         myProcess.Start("CMD.exe","taskkill /f /im excel.exe");         
    }

private void myProcess_Exited(object sender, System.EventArgs e)
    {

        eventHandled = true;
        File.WriteAllText(saveFileDialog.FileName, sb.ToString());
    }
  1. A solution more difficile is to delete only one instance Excel of your file. But it's can create exception because we can't handle correctly behavior of Excel

Like the 2nd solution but

myProcess.Start("CMD.exe","taskkill /FI "WindowTitle eq Microsoft Excel - filename.xls");
Antoine V
  • 6,998
  • 2
  • 11
  • 34