C# FileSystemWatcher WaitForChanged Method only detects one file change - c#

I got a problem similar to this one: FileSystemWatcher - only the change event once firing once?
But since that thread is two years old and my code is a bit different, I decided to open a new question.
Well, here's my code:
while (true)
{
FileSystemWatcher fw = new FileSystemWatcher();
fw.Path = #"Z:\";
fw.Filter = "*.ini";
fw.WaitForChanged(WatcherChangeTypes.All);
Console.WriteLine("File changed, starting script...");
//if cleanup
try
{
if (File.ReadAllLines(#"Z:\file.ini")[2] == "cleanup")
{
Console.WriteLine("Cleaning up...");
Process c = new Process();
c.StartInfo.FileName = System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop).Trim('\\') + #"\clean.exe";
c.StartInfo.WorkingDirectory = System.Environment.SpecialFolder.DesktopDirectory.ToString();
c.Start();
c.WaitForExit();
Console.WriteLine("Done with cleaning up, now starting script...");
}
}
catch
{
Console.WriteLine("No cleanup parameter found.");
}
Process p = new Process();
p.StartInfo.FileName = System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop).Trim('\\') + #"\go.exe";
p.StartInfo.WorkingDirectory = System.Environment.SpecialFolder.DesktopDirectory.ToString();
p.Start();
Console.WriteLine("Script running...");
p.WaitForExit();
fw = null;
Console.WriteLine("Done. Waiting for next filechange...");
}
Problem: This program should detect a file change in the file "Z:\file.ini". If it has changed, a script should be fired. When the script is done, the programm should return to the start and start watching for changes, again (that's why I used the while-loop).
Well, the first change is detected and everything seems to be working just fine, but any changes AFTER the first one are not going to be detected. I tried to set the FileSystemWatcher Object to null, as you can see, but it didn't help.
So, I hope for good answers. Thanks.

I would change your design so you don't rely on the FileSystemWatcher for any changes. Instead poll the directory or the file that your watching for any changes. You can then use the FileSystemWatcher in conjunction with this to wake it up as soon as possible if we know there are changes. This way, if you miss an event, you'd still recover from it based on your poll time-out.
e.g.
static void Main(string[] args)
{
FileSystemWatcher watcher = new FileSystemWatcher(#"f:\");
ManualResetEvent workToDo = new ManualResetEvent(false);
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Changed += (source, e) => { workToDo.Set(); };
watcher.Created += (source, e) => { workToDo.Set(); };
// begin watching
watcher.EnableRaisingEvents = true;
while (true)
{
if (workToDo.WaitOne())
{
workToDo.Reset();
Console.WriteLine("Woken up, something has changed.");
}
else
Console.WriteLine("Timed-out, check if there is any file changed anyway, in case we missed a signal");
foreach (var file in Directory.EnumerateFiles(#"f:\"))
Console.WriteLine("Do your work here");
}
}

Related

Communicate with process in C#

I need to communicate with external executable (ampl.exe) using standard input and standard output. This exe make calculations during some minutes with some display in the console. It has a prompt so I can succesively launch calculations by using its standard input as soon as a calculation is finished.
The external exe is launched as :
var myProcess = new Process();
myProcess.StartInfo = new ProcessStartInfo("ampl.exe");
myProcess.StartInfo.CreateNoWindow = true;
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.RedirectStandardOutput = true;
myProcess.StartInfo.RedirectStandardError = true;
myProcess.StartInfo.RedirectStandardInput = true;
myProcess.Start();
I communicate with it by using myProcess.StandardInput and myProcess.StandardOutput (synchronous way).
I use standard input to launch the calcul, for example :
myProcess.StandardInput.WriteLine("solve;");
I want to wait the end of the solve statement, get results in files, prepare new calculation input files and then launching a second solve.
My problem is that I do now know when the first calculation is finished, that is when the exe is waiting for new command in its standard input.
The only way I found is to add a specific display command and wait for getting it it its standard output :
myProcess.StandardInput.WriteLine("solve;");
myProcess.StandardInput.WriteLine("print 'calculDone';");
string output = myProcess.StandardOutput.ReadLine();
while (!output.Contains("calculDone"))
{
output = myProcess.StandardOutput.ReadLine();
}
Is there another way avoiding to use this display command to do this ?
Edit : following advices, I tried the asynchronous way. But I still need to print 'CalculDone' to know when the solve statement ended. I do not get the prompt of ampl.exe (which is 'ampl : ') in the standard output of the process.
AutoResetEvent eventEnd = new AutoResetEvent(false);
var myProcess = new Process();
myProcess.StartInfo = new ProcessStartInfo("ampl.exe");
myProcess.StartInfo.CreateNoWindow = true;
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.RedirectStandardOutput = true;
myProcess.StartInfo.RedirectStandardError = true;
myProcess.StartInfo.RedirectStandardInput = true;
myProcess.EnableRaisingEvents = true;
myProcess.OutputDataReceived += (sender, e) =>
{
if (e.Data == "commandDone")
{
eventEnd.Set();
}
else if (e.Data != null)
{
Console.WriteLine("ampl: {0}", e.Data);
}
};
myProcess.Start();
myProcess.BeginOutputReadLine();
myProcess.StandardInput.WriteLine("solve;");
myProcess.StandardInput.WriteLine("print 'commandDone';");
eventEnd.WaitOne();
The best option would be to use the Processs.OutputDataReceived event instead of a tight while loop. It’s like the event async pattern, you launch an asynchronous task and wait for an event callback telling you it’s done. The continuation of the asynchronous task would go in the event handler. Remember to unsubscribe the event handler the first time it goes off, otherwise it will be firing when you don’t want it to.
Another option I have never tried is Process.WaitForInputIdle() method, but I’m not sure if this will work in your particular case. If it does you wouldn’t need to write anything to the input stream.

cant get process error output using process.ErrorDataReceived c#

I've built Form App that I use for some time , Now I want to Catch the StandardError of my process as well as its standartOutput
I've looked at answers in SO and MSDN and yet and cant get it right
My code :
public void RunProcess(string FileName, string Arguments,, bool IsPrintOutput = true)
{
process = new Process();
process.ErrorDataReceived += new DataReceivedEventHandler(OnDataReceivedEvent);
if (IsPrintOutput) process.OutputDataReceived += new DataReceivedEventHandler(OnDataReceivedEvent);
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = FileName;
process.StartInfo.Arguments = Arguments;
if (EventWhenExit)
{
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(myprocess_Exited);
}
process.Start();
process.BeginOutputReadLine();
//run polling on stored logs to print them to screen
PollingService();
}
I've check it with Iperf and I see that when I run it with correct argument I get correct output
but when I just send it with out any argumnet I see that with cmd I get
C:\>iperf.exe
Usage: iperf [-s|-c host] [options]
Try `iperf --help' for more information.
And my App I get Nothing !
what am I missing here ?
Thanks
You can stop reading here ! If you want to see the details of inner method continue below :
private void OnDataReceivedEvent(object sender, DataReceivedEventArgs e)
{
string ProcessOutput = e.Data;
ProcessLog.Add(e.Data);
}
private void PollingService()
{
var T = new Thread (()=>
{
while (true /* ProcessRunning*/)
{
if (ProcessLogIndex < ProcessLog.Count)
{
lock (this)
{
var tempList = ProcessLog.GetRange(ProcessLogIndex, ProcessLog.Count - ProcessLogIndex);
ProcessLogIndex = ProcessLog.Count;
foreach (var ToSend in tempList)
{
onDataOutputFromProcess(this, ToSend, sProcessNameID.ToString());
}
}
}
Thread.Sleep(400);
}
});
T.IsBackground = true;
T.Start();
}
I don't see a call to BeginErrorReadLine() anywhere in the code you posted. If you don't call that method, then the Process class won't actually redirect the stderr to your event handler.
I believe the above is the issue, but if you are actually calling that somewhere (and just didn't show it), then it is worth considering that there are some strange console programs out there that don't actually used stderr (or stdout) for error output. Instead, they write directly to the console window or some other non-standard mechanism. In those cases, you won't be able to receive the error output by redirecting stderr.
You can identify those programs by redirecting their output at the command like with e.g. iperf.exe 2> foo.txt. The stderr file handle is 2, and so that syntax redirects that file handle to a file named foo.txt. If the file is empty and you see errors on the screen, then the program is one of those strange programs.
But really, I think you probably just forgot to call BeginErrorReadLine(). :)

Process often exiting prematurely

I have this small piece of code (easy to try):
class Program
{
static void Main(string[] args)
{
List<string> paths = new List<string>() { "1.docx", "2.docx", "3.docx", "4.docx" };
foreach (string path in paths)
{
string path2 = #"Path\to\those\files" + path;
Process process = new Process();
process.StartInfo = new ProcessStartInfo(path2);
System.Diagnostics.Debug.WriteLine("~>" + path2);
process.Exited += (s1, e1) => process_Exited(path2);
process.EnableRaisingEvents = true;
process.Start();
}
while (true) { }
}
static void process_Exited(string path)
{//<~ breakpoint here
}
}
What is happening is sometimes, the breakpoint is hit moments after I start the application despite it having to wait for the processes to be exited one after another. It is always the last file of all, be it 2 files, 3 files, 4 files or more. The only time the breakpoint is never hit prematurely is when paths contains only one file. (By the way, maybe I could not care much about this strange behaviour, but when I really exit the .docx file (the last one from the paths list), the breakpoint is not hit.
Why is this (process sometimes exiting prematurely) happening and how to prevent that?
UPDATE: I just noticed it is not necessarily the last of files in paths, sometimes it is random one.
Eventually, unable to solve the primary issue, I ended up using NetOffice:
NetOffice.WordApi.Application wordApp = new NetOffice.WordApi.Application();
wordApp.Visible = true;
NetOffice.WordApi.Document doc = wordApp.Documents.Open(path);
which solved the premature closing.

Restart an application by itself

I want to build my application with the function to restart itself. I found on codeproject
ProcessStartInfo Info=new ProcessStartInfo();
Info.Arguments="/C choice /C Y /N /D Y /T 3 & Del "+
Application.ExecutablePath;
Info.WindowStyle=ProcessWindowStyle.Hidden;
Info.CreateNoWindow=true;
Info.FileName="cmd.exe";
Process.Start(Info);
Application.Exit();
This does not work at all...
And the other problem is, how to start it again like this?
Maybe there are also arguments to start applications.
Edit:
http://www.codeproject.com/script/Articles/ArticleVersion.aspx?aid=31454&av=58703
I use similar code to the code you tried when restarting apps. I send a timed cmd command to restart the app for me like this:
ProcessStartInfo Info = new ProcessStartInfo();
Info.Arguments = "/C ping 127.0.0.1 -n 2 && \"" + Application.ExecutablePath + "\"";
Info.WindowStyle = ProcessWindowStyle.Hidden;
Info.CreateNoWindow = true;
Info.FileName = "cmd.exe";
Process.Start(Info);
Application.Exit();
The command is sent to the OS, the ping pauses the script for 2-3 seconds, by which time the application has exited from Application.Exit(), then the next command after the ping starts it again.
Note: The \" puts quotes around the path, incase it has spaces, which cmd can't process without quotes.
Hope this helps!
Why not use
Application.Restart();
??
More on Restart
Why not just the following?
Process.Start(Application.ExecutablePath);
Application.Exit();
If you want to be sure the app does not run twice either use Environment.Exit(-1) which kills the process instantaneously (not really the nice way) or something like starting a second app, which checks for the process of the main app and starts it again as soon as the process is gone.
You have the initial application A, you want to restart.
So, When you want to kill A, a little application B is started, B kill A, then B start A, and kill B.
To start a process:
Process.Start("A.exe");
To kill a process, is something like this
Process[] procs = Process.GetProcessesByName("B");
foreach (Process proc in procs)
proc.Kill();
A lot of people are suggesting to use Application.Restart. In reality, this function rarely performs as expected. I have never had it shut down the application I am calling it from. I have always had to close the application through other methods such as closing the main form.
You have two ways of handling this. You either have an external program that closes the calling process and starts a new one,
or,
you have the start of your new software kill other instances of same application if an argument is passed as restart.
private void Application_Startup(object sender, StartupEventArgs e)
{
try
{
if (e.Args.Length > 0)
{
foreach (string arg in e.Args)
{
if (arg == "-restart")
{
// WaitForConnection.exe
foreach (Process p in Process.GetProcesses())
{
// In case we get Access Denied
try
{
if (p.MainModule.FileName.ToLower().EndsWith("yourapp.exe"))
{
p.Kill();
p.WaitForExit();
break;
}
}
catch
{ }
}
}
}
}
}
catch
{
}
}
Winforms has the Application.Restart() method, which does just that. If you're using WPF, you can simply add a reference to System.Windows.Forms and call it.
Another way of doing this which feels a little cleaner than these solutions is to run a batch file which includes a specific delay to wait for the current application to terminate. This has the added benefit of preventing the two application instances from being open at the same time.
Example windows batch file ("restart.bat"):
sleep 5
start "" "C:\Dev\MyApplication.exe"
In the application, add this code:
// Launch the restart batch file
Process.Start(#"C:\Dev\restart.bat");
// Close the current application (for WPF case)
Application.Current.MainWindow.Close();
// Close the current application (for WinForms case)
Application.Exit();
My solution:
private static bool _exiting;
private static readonly object SynchObj = new object();
public static void ApplicationRestart(params string[] commandLine)
{
lock (SynchObj)
{
if (Assembly.GetEntryAssembly() == null)
{
throw new NotSupportedException("RestartNotSupported");
}
if (_exiting)
{
return;
}
_exiting = true;
if (Environment.OSVersion.Version.Major < 6)
{
return;
}
bool cancelExit = true;
try
{
List<Form> openForms = Application.OpenForms.OfType<Form>().ToList();
for (int i = openForms.Count - 1; i >= 0; i--)
{
Form f = openForms[i];
if (f.InvokeRequired)
{
f.Invoke(new MethodInvoker(() =>
{
f.FormClosing += (sender, args) => cancelExit = args.Cancel;
f.Close();
}));
}
else
{
f.FormClosing += (sender, args) => cancelExit = args.Cancel;
f.Close();
}
if (cancelExit) break;
}
if (cancelExit) return;
Process.Start(new ProcessStartInfo
{
UseShellExecute = true,
WorkingDirectory = Environment.CurrentDirectory,
FileName = Application.ExecutablePath,
Arguments = commandLine.Length > 0 ? string.Join(" ", commandLine) : string.Empty
});
Application.Exit();
}
finally
{
_exiting = false;
}
}
}
This worked for me:
Process.Start(Process.GetCurrentProcess().MainModule.FileName);
Application.Current.Shutdown();
Some of the other answers have neat things like waiting for a ping to give the initial application time to wind down, but if you just need something simple, this is nice.
For .Net application solution looks like this:
System.Web.HttpRuntime.UnloadAppDomain()
I used this to restart my web application after changing AppSettings in myconfig file.
System.Configuration.Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~");
configuration.AppSettings.Settings["SiteMode"].Value = model.SiteMode.ToString();
configuration.Save();

handle exit event of child process

I have a console application and in the Main method. I start a process like the code below, when process exists, the Exist event of the process is fired but it closed my console application too, I just want to start a process and then in exit event of that process start another process.
It is also wired that process output is reflecting in my main console application.
Process newCrawler = new Process();
newCrawler.StartInfo = new ProcessStartInfo();
newCrawler.StartInfo.FileName = configSection.CrawlerPath;
newCrawler.EnableRaisingEvents = true;
newCrawler.Exited += new EventHandler(newCrawler_Exited);
newCrawler.StartInfo.Arguments = "someArg";
newCrawler.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
newCrawler.StartInfo.UseShellExecute = false;
newCrawler.Start();
You have to call newCrawler.WaitForExit() in order to stay until the child process finish. Then you can use newCrawler.ExitCode to have the exit value.
Seems like the Process exit handling could have caused the application error. So the application could have terminated. Can you put a proper try..catch block and debugg to see what is going wrong. Or comment the
line
newCrawler.Exited += new EventHandler(newCrawler_Exited);
and see what happens.
Please try following code (This is from MSDN) , also don't forget to pass one argument (FileName)
using System;
using System.Diagnostics;
using System.ComponentModel;
using System.Threading;
using Microsoft.VisualBasic;
class PrintProcessClass
{
private Process myProcess = new Process();
private int elapsedTime;
private bool eventHandled;
// Print a file with any known extension.
public void PrintDoc(string fileName)
{
elapsedTime = 0;
eventHandled = false;
try
{
// Start a process to print a file and raise an event when done.
myProcess.StartInfo.FileName = fileName;
myProcess.StartInfo.Verb = "Print";
myProcess.StartInfo.CreateNoWindow = true;
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(myProcess_Exited);
myProcess.Start();
}
catch (Exception ex)
{
Console.WriteLine("An error occurred trying to print \"{0}\":" + "\n" + ex.Message, fileName);
return;
}
// Wait for Exited event, but not more than 30 seconds.
const int SLEEP_AMOUNT = 100;
while (!eventHandled)
{
elapsedTime += SLEEP_AMOUNT;
if (elapsedTime > 30000)
{
break;
}
Thread.Sleep(SLEEP_AMOUNT);
}
}
// Handle Exited event and display process information.
private void myProcess_Exited(object sender, System.EventArgs e)
{
eventHandled = true;
Console.WriteLine("Exit time: {0}\r\n" +
"Exit code: {1}\r\nElapsed time: {2}", myProcess.ExitTime, myProcess.ExitCode, elapsedTime);
}
public static void Main(string[] args)
{
// Verify that an argument has been entered.
if (args.Length <= 0)
{
Console.WriteLine("Enter a file name.");
return;
}
// Create the process and print the document.
PrintProcessClass myPrintProcess = new PrintProcessClass();
myPrintProcess.PrintDoc(args[0]);
}
}
One thing I noticed is, if you are not passing the filename as parameter, that will lead the process to crash, but still the application is intact (Since the exception is handled inside the process).
If you are not passing the filename the above code will crash beacuse the
myPrintProcess.PrintDoc(args[0]);
will throw exception from main process itself.
I tried to create an exceptin inside the Exit handler, at that time the application (main process) also crashed.
can you try commenting the code inside Exit handler?

Categories