Process takes longer to finish than in CMD - c#

I am running a program that runs driverquery.exe command and gets back the ouput
pProcess.StartInfo.CreateNoWindow = true;
debugLog.WriteToLog("");
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
debugLog.WriteToLog("before start method");
pProcess.Start();
debugLog.WriteToLog("after start method");
if (windowTitleToHide.Length > 0)
{
WindowsUtil.HideWindow(windowTitleToHide);
}
if (null != attributeName && !"".Equals(attributeName))
Console.WriteLine(attributeName + " : ");
debugLog.WriteToLog("before read to end");
StreamReader reader = pProcess.StandardOutput;
String strOutput = string.Empty;
while (reader.Peek() > -1)
strOutput += reader.ReadLine();
debugLog.WriteToLog("after read to end");
Console.WriteLine(strOutput);
debugLog.WriteToLog("before wait for exit");
pProcess.WaitForExit();
debugLog.WriteToLog("after wait for exit");
pProcess.Close();
The process takes about 30 minutes to finish.If i run the same process via cmd it always finishes in 2 minutes. I have tried using readtoend instead of readline but that also did not help. Can some tell what is wrong here? in my logs i can see the last line getting printed as before wait for exit
PS: When i see the processes in the taskmanager the driverquery.exe is running but not consuming any CPU cycles. The process calling this code is consuming about 99% CPU. I know for sure that the calling code is not doing any other task while running this code.

I might guess the problem is related to :
while (reader.Peek() > -1)
strOutput += reader.ReadLine();
You probably are not reading the full output of your application. If there is any pause in its output then reader.Peek() will return -1 before the application is finished its full output. If it is outputting a lot of data you may even be overflowing the output stream since your process will have given up reading after emptying the stream once. If this is the case, the child process may be generating a lot of exceptions outputting to a full stream (which will dramatically increase execution time). Profiling and debugging would tell you more about what is actually going on.
You might try an async approach like this :
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.EnableRaisingEvents = true; // Enable events
pProcess.OutputDataReceived += outputRedirection; // hook up
pProcess.Start();
pProcess.BeginOutputReadLine(); // use async BeginOutputReadLine
pProcess.WaitForExit();
where
static void outputRedirection(object sendingProcess,
DataReceivedEventArgs outLine)
{
try
{
if (outLine.Data != null)
Console.WriteLine(outLine.Data);
// or collect the data, etc
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return;
}
}
Rather than polling in a tight loop (which is likely to empty the output stream faster than the child process will fill it, and therefore fail), this waits for data to come in. Your main process will still block on the call to WaitForExit but a threadpool thread will handle the incoming events.
EDIT
Here is an SSCCE that works just fine :
static void Main(string[] args)
{
Stopwatch spw = new Stopwatch();
spw.Start();
Process pProcess = new Process();
pProcess.StartInfo.FileName = "driverquery.exe";
pProcess.StartInfo.CreateNoWindow = true;
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.EnableRaisingEvents = true;
pProcess.OutputDataReceived += outputRedirection;
pProcess.Start();
pProcess.BeginOutputReadLine();
pProcess.WaitForExit();
pProcess.Close();
spw.Stop();
Console.WriteLine();
Console.WriteLine("Completed in : " +
spw.ElapsedMilliseconds.ToString()
+ "ms");
}
Using outputRedirection as define above --> output :
If this isn't working for you, then please show us your complete, real code. Something else you are doing is wrong.

Related

Message Handler Stops Seeing Responses?

