The code that I have included below successfully writes to a CSV file. But if the CSV file that I am writing to happens to be open in Excel, I get a System.IO.Exception that indicates that "the file is being used by another process."
How can I change my code so that the program will continuing running and wait until the CSV is no longer open in Excel?
private void timer1_Tick(object sender, EventArgs e)
{
int actmonth, actyear, actsecond;
System.DateTime fecha = System.DateTime.Now;
actmonth = fecha.Month;
actyear = fecha.Year;
if (actmonth <= 9)
{
valorfechaact = System.Convert.ToString(actyear) + "00" + System.Convert.ToString(actmonth);
}
else
{
valorfechaact = System.Convert.ToString(actyear) + "0" + System.Convert.ToString(actmonth);
}
actsecond = fecha.Second;
string label;
label = label1.Text;
string at = "#";
string filename = valorfechaact + ".csv";
string ruta3 = System.IO.Path.Combine(at, label, filename);
if (Directory.Exists(label1.Text))
{
StreamWriter wr = new StreamWriter(ruta3, true);
wr.WriteLine("1asd" + actsecond);
wr.Close();
wr.Dispose();
}
else
{
System.Console.WriteLine("no se puede escribir en el archivo");
timer1.Stop();
}
}
You can write a Methode which try to open the File with a FileStream and return a boolean Flag
A possible Solution is
public static class FileInfoExtension
{
public static bool IsLocked(this FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
}
catch (IOException)
{
return true;
}
finally
{
stream?.Close();
}
return false;
}
}
Then you can use it
var fileInfo = new FileInfo(ruta3);
if (!fileInfo.IsLocked())
{
// do code
}
A very simple (and bad) Solution to wait is
while (file.IsLocked())
{
Thread.Sleep(100);
}
General is your Code unclear and difficult to read.
You have much redudant code and few variable are bad named.
Maybe this Guidline can help you https://github.com/dennisdoomen/CSharpGuidelines
Maybe a little bit clearer solution is
private void timer1_Tick(object sender, EventArgs e)
{
var directory = label1.Text;
if (!Directory.Exists(directory))
{
Console.WriteLine("no se puede escribir en el archivo");
timer1.Stop();
return;
}
var now = DateTime.Now;
_valorfechaact = now.Month <= 9 ? $"{now.Year}00{now.Month}" : $"{now.Year}0{now.Month}";
var fullname = Path.Combine("#", directory, $"{_valorfechaact}.csv");
var fileInfo = new FileInfo(fullname);
if (fileInfo.IsLocked())
{
Console.WriteLine($"The File {fullname} is locked!");
return;
}
using (var wr = new StreamWriter(fullname, true))
{
wr.WriteLine("1asd" + now.Second);
}
}
You could take a look at this question:
Checking if an Excel Workbook is open
One of the approaches that are discussed is to simply attempt to access the file. If that throws an exception, you can wait and try again.
If you really want to wait until the workbook is writable you can do that, e.g. by using a while loop (probably you'll want to add a time out, or if relevant alert the user that he/she needs to close the file in Excel).
In code it could be something like:
int someLargeNumberOfIterations = 100000000;
while(FileIsLocked(filepath) && elapsedMs < timeoutMs) {
Thread.SpinWait(someLargeNumberOfIterations);
// Set elapsed
}
// Write the file
where FileIsLocked is a function you write based on the aforementioned post and timeoutMs is some appropriate timeout.
Related
I'm writing a program which copies an excel file to another location and removes the sheets except for the visible sheets and saving the copied file. I have used the BackgroundWorker class in order to achieve this.
First, I initialized the Background Worker methods.
private void InitializeBackgroundWorker()
{
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
}
"BackgroundWorker.DoWork()" method is as follows.
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
GenerateReports(worker);
// Cancel the asynchronous operation.
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
worker.ReportProgress(100);
if(backgroundWorker.IsBusy)
{
this.backgroundWorker.CancelAsync();
}
}
The "GenerateReports()" method contains the "ExtractVisibleSheets()" method which extracts the visible sheets, which then calls the "CopyVisibleSheets()" method.
private void ExtractVisibleSheets(String originalDirectory, String convertedDirectory)
{
//Get the .xlsx files of the original reports and the converted reports
visibleSheetsOriginal = Directory.GetFiles(originalDirectory, "*.xlsx");
visibleSheetsConverted = Directory.GetFiles(convertedDirectory, "*.xlsx");
//Copy the visible sheets to the defined workbooks
//Sample Reports
CopyVisibleSheets(originalDirectory, visibleSheetsOriginal, visibleSheetsBasePath);
//Converted Reports
CopyVisibleSheets(convertedDirectory, visibleSheetsOriginal, visibleSheetsConvertedPath);
}
private void CopyVisibleSheets(String directory, String[] excelFiles, String path)
{
excelApplication = null;
workbook = null;
Excel.Worksheet sheet = null;
String copiedReport = "";
try
{
foreach(String report in excelFiles)
{
copiedReport = path + "\\" + report.Substring(report.LastIndexOf('\\') + 1);
excelApplication = GetExcelApplication();
File.Copy(report, copiedReport);
OpenXmlFileProcessor.RemoveCustomProperty(copiedReport, FileProcessor.BaClientVerParam);
workbook = excelApplication.Workbooks.Open(copiedReport);
EnableDisableAlertsAndEvents(false);
for (int i = workbook.Worksheets.Count; i > 0; i--)
{
sheet = excelApplication.ActiveWorkbook.Worksheets[i];
if(sheet.Visible != XlSheetVisibility.xlSheetVisible)
{
sheet.Visible = XlSheetVisibility.xlSheetVisible;
sheet.Delete();
}
}
workbook.Save();
EnableDisableAlertsAndEvents(true);
workbook.Close();
Marshal.ReleaseComObject(workbook);
}
}
finally
{
QuitAndReleaseExcelApplication(false);
}
}
"BackgroundWorker.RunWorkerCompleted()" method is given below
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
// Next, handle the case where the user cancelled
// the operation.
}
else
{
// Finally, handle the case where the operation
// succeeded.
MessageBox.Show("Directory Generation Successful!");
}
EnableControls();
}
But an error occurs during the line "File.Copy(report, copiedReport)" as follows and is fired up from the "BackgroundWorker.RunWorkerCompleted()" method.
Error
Do let me know if someone knows the reason for this error.
As a rule, the system C: drive requires admin privileges for writing. I'd suggest choosing another drive or folder (application data).
path + "\\" + report.Substring(report.LastIndexOf('\\') + 1);
try to use double qute "" (report.LastIndexOf('\\') + 1);
its a type of strings
try to use path + "//" + report.Substring(report.LastIndexOf("//") + 1);
correct me if im wrong :)
My issue is that I keep seeing a recurring theme with trying to allow my Notepad clone to save a file. Whenever I try to save a file, regardless of the location on the hard disk, the UnauthorizedAccess Exception continues to be thrown. Below is my sample code for what I've done, and I have tried researching this since last night to no avail. Any help would be greatly appreciated.
//located at base class level
private const string fileFilter = "Text Files|*.txt|All Files|*.*";
private string currentPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
private void MenuFileSaveAs_Click(object sender, RoutedEventArgs e)
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.DefaultExt = "*.txt";
sfd.Filter = fileFilter;
sfd.AddExtension = true;
sfd.InitialDirectory = currentPath;
sfd.RestoreDirectory = true;
sfd.OverwritePrompt = true;
sfd.ShowDialog();
try
{
System.IO.File.WriteAllText(currentPath,TxtBox.Text,Encoding.UTF8);
}
catch (ArgumentException)
{
// Do nothing
}
catch(UnauthorizedAccessException)
{
MessageBox.Show("Access Denied");
}
}
Change the following lines.
...
if (sfd.ShowDialog() != true)
return;
try
{
using (var stream = sfd.OpenFile())
using (var writer = new StreamWriter(stream, Encoding.UTF8))
{
writer.Write(TxtBox.Text);
}
}
...
I hope it helps you.
You need to get the correct path context and file object from the dialog box once the user has hit 'ok'. Namely verify the user actually hit ok and then use the OpenFile property to see what their file selection is:
if (sfd.ShowDialog.HasValue && sfd.ShowDialog)
{
if (sfd.OpenFile() != null)
{
// convert your text to byte and .write()
sfd.OpenFile.Close();
}
}
I am having issue writing/Reading string into file with BackgroundWorker
But I don't know where is it hapenning.
When i click "start" on my app, i'm checking whether there's a first line or not in a file :
StreamWriter writeToCsv;
public string filename;
public bool canAcces = false;
public bool enteteExiste = false;
private void start_Click(object sender, EventArgs e)
{
filename = filename_box.Text;
if (filename_valid(filename) == false)
{
MessageBox.Show("Nom du fichier incorrect \n Seuls les caractères propre a Windows sont autorisés \n Le fichier doit se terminer par .csv");
}
//DEMARRAGE DE LA PROCEDURE
boxLogs.Clear();
if (filename_valid(filename))
{
try
{
verifieEntete();
//INSERTION DE L'ENTETE DU FICHIER CSV
writeToCsv = new StreamWriter(boxFilePath.Text + "\\" + filename, true);
canAcces = true;
}
}
}
This task is completed synchronous. It's the first thing that the program do.
The function "verifieEntete()" is changing a boolean, "enteteExiste"
public void verifieEntete()
{
string absolutFilePath = boxFilePath.Text + '\\' + filename;
if (!File.Exists(absolutFilePath))
{
File.Create(absolutFilePath).Close();
}
String[] fileContent = File.ReadAllText(absolutFilePath).Split(',');
for (int i = 0; i < fileContent.Length; i++)
if (fileContent[i].Contains("MAC;SERIAL;IP;MODELE;MODULE-EXT;NUM-COURT;SITE"))
enteteExiste = true;
}
Now, here comes the asynchronous part.
I did this :
public void startParListe()
{
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
if (bw.IsBusy != true)
bw.RunWorkerAsync();
}
And in my bw_DoWork function, here are the first lines :
public void bw_DoWork(object sender, DoWorkEventArgs e)
{
countPlages = listePlages.Items.Count;
if (countPlages != 0 && boxFilePath.Text != "" && canAcces == true && filename_valid(filename))
{
tableauPlages = new string[countPlages, 2];
if (enteteExiste == false)
{
writeToCsv.WriteLine("MAC;SERIAL;IP;MODELE;MODULE-EXT;NUM-COURT;SITE");
}
}
}
Here's the issue :
The program runs, create the file (if not exists) then should put a first line in it :
writeToCsv.WriteLine("MAC;SERIAL;IP;MODELE;MODULE-EXT;NUM-COURT;SITE");
But instead, the program create the file, but don't put anything in it. And even worst, the program does not end. As if it's looping on this WriteLine but never write it.
I have tons of other instructions after that, but i can see that none of its are executed.
What is wrong with the background worker and Streamwriter ?
In general it is better to close and dispose your StreamWriter when you're done with it:
using (var writer = new StreamWriter(boxFilePath.Text + "\\" + filename, true))
{
canAcces = true;
}
Reopen a new StreamWriter when you are writing the actual data.
if (enteteExiste == false)
{
using (var writer = new StreamWriter(boxFilePath.Text + "\\" + filename, true))
{
writer.WriteLine("MAC;SERIAL;IP;MODELE;MODULE-EXT;NUM-COURT;SITE");
}
}
If you don't dispose, a file handle will remain open, which can lead to hard to debug issues, especially in a multi-threading environment.
If performance is an issue with this approach, consider to write the data to a buffer and write that buffer to a file in longer intervals.
I think this will help.
I've got this code at the start of the form that reads a file that already exists and sets value of 4 textBoxes accordingly to what it's written inside. How do I proceed if the file hasn't yet been created? Any help would be very appreciated.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
FileStream file = new FileStream("cenaEnergentov.txt", FileMode.Open, FileAccess.Read);
StreamReader sr = new StreamReader(file);
sr.ReadLine();
var textLines = File.ReadAllLines("cenaEnergentov.txt");
foreach (var line in textLines)
{
string[] dataArray = line.Split(';');
textBox1.Text = (dataArray[0]);
textBox2.Text = (dataArray[1]);
textBox3.Text = (dataArray[2]);
textBox4.Text = (dataArray[3]);
}
}
If the uper is a false I'd like to proceed with normal script down below that starts with:
public void trackBar1_Scroll(object sender, EventArgs e)
{
......
Use a simple if statement
// I edit this line according to your comment
if(File.Exists(String.Concat("cenaEnergentov".ToUpper(), ".txt"))
{
// do your job
}
else
{
// call appropriate method
trackBar1_Scroll(this,EventArgs.Empty); // for example
}
Try this before you open the file:
var filename = "filename.txt";
if (!File.Exists(filename))
{
File.Create(filename);
}
This won't account for the fact that you're assigning values without checking to see if they exist first. Implementing that is relatively trivial as well.
It also appears that the FileStream and StreamReader are redundant. Just use File.ReadAllLines instead.
The previous solutions will work OK... however they don't really answer the big question:
How do I know when to continue?
The best way would be to use a FileSystemWatcher:
var watcher = new FileSystemWatcher(path, ".txt");
watcher.Created += (sender, e) =>
{
if (e.ChangeType == WatcherChangeTypes.Created)
initForm();
};
Where initForm() is:
void initForm()
{
if(File.Exists(path))
{
// Update form
}
else
{
var watcher = new FileSystemWatcher(path, ".txt");
watcher.Created += (sender, e) =>
{
if (e.ChangeType == WatcherChangeTypes.Created)
initForm();
};
}
}
try this
if(File.Exists("yourFile.txt"))
{
//do what you do
}
else
{
// call appropriate method
}
I am creating a file using file stream, but before that i am applying if condition to see if the file exist or not. When i click on button and if supppose file is there it deletes the file. Its ok, and again if i press the button the file gets created. At first time it works well.
Now the file is created, again if I press the button and it should delete but it is trhowing an exception saying that*The process cannot access the file 'C:\Hello1' because it is being used by another process.*
Below is my code
private void button2_Click(object sender, EventArgs e)
{
string fileName = #"C:\Hello1";
if
(File.Exists(fileName))
{
File.Delete(fileName);
MessageBox.Show("File is deleted");
}
else
{
FileInfo createFile = new FileInfo(fileName);
FileStream create = createFile.Create();
MessageBox.Show("Created");
}
}
So why I am not able to delete second time, My text file is not open also but still it is showing the exception.
You're never closing your stream that created the file. Put your FileStream in a using statement, which will automatically clean up the open file handle.
using(FileStream create = createFile.Create())
{
//code here
}
The file stream is still opened when you're trying second time, try this:
private void button2_Click(object sender, EventArgs e)
{
string fileName = #"C:\Hello1";
if
(File.Exists(fileName))
{
File.Delete(fileName);
MessageBox.Show("File is deleted");
}
else
{
FileInfo createFile = new FileInfo(fileName);
using(FileStream create = createFile.Create())
{
MessageBox.Show("Created");
}
}
}
Oh yes i got the answer,
I need to use
private void button2_Click(object sender, EventArgs e)
{
string fileName = #"C:\Hello1";
if
(File.Exists(fileName))
{
File.Delete(fileName);
MessageBox.Show("File is deleted");
}
else
{
FileInfo createFile = new FileInfo(fileName);
FileStream create = createFile.Create();
MessageBox.Show("Created");
create.Close();
}
}
We need to use create.Close();
Here is an example I used to write a local log:
StreamWriter log;
string fpath = string.Format(#"{0}\{1}.txt",GetDirectory(),DateTime.Now.ToString("yyy-MM-dd"));
if (!File.Exists(fpath))
{
log = new StreamWriter(fpath);
}
else
{
log = File.AppendText(fpath);
}
log.WriteLine(string.Format("{0} ==> {1}",DateTime.Now.ToString("MM/dd/yyy HH:mm:ss"), Message));
log.Dispose();
log = null;