I have checked all other solutions, nothing is working.
I am calling an asynchronous logging method from different button events.
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() => LoggerTest());
}
private async void button2_Click(object sender, EventArgs e)
{
await Task.Run(() => LoggerTest());
}
private async void LoggerTest()
{
for (int i = 0; i < 10000; i++)
{
Logger.Log(string.Format("Counter: {0}", i));
Thread.Sleep(10);
}
}
Log method uses StreamWriter
private void Log(string log)
{
if (!IsFileLocked(fullPath))
{
using (StreamWriter file = new StreamWriter(fullPath, append: true))
{
file.WriteLine(log);
file.Close(); // I know this is unnecessary in the using block
}
}
}
private bool IsFileLocked(string file)
{
try
{
using (var stream = File.OpenRead(file))
return false;
}
catch (IOException)
{
return true;
}
}
When I click on button1 and button2, the below exception is caught:
System.IO.IOException: 'The process cannot access the file 'C:..\x.txt' because it is being used by another process.'
try something like this
private static readonly object locker = new object();
lock (locker)
{
using (FileStream fileStream = new FileStream("FilePath"), FileMode.Append))
{
using (StreamWriter writer = new StreamWriter(fileStream))
{
writer.WriteLine(log);
}
}
}
lock keyword will lock the stream writer till the current writer process is finished
I'm developping an app in C# using microsoft visual studio (windows form).
What i want to do is to manage different environment through one GUI.
Thus, my gui have to start asynchronously some process (which are commandline applications).
The problem is that I can get the standard output of the process only once it's finished, meaning I can't show what the process is doing in runtime.
since the applications I want to run can take quite a long runtime (uploading big files ...) i would like to display the process output in runtime.
Thus, i created a backgroundworker to separate my gui from the process, and i tried to use a temporary file where the process output is written.
then using a FileSystemWatcher, I could use the "change" event to display the messages in my GUI.
My problem is that since the temporary file is open for writting, i can't read from it at the same time.
Here is my code, does anyone have a way to bypass this problem ? or an other way to do it ?
public partial class Form1 : Form
{
Boolean done = false;
private FileSystemWatcher observateur;
public Form1()
{
InitializeComponent();
// delete the temporary file if existing
if (System.IO.File.Exists("C:\\testoutput.txt"))
{
try
{
System.IO.File.Delete("C:\\testoutput.txt");
}
catch (System.IO.IOException exept)
{
Console.WriteLine(exept.Message);
return;
}
}
File.Create("C:\\testoutput.txt");
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler
(backgroundWorker1_ProgressChanged);
observateur = new FileSystemWatcher();
observateur.Filter = "C:\\testoutput.txt";
observateur.Changed += new FileSystemEventHandler(this.OnChanged);
observateur.Created += new FileSystemEventHandler(this.OnCreate);
}
private void OnChanged(object source, FileSystemEventArgs e)
{
// I tried to bypass the problem of having the file opened by copying it but i doesn't work
File.Copy("C:\\testouput.txt", "C:\\TEMP.txt", true);
}
private void OnCreate(object source, FileSystemEventArgs e)
{
Console.WriteLine("Created");
}
private void button3_Click(object sender, EventArgs e)
{
string outputworker = "";
backgroundWorker1.RunWorkerAsync(outputworker);
while (!done)
{
string text = System.IO.File.ReadAllText("C:\\TEMP.txt");
Thread.Sleep(200);
}
}
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
outputTextArea.Text = "Processing......" + progressBar1.Value.ToString() + "%";
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string[] args = { "/k " };
string outputWork = e.Argument as string;
backgroundWorker1.ReportProgress(10);
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.WorkingDirectory = "C:\\XXXXXXXXXX";
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "cmd.exe";
int nArgs = args.Length;
if (nArgs > 0)
{
process.StartInfo.Arguments = args[0];
}
for (int i = 1; i < nArgs; i++)
{
process.StartInfo.Arguments = String.Concat(process.StartInfo.Arguments, " && ", args[i]);
}
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
backgroundWorker1.ReportProgress(20);
process.Start();
backgroundWorker1.ReportProgress(40);
System.IO.StreamWriter sIn = process.StandardInput;
sIn.WriteLine("ExternalCommandLineApp1.exe >> C:\\testoutput.txt");
backgroundWorker1.ReportProgress(60);
sIn.WriteLine("ExternalCommandLineApp1.exe >> C:\\testoutput.txt");
System.IO.StreamReader sOut = process.StandardOutput;
backgroundWorker1.ReportProgress(90);
sIn.WriteLine("EXIT");
outputWork = sOut.ReadToEnd();
process.Close();
backgroundWorker1.ReportProgress(100);
e.Result = outputWork;
done = true;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
string output = e.Result as string;
//outputTextArea.Text = output;
}
}
This is not the best way as mentioned in other answers, but it still can work successfully.
You can open a file for reading/writing without blocking other reads/writes. Just use File.Open instead of helper methods and provide additional parameters (FileMode and FileShare)
Here is a complete example. Note that one thread keeps file opened for writing and second thread opens and closes file every time and reads all lines:
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string fileName = "c:\\_data\\temp.txt";
Task writer = new Task(() => {
using (FileStream fs = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite))
using (StreamWriter sw = new StreamWriter(fs))
{
for (int i = 0; i < 50; i++ )
{
sw.WriteLine(DateTime.Now.Millisecond.ToString());
sw.Flush();
Thread.Sleep(500);
}
}
});
Task reader = new Task(() => {
for (int i = 0; i < 50; i++)
{
Thread.Sleep(500);
Console.WriteLine("Read again");
if (File.Exists(fileName))
{
using (FileStream fs = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
using (StreamReader r = new StreamReader(fs))
{
while (!r.EndOfStream)
{
Console.WriteLine(r.ReadLine());
}
}
}
}
});
writer.Start();
reader.Start();
writer.Wait();
reader.Wait();
}
}
}
The simplest way with what you've already got is to exploit the UserState you can pass with the BackgroundWorker.
In the backgroundWorker1_DoWork method, you can use
backgroundWorker1.ReportProgress(0, "Whatever text you want to send right now.");
And in backgroundWorker1_ProgressChanged, you can read the message and put it in the text box like this:
outputTextArea.AppendText((e.UserState as string) + "\r\n");
This is a bit inefficient, but it should be much safer and faster than your original solution anyway.
In .NET, you've got many options of passing data between threads. If you want to learn more about the concepts, problems and solutions of multi-threading, you can give this a go: http://www.albahari.com/threading/
You can get the Standard Output of processes using System.Diagnostics.Process StandardOutput property (it's a Stream).
http://msdn.microsoft.com/en-us/library/vstudio/system.diagnostics.process.standardoutput(v=vs.90).aspx
I suggest you use Windows Communications Foundation to do this.
Following is a complete example.
There are two helper classes that you would normally put into a class library for reuse, class WcfServiceHost<T> and class WcfServiceProxy<T>.
This is a console app which you should run from the command line twice, passing a parameter of monitor for the first instance you start, and worker for the second instance.
Run it from the command like like this (assuming the app is called ConsoleApp1.exe):
start ConsoleApp1.exe monitor
start ConsoleApp1.exe worker
and then look at the output. The monitor instance is waiting for progress reports from the worker. The worker instance is reporting the progress, effectively by calling a function in the monitor instance (RPC, or Remote Procedure Call).
Here's the complete code. You will need to reference System.ServiceModel:
using System;
using System.Diagnostics;
using System.ServiceModel;
using System.Threading;
namespace Demo
{
[ServiceContract]
interface IProgressReporter
{
[OperationContract]
void ReportProgress(double percentComplete, string message);
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
sealed class Monitor: IProgressReporter
{
public void ReportProgress(double percentComplete, string message)
{
Console.WriteLine("Monitor received progress - Completed {0}%: {1}", percentComplete, message);
if (percentComplete == 100)
{
Program.ReportFinished();
}
}
}
public sealed class WcfServiceHost<T>: IDisposable where T: class
{
public WcfServiceHost(T service, string wcfEndpointAddress)
{
_service = service;
_wcfEndpointAddress = wcfEndpointAddress;
var serviceHost = new ServiceHost(service);
serviceHost.AddServiceEndpoint(typeof(T), new NetNamedPipeBinding(), wcfEndpointAddress);
serviceHost.Open();
_serviceHost = serviceHost;
}
public T Service
{
get
{
return _service;
}
}
public string WcfEndpointAddress
{
get
{
return _wcfEndpointAddress;
}
}
/// <summary>Disposal.</summary>
public void Dispose()
{
if (_serviceHost != null)
{
try
{
_serviceHost.Close();
}
catch (Exception exception) // Don't allow exceptions to escape from Dispose().
{
Trace.WriteLine("There was an exception while closing the host: " + exception.Message);
}
}
}
private readonly T _service;
private readonly string _wcfEndpointAddress;
private readonly ServiceHost _serviceHost;
}
public sealed class WcfServiceProxy<T>: IDisposable where T: class
{
public WcfServiceProxy(string wcfEndpointAddress)
{
_wcfEndpointAddress = wcfEndpointAddress;
_channelFactory = new ChannelFactory<T>(new NetNamedPipeBinding(), _wcfEndpointAddress);
_service = _channelFactory.CreateChannel();
_comms = _service as ICommunicationObject;
if (_comms == null)
{
throw new InvalidOperationException("proxy does not implement ICommunicationObject.");
}
}
public T Service
{
get
{
return _service;
}
}
public string WcfEndpointAddress
{
get
{
return _wcfEndpointAddress;
}
}
public void Dispose()
{
closeComms();
closeChannelFactory();
}
private void closeComms()
{
try
{
_comms.Close();
}
catch (CommunicationException exception) // Not closed - call Abort to transition to the closed state.
{
Debug.WriteLine("CommunicationException while closing ICommunicationObject: " + exception.Message);
_comms.Abort();
}
catch (TimeoutException exception) // Not closed - call Abort to transition to the closed state.
{
Debug.WriteLine("TimeoutException while closing ICommunicationObject: " + exception.Message);
_comms.Abort();
}
catch (Exception exception) // Not closed - call Abort to transition to the closed state.
{
Trace.WriteLine("Unexpected exception while closing ICommunicationObject: " + exception.Message);
_comms.Abort();
}
}
private void closeChannelFactory()
{
try
{
_channelFactory.Close();
}
catch (CommunicationException exception) // Not closed - call Abort to transition to the closed state.
{
Debug.WriteLine("CommunicationException while closing ChannelFactory: " + exception.Message);
_channelFactory.Abort();
}
catch (TimeoutException exception) // Not closed - call Abort to transition to the closed state.
{
Debug.WriteLine("TimeoutException while closing ChannelFactory: " + exception.Message);
_channelFactory.Abort();
}
catch (Exception exception) // Not closed - call Abort to transition to the closed state.
{
Trace.WriteLine("Unexpected exception while closing ChannelFactory: " + exception.Message);
_channelFactory.Abort();
}
}
private readonly T _service;
private readonly string _wcfEndpointAddress;
private readonly ChannelFactory<T> _channelFactory;
private readonly ICommunicationObject _comms;
}
internal static class Program
{
static void Main(string[] args)
{
if (args.Length > 0 && args[0] == "worker")
runWorker();
else
runMonitor();
Console.WriteLine("\nEnded. Press a key to exit.");
Console.ReadKey();
}
public static void ReportFinished()
{
finished.Set();
}
static void runMonitor()
{
using (new WcfServiceHost<IProgressReporter>(new Monitor(), SERVICE_PIPE_NAME))
{
finished.WaitOne();
}
}
static void runWorker()
{
using (var proxy = new WcfServiceProxy<IProgressReporter>(SERVICE_PIPE_NAME))
{
for (int i = 0; i <= 100; ++i)
{
Thread.Sleep(100);
Console.WriteLine("Worker reporting progress: Completed {0}%: {1}", i, i);
proxy.Service.ReportProgress(i, i.ToString());
}
}
}
private static ManualResetEvent finished = new ManualResetEvent(false);
private const string SERVICE_PIPE_NAME = "net.pipe://localhost/MyServicePipeName";
}
}
thanks to you i managed to do what i wanted ^^
Since it took me quite some time to search/debug, I share my solution.
I used a temporary text file, so it's not very "professional" but it works.
To run the process, you have to call :
string[] args = { "/c cmd1", "cmd2" , "cmd3"};
backgroundWorker1.RunWorkerAsync(args);
(sync on a button pressed event for example)
public partial class Form1 : Form
{
string fileName = "c:\\temp\\tempoutput.txt";
public Form1()
{
InitializeComponent();
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler
(backgroundWorker1_ProgressChanged);
}
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// This function fires on the UI thread so it's safe to edit the UI control directly
progressBar1.Value = e.ProgressPercentage;
readTempFile();
//outputTextArea.Text = "Processing......" + progressBar1.Value.ToString() + "%";
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// command line
string[] args = e.Argument as string[];
backgroundWorker1.ReportProgress(2);
try
{
FileStream fs = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
StreamWriter sw = new StreamWriter(fs);
sw.WriteLine("### Starting the process : ###");
sw.Flush();
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.WorkingDirectory = "WorkdirPath";
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "cmd.exe";
// create the command line
int nArgs = args.Length;
if (nArgs > 0)
{
process.StartInfo.Arguments = args[0];
}
for (int i = 1; i < nArgs; i++)
{
process.StartInfo.Arguments = String.Concat(process.StartInfo.Arguments, " && ", args[i]);
}
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
backgroundWorker1.ReportProgress(5);
process.Start();
backgroundWorker1.ReportProgress(10);
System.IO.StreamWriter sIn = process.StandardInput;
System.IO.StreamReader sOut = process.StandardOutput;
backgroundWorker1.ReportProgress(15);
int timeCount = 15;
string tempOut = "";
while (!sOut.EndOfStream)
{
tempOut = sOut.ReadLine();
sw.WriteLine(tempOut);
sw.Flush();
if (timeCount < 90)
{
// increasing the progress bar value.
//timeCount += 1;
}
backgroundWorker1.ReportProgress(timeCount);
}
sw.WriteLine("Closing process");
sw.Flush();
process.Close();
backgroundWorker1.ReportProgress(100);
}
catch (System.IO.IOException exept)
{
Console.WriteLine(exept.Message);
return;
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
readTempFile();
}
private void readTempFile()
{
try
{
FileStream fs = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
StreamReader r = new StreamReader(fs);
string output = r.ReadToEnd();
outputTextArea.Text = output;
}
catch (System.IO.IOException exept)
{
Console.WriteLine(exept.Message);
return;
}
}
}
Please suggest whats wrong with the following code to write names to text file. The file is being created but nothing is written in it. Although program is running fine and there is no exception yet not getting anything in txt file.
class IO
{
public void write(string name)
{
try
{
FileStream fs = new FileStream(#"D:\Infogain\ObjSerial.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite);
StreamWriter sw = new StreamWriter(fs);
sw.BaseStream.Seek(0, SeekOrigin.Current);
sw.Write(name);
fs.Close();
}
catch (Exception ex)
{
Console.WriteLine("Issue in writing: " + ex.Message);
}
}
public static void Main(string[] args)
{
string name;
int ch;
List<string> list = new List<string>();
do
{
Console.WriteLine("Enter name");
name = Console.ReadLine();
IO io = new IO();
io.write(name);
Console.WriteLine("Enter 1 to continue");
ch = Convert.ToInt32(Console.ReadLine());
}while(ch==1);
}
}
You should read up on Object Oriented Programming a little. Creating a new IO object within that loop makes no sense. Also your write function is kind of messed up.
Fixed version:
(note: "write" function appends to file)
public class IO
{
public static void write(string name)
{
try
{
string path = #"e:\mytxtfile.txt";
using (StreamWriter sw = File.AppendText(path))
{
sw.WriteLine(name);
}
}
catch (Exception ex)
{
Console.WriteLine("Issue in writing: " + ex.Message);
}
}
public static void Main(string[] args)
{
string name;
int ch;
List<string> list = new List<string>();
do
{
Console.WriteLine("Enter name");
name = Console.ReadLine();
write(name);
Console.WriteLine("Enter 1 to continue");
ch = Convert.ToInt32(Console.ReadLine());
} while (ch == 1);
}
}
My Service read from com port and save the info in DB.It works perfectly fine for a while,but sometimes the error window is appeared for getting an exception,and if I don't click 'no' buttun , it doesn't read from com port.When I check in event viewer,I see some exception number 7034,7031 on my service.I log every where in my code and I use try,catch .I don't have ant catch in my log file,so I can't understand what is the problem?
public partial class Service1 : ServiceBase
{
string _fileName = #"c:\logSensor\log.ini";
internal delegate void StringDelegate(string data);
ArrayList lines = new ArrayList();
BL.EnterDatas eData = new BL.EnterDatas();
private class Line
{
public string Str;
public Line(string str)
{
Str = str;
}
}
public Service1()
{
InitializeComponent();
CommPort com = CommPort.Instance;
com.Open();
com.StatusChanged += OnStatusChanged;
com.DataReceived += OnDataReceived;
timer1.Enabled = true;
using (System.IO.StreamWriter writer = new System.IO.StreamWriter(_fileName, true))
{
writer.WriteLine(PublicVariable.DateShamsi() + " " + PublicVariable.Nowtime() +
" . " + "Step1:Load ");
writer.Flush();
}
}
protected override void OnStart(string[] args)
{
}
protected override void OnStop()
{
}
private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
try
{
timer1.Stop();
using (System.IO.StreamWriter writer = new System.IO.StreamWriter(_fileName, true))
{
writer.WriteLine(PublicVariable.DateShamsi() + " " + PublicVariable.Nowtime() + "timer1_Elapsed:Stop Timer ");
writer.Flush();
}
ReadLog();
StreamReader sr = new StreamReader(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + "\\path.txt");
string _logPath = sr.ReadLine();
FileStream fs = new FileStream(_logPath, FileMode.Open);
fs.SetLength(0);
fs.Close();
}
catch (Exception ex)
{
using (System.IO.StreamWriter writer = new System.IO.StreamWriter(_fileName, true))
{
writer.WriteLine(PublicVariable.DateShamsi() + " " + PublicVariable.Nowtime() + "Catch:timer1_Elapsed " + ex.Message);
writer.Flush();
}
}
using (System.IO.StreamWriter writer = new System.IO.StreamWriter(_fileName, true))
{
writer.WriteLine(PublicVariable.DateShamsi() + " " + PublicVariable.Nowtime() + "final:timer1_Elapsed ");
writer.Flush();
}
timer1.Start();
}
#region Functions...
...
}
I created a System.Timers.Timer object with an interval of 5000 ms. On the Elapsed event of this timer, I'm searching the new PDF files which appeared on Desktop. If there are new PDF files, I add those to the specific file, but my program catch this error: The process cannot acces the file 'C:\Users\Admin\Desktop\StartupFiles.dat' because it is being used by another process.
Here is my code:
private readonly string fileName = Application.StartupPath + #"\StartupFiles.dat";
private readonly string sourceDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
void timerCheck_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
try
{
if (!File.Exists(fileName))
File.Create(fileName);
string[] PDFiles = Directory.GetFiles(sourceDirectory, "*.pdf", SearchOption.TopDirectoryOnly);
string[] textFile = File.ReadAllLines(fileName);
bool exist;
string addText = string.Empty;
foreach (string s in PDFiles) // Check the files from the desktop with the files from the fileName variabile folder
{
exist = false;
foreach (string c in textFile)
{
if (string.Compare(s, c) == 0)
{
exist = true;
break;
}
}
if (!exist)
{
addText += s + '\n';
}
}
if (!string.IsNullOrEmpty(addText)) // If a new PDF appeard on the desktop, save it to file
{
using (StreamWriter sw = File.AppendText(fileName))
{
sw.Write(addText);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Maybe I have to set a little delay between ReadAllLines and File.AppendText ?
#charqus, This should Work
if (!File.Exists(fileName))
File.Create(fileName).Dispose();
string[] PDFiles = Directory.GetFiles(sourceDirectory, "*.pdf", SearchOption.TopDirectoryOnly);
List<String> fileList = new List<String>();
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
using (BinaryReader r = new BinaryReader(fs))
{
fileList.Add(r.ReadString());
}
}
string[] textFile = fileList.ToArray();
Calling the Dispose method ensures that all the resources are properly released.