As a workaround for a dumb issue, I have a separate application do my SharePoint file syncing that I launch as a separate process and communicate via WriteLine and ReadLine. After the third Sync command, it seems the Process executes it (as evidenced by a separate log file), but my SyncToolMessageHandler is no longer getting alerted to its responses even with forced flushes on the external process. If I run the program myself and send the exact same commands, no issue in the console window. So why do I seem to lose my message handler? The Process handle is still alive and well.
I also see this in my debugger on SyncToolProcess. It shows this before I send the first WriteLine so I'm not sure what it's talking about but maybe it's a clue? I'm establishing the process/callbacks and sending commands from an "Update" loop in Unity and the responses are obviously asynchronous whenever the process responds. Is that somehow a problem?
Sender:
public void LaunchSyncTool()
{
SyncToolProcess = new Process();
SyncToolProcess.StartInfo.FileName = "SyncProcess.exe";
SyncToolProcess.StartInfo.Arguments = SpecialArguments;
SyncToolProcess.StartInfo.UseShellExecute = false;
SyncToolProcess.StartInfo.RedirectStandardOutput = true;
SyncToolProcess.StartInfo.RedirectStandardInput = true;
SyncToolProcess.StartInfo.RedirectStandardError = true;
SyncToolProcess.StartInfo.CreateNoWindow = true;
SyncToolProcess.OutputDataReceived += new DataReceivedEventHandler(SyncToolMessageHandler);
SyncToolProcess.ErrorDataReceived += new DataReceivedEventHandler(SyncToolMessageHandler);
SyncToolProcess.Start();
SyncToolProcess.BeginOutputReadLine();
SyncToolProcess.BeginErrorReadLine();
SyncToolProcess.StandardInput.WriteLine("Sync File1 File2");
}
// Super simplified, but the ping and pong works great until about the 3rd sync.
public void SyncToolMessageHandler(object sender, DataReceivedEventArgs e)
{
if (e.Data == "Good")
{
Debug("Received 'Good', Sending Command");
SyncToolProcess.StandardInput.WriteLine("Sync File3 File4");
}
else Debug(e.Data); // Exceptions come up Null for some reason, but not seeing them here :-\
// SyncToolProcess.HasExited is always false
}
Receiver:
while (true)
{
string inputCmd = await Console.In.ReadLineAsync();
Debug("Received \"" + inputCmd + "\"");
try
{
string[] split = inputCmd.Split(' ');
switch (split[0])
{
case "Sync":
Sync(split);
break;
case "Quit":
Debug("Quitting");
return;
default:
Debug("Unknown Request: " + inputCmd);
break;
}
}
catch (Exception e)
{
Error(e.Message + "\n" + e.StackTrace);
}
Status("Good");
}
Ugh. The problem was the Unity Editor. If I build the program first it no longer misses the callbacks. Not exactly an answer for using the Editor, but at least I know the code is right!

Console.KeyAvailable Fails When Starting a Process

Here's a snippet of code the works just fine. As I am simulating a long running process, any keystrokes are queued up. The Console.Available returns true and false just as the documentation indicates it should. Everything works great here:
while (true) {
Console.WriteLine("Starting long task...");
// Simulate some long task by sleeping 3 seconds
System.Threading.Thread.Sleep(3000);
Console.WriteLine("Long task is finished.");
if (Console.KeyAvailable)
Console.WriteLine("A key is available: " + Console.ReadKey(false).Key);
else
Console.WriteLine("*** No Key available ***");
}
Here's the problem: When I substitute the Thread.Sleep() with code to create and run a real Process, the Console.KeyAvailable ceases to work. Console.KeyAvailable behaves erratically, usually returning false but sometimes returning true if I type enough keys fast enough.
Has anyone an explanation for this?
while (true) {
LongRunningProcess("someFile.bin");
if (Console.KeyAvailable)
Console.WriteLine("A key is available: " + Console.ReadKey(false).Key);
else
Console.WriteLine("*** No Key available ***");
}
private static bool LongRunningProcess(String filename) {
ProcessStartInfo processStartInfo = new ProcessStartInfo("BlahBlahBlah.exe", filename);
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardError = true;
Process p = new Process();
p.StartInfo = processStartInfo;
p.Start();
StreamReader stdError = p.StandardError;
int readResult = stdError.Read();
p.Close();
if (readResult != -1) // error was written to std error
return false;
return true;
}
The started process shows a window that becomes the active window. Your console application only receives keyboard input while your console window is the active window. Try setting the CreateNoWindow property of the ProcessStartInfo to true.

Why is OutputDataReceived not being raised for prompt, preventing AutoResetEvent from being signaled?

I'm making an app that will spawn up a process that has a command-line interpreter. I need to supply commands to this CLI from another machine. Now, I have to detect when the command is finished, so I'm checking for when the CLI's prompt appears in the standard output of the process I'm spawning. Here's a snippet of code:
private string StartProcess(string input)
{
try
{
StringBuilder output = new StringBuilder();
StringBuilder error = new StringBuilder();
AutoResetEvent commandFinished = new AutoResetEvent(false);
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = "myprocess.exe",
Arguments = "",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UserName = System.Environment.UserName
};
Process myProcess = new Process()
{
StartInfo = startInfo,
EnableRaisingEvents = true
};
myProcess.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{
if (e.Data != null)
{
string prompt = "user >";
if (e.Data.Substring(e.Data.Length - prompt.Length).Equals(prompt))
{
Console.WriteLine("Received prompt! Sending CommandFinished signal!");
commandFinished.Set();
Console.WriteLine("CommandFinished signal set!");
}
else
{
output.AppendLine(e.Data);
}
}
else
{
// Data finished
Console.WriteLine("StdOut data finished! Sending CommandFinished signal!");
commandFinished.Set();
Console.WriteLine("CommandFinished signal set!");
}
});
myProcess.ErrorDataReceived += new DataReceivedEventHandler((sender, e) =>
{
if (e.Data != null)
{
Console.WriteLine("Error Data received: " + e.Data.ToString());
error.AppendLine(e.Data);
}
});
myProcess.Start();
myProcess.BeginOutputReadLine();
myProcess.BeginErrorReadLine();
Console.WriteLine("Executing input command: " + input);
myProcess.StandardInput.WriteLine(input);
Console.WriteLine("Waiting for input command to complete...");
commandFinished.WaitOne();
Console.WriteLine("Command complete!");
return output.ToString();
}
catch (Exception ex)
{
Console.WriteLine("EXCEPTION: " + ex.ToString());
throw ex;
}
}
Now, the code is hanging on the call to WaitOne(). I'm confused as to why - I don't detect the CLI prompt in the output, and I never get any WriteLines telling me the prompt was received in the OutputDataReceived event, or that the Data received was null. I.e. the OutputDataReceived event isn't being raised when the previous command has completed and the prompt is displayed.
The input command I'm supplying does take a while, but it does finish. Am I using AutoResetEvent wrong here?
Without a good, minimal, complete code example it's impossible to know for sure what might be wrong with the code. But I can see one obvious explanation:
The OutputDataReceived event is raised for each line of output that is received. That is, a string terminated by a newline character.
Without specifics about your external process, I can't say for sure. But most CLI type scenarios involve the display of a prompt without a newline character. I.e. the prompt is written to the console and user input is expected to be echoed to the console immediately following the prompt, rather than on the next line.
If that's the case for your scenario, then almost certainly you are failing to detect the prompt because the event won't be raised for the prompt alone. After the final line of output from the previous command's operation, the next time the event will be raised is after the command has been sent to the process via standard input. This is obviously too late to be useful in knowing when to send that command. :)
To get this to work, you will have to read input from the process via one of the other mechanisms available that are not line-based.

how to make sure my process that i am starting will close of my application crash

in my application i m open Wireshark process and start capturing packts, in the UI i have Start button who start the capturing and Stop button who stop capturing and i am doing this by killing my wireshark process.
my question is if i am close my application in the middle of the capturing but nor with my Stop button my Wireshark process continue to run, how can i handle this situation and make sure that my process will close if my application crash or someone close it in the middle of capturing
public void startCapturing()
{
ThreadStart tStarter = delegate { openAdapterForStatistics(_device); };
Thread thread = new Thread(tStarter);
thread.IsBackground = true;
thread.Start();
ProcessStartInfo tsharkStartInfo = new ProcessStartInfo();
tsharkStartInfo.FileName = _tshark;
tsharkStartInfo.RedirectStandardOutput = true;
tsharkStartInfo.RedirectStandardError = true;
tsharkStartInfo.RedirectStandardInput = true;
tsharkStartInfo.UseShellExecute = false;
tsharkStartInfo.CreateNoWindow = true;
tsharkStartInfo.Arguments = string.Format(" -i " + _interfaceNumber + " -s " + _packetLimitSize + " -w " + _pcapPath);
_tsharkProcess.StartInfo = tsharkStartInfo;
_tsharkProcess.ErrorDataReceived += _cmdProcess_ErrorDataReceived;
_tsharkProcess.OutputDataReceived += tshark_OutputDataReceived;
_tsharkProcess.EnableRaisingEvents = true;
_tsharkProcess.Start();
_tsharkProcess.BeginOutputReadLine();
_tsharkProcess.BeginErrorReadLine();
FileInfo fileInfo = new FileInfo(_pcapPath);
string directoryName = fileInfo.DirectoryName;
DirectoryInfo directoryInfo = new DirectoryInfo(directoryName);
FileInfo[] dirs = directoryInfo.GetFiles();
_file = dirs.FirstOrDefault(f => f.Name.Equals(fileInfo.Name));
_tsharkProcess.WaitForExit();
}
Assuming you're handling exceptions using try-catch, you can make sure the Wireshark process is closed in the finally block at the end of your topmost try-catch statement. From the MSDN documentation:
"The finally block is useful for cleaning up any resources allocated in the try block. Control is always passed to the finally block regardless of how the try block exits."
For example:
try
{
// Your application's code.
}
catch( Exception )
{
// Handle, log, whatever.
}
finally
{
// Make sure Wireshark process is killed.
}
The code in the finally block will always get executed whether or not there was an exception.
You can't be 100% sure. If your application crashes in a 'civilized' manner, you can close the Wireshark process (much like #jebar8 suggested). Unfortunately, your application could crash in a way that doesn't run your finally code (if it's out of memory or your main thread is out of stack space, for example).
If you want to take that into account as well, you need a third process. Write up a small program that will launch your application and monitor it. If your application process disappears, your monitoring process can then kill Wirehsark is it's alive. Since the monitoring program is a short and simple one, the chance of it crashing unexpectedly are very slim.

How to read to end process output asynchronously in C#?

I have problem with reading the output of one Process asynchronously in C#.
I found some other similar questions on this site but they don't really help me.
Here is what I do:
Make new process
Set startinfo
-FileName, Arguments, CreateNoWindow(true), UseShellExecute(false), RedirectStandardOutput(true)
Add event handler to OutputDataReceived;
Start process, BeginOutputReadLine and then WaitForExit().
It works fine but the output of the started process writes some percents(%) which I want to get but I can't since my code reads line by line and the percents don't show up.
Example:
%0,%1...%100
Finished.
My output:
%0
Finished.
Here is the current code of my program:
StringBuilder sBuilder = new StringBuilder();
static void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
sBuilder.AppendLine(e.Data);
}
static void CommandExecutor()
{
Process process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = /*path of the program*/,
Arguments = /*arguments*/,
CreateNoWindow = true,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden,
RedirectStandardOutput = true
}
};
process.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
}
Process.WaitForExit() will wait until the asynchronous output / error stream reading finished. Unfortunately this is not true for Process.WaitForExit(timeout) overload. This is what the Process class does internally:
//...
finally
{
if (processWaitHandle != null)
{
processWaitHandle.Close();
}
if (this.output != null && milliseconds == -1)
{
this.output.WaitUtilEOF();
}
if (this.error != null && milliseconds == -1)
{
this.error.WaitUtilEOF();
}
this.ReleaseProcessHandle(safeProcessHandle);
}
... So it will wait for the async reads only if there was no timeout!
To fix it simply call the parameterless WaitForExit() after WaitForExit(timeout) returned true:
// ...
if (process.WaitForExit( 10 * 1000 ) && process.WaitForExit() )
{
// Process'es OutputDataReceived / ErrorDataReceived callbacks will not be called again, EOF streams reached
}
else
{
throw new Exception("timeout");
}
For details read the remarks here: http://msdn.microsoft.com/en-us/library/ty0d8k56%28v=vs.110%29
It seems that reading stream output asynchronously is a bit broken - not all the data is read before the process exits. Even if you call Process.WaitForExit() and even if you then call Process.Close() (or Dispose()) you can still get a lot of data afterwards. See http://alabaxblog.info/2013/06/redirectstandardoutput-beginoutputreadline-pattern-broken/ for a full write-up, but the solution is basically to use synchronous methods. To avoid a deadlock, though, you have to call one of them on another thread:
using (var process = Process.Start(processInfo))
{
// Read stderr synchronously (on another thread)
string errorText = null;
var stderrThread = new Thread(() => { errorText = process.StandardError.ReadToEnd(); });
stderrThread.Start();
// Read stdout synchronously (on this thread)
while (true)
{
var line = process.StandardOutput.ReadLine();
if (line == null)
break;
// ... Do something with the line here ...
}
process.WaitForExit();
stderrThread.Join();
// ... Here you can do something with errorText ...
}
There are few things that are getting in the way of it...
The console app is probably using "\b" backspace to overwrite the percentage, its maybe not flushing to the stdout stream after every write, and the BeginOutputReadLine presumably waits for the end of line before giving you data.
See how you get on with reading process.StandardOutput.BaseStream via BeginRead (this code isn't proper async and the "\b"s will need processed differently if your putting progress in a form):
while (true)
{
byte[] buffer = new byte[256];
var ar = myProcess.StandardOutput.BaseStream.BeginRead(buffer, 0, 256, null, null);
ar.AsyncWaitHandle.WaitOne();
var bytesRead = myProcess.StandardOutput.BaseStream.EndRead(ar);
if (bytesRead > 0)
{
Console.Write(Encoding.ASCII.GetString(buffer, 0, bytesRead));
}
else
{
myProcess.WaitForExit();
break;
}
}
What about using a StreamReader on process.StandardOutput, and the using the Read() method ?
http://msdn.microsoft.com/fr-fr/library/system.io.streamreader.read(v=vs.80).aspx

Categories

Resources