I have a C# application A which calls another C# application B, which calls multiple instances of application C.
I would like to redirect the output of application C to the output of application A.
I already have a working redirection of the output of application B into the output of application A.
Now, from within application B's code, I'm redirecting every process's output, and I'm printing the redirected output to the console. Unfortunately, for some reason, nothing is printed.
(I'm currently testing it without using application A - I'm only running application B).
Here is the code:
private void runSingleFile (string execFile, string commandArgs)
{
Process processToRun = new Process();
processToRun .StartInfo.FileName = execFile;
processToRun .StartInfo.Arguments = commandArgs;
processToRun .StartInfo.UseShellExecute = false;
processToRun .StartInfo.RedirectStandardOutput = true;
processToRun .OutputDataReceived += outputRedirection;
processToRun.Start();
Console.WriteLine("");
Thread.Sleep(100);
processToRun.BeginOutputReadLine();
}
private void outputRedirection(object sendingProcess, DataReceivedEventArgs outLine)
{
try
{
if (outLine.Data != null)
Console.WriteLine(outLine.Data);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return;
}
}
Any ideas?
The .net Process object doesn't make it easy for one to "do the right thing" with respect to correctly handling the IO.
These pages list some of the concerns that need to be dealt with:
MSDN's ProcessStartInfo.RedirectStandardOutput Property page
Raymond's blog Be careful when redirecting both a process's stdin and stdout to pipes...
C# test.net's How to use System.Diagnostics.Process correctly
From your code sample, here are some of the items that you will want to look at:
You will want to capture both stderr and stdin (the latter should be immediately closed if not used).
You also need to be aware that you can still receive events on your outputRedirection callback after the child process has exited.
The code you have posted works. Given test program
// ConsoleApplication2.exe
static void Main(string[] args)
{
Console.WriteLine("Test1...");
Console.WriteLine("Test2...");
System.Threading.Thread.Sleep(100);
Console.WriteLine("Test3...");
}
called as :
static void Main(string[] args)
{
runSingleFile("ConsoleApplication2.exe", "");
runSingleFile("ConsoleApplication2.exe", "");
runSingleFile("ConsoleApplication2.exe", "");
Console.ReadLine();
}
produces output :
Clearly something else is not working in another part of your code (Something you have not shown us).
The most likely explanation, if application C is working correctly, is that application B is terminating before all instances of C have completed. You may have to add some code that makes B wait for all instances of C to return.
Note that :
static void Main(string[] args)
{
runSingleFile("ConsoleApplication2.exe", "");
runSingleFile("ConsoleApplication2.exe", "");
runSingleFile("ConsoleApplication2.exe", "");
//Console.ReadLine(); // ** Don't wait!
}
completes immediately and fails to return some or all of the data (especially if you remove the Sleep call in runSingleFile.
Consider :
static long processCount = 0; //ADD
static void runSingleFile(string execFile, string commandArgs)
{
Interlocked.Increment(ref processCount); //ADD
Process processToRun = new Process();
processToRun.StartInfo.FileName = execFile;
processToRun.StartInfo.Arguments = commandArgs;
processToRun.StartInfo.UseShellExecute = false;
processToRun.StartInfo.RedirectStandardOutput = true;
processToRun.OutputDataReceived += outputRedirection;
processToRun.EnableRaisingEvents = true; //ADD
processToRun.Exited += processExited; //ADD
processToRun.Start();
Console.WriteLine("");
processToRun.BeginOutputReadLine();
}
static void processExited(object sender, EventArgs e)
{
Interlocked.Decrement(ref processCount);
}
with
static void Main(string[] args)
{
runSingleFile("ConsoleApplication2.exe", "");
runSingleFile("ConsoleApplication2.exe", "");
runSingleFile("ConsoleApplication2.exe", "");
while (Interlocked.Read(ref processCount) > 0)
{
System.Threading.Thread.Sleep(100);
}
}
The above then makes application B wait until all spawned processes have returned. The example is simplistic and obviously can be improved upon, but it demonstrates the problem, I think, and offers a method for the solution.
You might be tempted to use something more elegant like WaitHandle.WaitAll(), but this does introduce the problem that josh noted where your output events may not fire until after the process terminates - the process handle will signal that it has terminated but its posted messages may still be in the queue. Waiting on the Exited event tidies up that race condition since that event will always be the last message in the queue (AFAIK).
Also, note the use of Interlocked functions here - Console.WriteLine is threadsafe but other variable access is not. Since there is no synchronization context in a console application the events raised by spawned processes are handled by threads in the threadpool (not the main thread of the console application). This introduces all of the issues associated with multithreading which must be managed appropriately.
Related
I have a program that creates a subprocess and trys to handle any error messages as per below:
study.StartInfo.FileName = studyWorkingDir +"\\"+ clargParts[0];
study.StartInfo.Arguments = clargs;
study.StartInfo.UseShellExecute = false;
study.StartInfo.RedirectStandardError = true;
study.ErrorDataReceived += (sender, args) =>
{
Process process = (Process)sender;
if (!process.HasExited)
{
MyErrorBroadcaster.BroadcastMessage("Instance error: " + args.Data.ToString());
process.kill();
}
};
study.Start(); // Start the Study!
study.PriorityClass = ProcessPriorityClass.BelowNormal;
study.BeginErrorReadLine();
The child process is an application (given below). In order to actually get a non-empty error in the above master process, I've needed to wrap the whole sub-program in a try-catch which makes a custom Console.Error.WriteLine() call
static class Program
{
static void Main()
{
try
{
Console.Title = "MyProgram";
Application.Run(new GUI());
}
catch (Exception e)
{
Console.Error.WriteLine(e.ToString().Replace("\r\n","\t"));
}
}
}
The problem is, this try-catch in the child program clobbers the debugger from stopping on the actual error locations in visual studio, which I think will annoy/be problematic for me and my coworkers as we develop. (Seems kludgy too!)
Is there a better way to get have this sub-application to report errors to the master process, without affecting normal debugger operation?
Revisiting this, the problem was that the master process was only getting the errors one line at a time and that first line happened to be "", after which it immediately closes the process (ignoring the following error lines). The e.ToString().Replace("\r\n","\t") bypassed this issue by guaranteeing the error was one line, but with unwanted side effects.
A better solution for me was the following:
study.StartInfo.UseShellExecute = false;
study.StartInfo.RedirectStandardError = true;
study.Start(); // Start the Study!
//This thread collects errors until the process ends, then reports them
(new Thread(() =>
{
//Acumulate errors, then send
String err = study.StandardError.ReadToEnd();
if (!string.IsNullOrEmpty(err))
MyErrorBroadcaster.BroadcastMessage("Instance Error", "\tInstance error: " + err);
})).Start();
This approach uses the study.StandardError.ReadToEnd(). This command is blocking which would be unsuitable for my purposes, so It is in a lambda thread (so the main program can move on!)
I am writing an application in C# that at some point starts an application as a child process using the Process class with Asynchronous IO redirection as shown below:
private void AppLaunch_Click(object sender, RoutedEventArgs e)
{
Process appProcess = new Process();
appProcess.StartInfo.FileName = currAppPath;
appProcess.StartInfo.Arguments = "";
//Setup Redirection
appProcess.StartInfo.UseShellExecute = false;
appProcess.StartInfo.ErrorDialog = false;
appProcess.StartInfo.RedirectStandardError = true;
appProcess.EnableRaisingEvents = true;
// Attach Output Handler
appProcess.ErrorDataReceived += appProc_DataReceived;
appProcess.Exited += appProc_Exited;
buildLogConsoleOutputTxtbox.AppendText(currAppPath + "\n");
appProcess.Start();
appProcess.BeginErrorReadLine();
}
private void appProc_DataReceived(object sender, DataReceivedEventArgs e)
{
if (!String.IsNullOrEmpty(e.Data))
{
this.appendLogText(e.Data);
}
}
private void appProc_Exited(object sender, System.EventArgs e)
{
Process proc = (Process)sender;
// Wait a short while to allow all console output to be processed and appended
Thread.Sleep(40);
this.appendLogText("\n>>");
proc.Close();
}
private void appendLogText(string logText)
{
// Use a delegate if called from a different thread,
// else just append the text directly
if (buildLogConsoleOutputTxtbox.Dispatcher.CheckAccess())
{
// Thread owns the TextBox
buildLogConsoleOutputTxtbox.AppendText(logText + Environment.NewLine);
}
else
{
//Invocation Required
appendLogCallBack appendLog = new appendLogCallBack(buildLogConsoleOutputTxtbox.AppendText);
buildlogScrollEnd buildlogscrl = new buildlogScrollEnd(buildLogConsoleOutputTxtbox.ScrollToEnd);
buildLogConsoleOutputTxtbox.Dispatcher.BeginInvoke(appendLog, new object[] { logText + Environment.NewLine });
buildLogConsoleOutputTxtbox.Dispatcher.BeginInvoke(buildlogscrl);
}
The Problem with this piece of code is that while I do get the stderr redirected properly to my textbox, This redirection seems to hide the process' stdout output, which I don't want redirected!
If I redirect stdout, I can see it redirected properly, but is it impossible to just redirect stderr and not stdout? I have looked around and googled regarding this topic but all discussions seem to be regarding redirecting stdout ... such as this : How to asynchronously read the standard output stream and standard error stream at once
I would be grateful for any help regarding this!
That is not possible as is - output and error handles are redirected simultaneously. MSDN article STARTUPINFO describes STARTF_USESTDHANDLES flag.
But there is a good news. Preserving child process output is still possible. You just have to:
redirect child process output
attach to child process console
write child process output back to its console
So right after process start invoke
[DllImport("Kernel32.dll", SetLastError = true) ]
static extern uint AttachConsole(int pid);
and then use simple Console.WriteLine in your DataReceived handler.
I posted about this a little while ago, but I resolved the other issue and ran into one more. I am about to deploy this program to 28 hosting machines so I want to make sure this is working before I do so.
I wrote a little c# NET application that is basically a wrapper for a Java application, when my app starts, the Java app starts, when my app closes, it closes, and so on.
Everything works properly except that when I close my application, the Java application continues to run. When I create the process, I store the Process var in a variable outside of the methods, and then use that when my application goes to shutdown. For whatever reason though it is not terminating the Java application.
class Program
{
private static Process minecraftProcess;
public static void LaunchMinecraft(String file, String memoryValue)
{
String memParams = "-Xmx" + memoryValue + "M" + " -Xms" + memoryValue + "M ";
String args = memParams + "-jar " + file + " nogui";
ProcessStartInfo processInfo = new ProcessStartInfo("java.exe", args);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
try
{
//using (Process minecraftProcess = Process.Start(processInfo))
using (minecraftProcess = Process.Start(processInfo))
{
minecraftProcess.WaitForExit();
}
}
catch
{
// Log Error
}
}
static void Main(string[] args)
{
Arguments CommandLine = new Arguments(args);
// Hook ProcessExit Event
AppDomain.CurrentDomain.ProcessExit += new EventHandler(Current_ProcessExit);
if (CommandLine["file"] != null && CommandLine["memory"] != null)
{
// Launch the Application (Command Line Parameters)
LaunchMinecraft(CommandLine["file"], CommandLine["memory"]);
}
else
{
// Launch the Application (Default Parameters)
LaunchMinecraft("minecraft_server.jar", "1024");
}
}
static void Current_ProcessExit(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(10000);
// If we have an active Minecraft Service, Shut it down
if (minecraftProcess != null)
{
minecraftProcess.Kill();
}
}
}
You can't Sleep in a ProcessExit handler.
The documentation states:
The total execution time of all
ProcessExit event handlers is limited,
just as the total execution time of
all finalizers is limited at process
shutdown. The default is two seconds.
An unmanaged host can change this
execution time by calling the
ICLRPolicyManager::SetTimeout method
with the OPR_ProcessExit enumeration
value.
Nevermind, I just realized the minecraftProcess variable is static.
Don't know if you did not solve this issue by yourself but:
You should be aware that there are Start methods for instances (returning bool) and static (returning a object).
You should not use using with something other than using-local variables!
Just this should work fine:
minecraftProcess = Process.Start(processInfo)
minecraftProcess.WaitForExit();
Hi there and thanking in advance
I am trying (very hard) to redirect Console input and output into a textbox. So far output is working fine but the trouble is with input.
For example I cannot execute a simple program that will do the following:
Console.WriteLine("Please enter your name: ");
string name = Console.ReadLine();
Console.WriteLine("Hi there " + name);
The reason I can't achieve this is because that the program has to stop while waiting for user to type his/her name and press enter. If I wait for user input on a new thread then the main GUI thread freezes and the textbox can never receive the KeyPress. This thing has me totally stumped. Any advice (or better still code) would be greatly appreciated.
Cheers
The code below is a Console app that calls another console app to do some work and not a WinForm app, but you could easily replace the event handlers (TransformProcessOutputDataReceived, and TransformProcessErrorDataReceived) to output to a text box instead of a TextWriter. Unfortunately it doesn't directly address your issue of the called console application waiting for user input. The code below does pump some input to the called console app via standard input, so perhaps you could supply it from your windows app in the same manner.
Hope this was helpful, I had a heck of a time getting it to work originally myself, sorry I forgot the original references I had used, it was a while ago.
private static void ProcessRetrievedFiles(List<string> retrievedFiles)
{
Console.WriteLine();
Console.WriteLine("Processing retrieved files:");
Console.WriteLine("---------------------------");
Console.WriteLine();
foreach (string filePath in retrievedFiles)
{
if (String.IsNullOrEmpty(filePath)) continue;
Console.WriteLine(filePath);
Process transformProcess = new Process();
string baseOutputFilePath = Path.Combine(ExportDirectory, Path.GetFileNameWithoutExtension(filePath));
transformProcess.StartInfo.FileName = TransformerExecutablePath;
transformProcess.StartInfo.Arguments = string.Format(
"-i:\"{0}\" -x:\"{1}\" -o:\"{2}.final.xml\"",
filePath,
string.Empty,
baseOutputFilePath);
transformProcess.StartInfo.UseShellExecute = false;
transformProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
transformProcess.StartInfo.RedirectStandardError = true;
transformProcess.StartInfo.RedirectStandardOutput = true;
transformProcess.StartInfo.RedirectStandardInput = true;
transformProcess.EnableRaisingEvents = true;
//attach the error/output recievers for logging purposes
transformProcess.ErrorDataReceived += TransformProcessErrorDataReceived;
transformProcess.OutputDataReceived += TransformProcessOutputDataReceived;
ProcessBridgeFileOutputWriter = new StreamWriter(
baseOutputFilePath + ".log",
false);
ProcessBridgeFileOutputWriter.AutoFlush = true;
transformProcess.Start();
transformProcess.BeginOutputReadLine();
transformProcess.BeginErrorReadLine();
//the exe asks the user to press a key when they are done...
transformProcess.StandardInput.Write(Environment.NewLine);
transformProcess.StandardInput.Flush();
//because we are not doing this asynchronously due to output writer
//complexities we don't want to deal with at this point, we need to
//wait for the process to complete
transformProcess.WaitForExit();
ProcessBridgeFileOutputWriter.Close();
ProcessBridgeFileOutputWriter.Dispose();
//detach the error/output recievers
transformProcess.ErrorDataReceived -= TransformProcessErrorDataReceived;
transformProcess.OutputDataReceived -= TransformProcessOutputDataReceived;
}
}
static void TransformProcessOutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Data))
{
ProcessBridgeFileOutputWriter.WriteLine(e.Data);
}
}
static void TransformProcessErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Data))
{
ProcessBridgeFileOutputWriter.WriteLine(string.Format("ERROR: {0}", e.Data));
}
}
I use a Console Application in Windows Mobile to handle incoming message interception. In the same console application i accept parameters (string args[]) which based on the parameters, register the message interceptor.
InterceptorType is a enum
static void Main(string[] args)
{
if (args[0] == "Location")
{
addInterception(InterceptorType.Location, args[1],args[2]);
}
}
private static void addInterception(InterceptorType type, string Location, string Number )
{
if (type == InterceptorType.Location)
{
using (MessageInterceptor interceptor = new MessageInterceptor(InterceptionAction.NotifyAndDelete, false))
{
interceptor.MessageCondition = new MessageCondition(MessageProperty.Sender, MessagePropertyComparisonType.Contains, Number, false);
string myAppPath = Assembly.GetExecutingAssembly().GetName().CodeBase;
interceptor.EnableApplicationLauncher("Location", myAppPath);
interceptor.MessageReceived += new MessageInterceptorEventHandler(interceptor_MessageReceived);
}
}
}
static void interceptor_MessageReceived(object sender, MessageInterceptorEventArgs e)
{
//Do something
}
I made this a console application because i want it keep running in the background and intercept incoming messages.
This works fine for the first time. But the problem is that I have to keep calling the addInterception method to add subsequent interception rules. This makes the console application start again and again for each time i add a rule. How do i make this run only once and add more message interceptor rules?
Since you already have a method in place to call the command prompt once, update your logic with some simple looping so you can pass N commands.
EDIT: I wrote it a fully compileable example to show you exactly what I am talking about. Note how the child process can be called any number of times without re-launching. This is not just a simple command line launch with arguments being passed because that idea will lead to X processes which is exactly what you do not want.
PARENT PROCESS: (The one with System.Diagnostics.Process)
/// <summary>
/// This is the calling application. The one where u currently have System.Diagnostics.Process
/// </summary>
class Program
{
static void Main(string[] args)
{
System.Diagnostics.Process p = new Process();
p.StartInfo.CreateNoWindow = false;
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = #"C:\AppfolderThing\ConsoleApplication1.exe";
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
p.OutputDataReceived += delegate(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Output received from application: {0}", e.Data);
};
p.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Output received from application: {0}", e.Data);
};
p.BeginErrorReadLine();
p.BeginOutputReadLine();
StreamWriter inputStream = p.StandardInput;
inputStream.WriteLine(1);
inputStream.WriteLine(2);
inputStream.WriteLine(-1);//tell it to exit
p.WaitForExit();
}
}
CHILD PROCESS:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
enum InterceptorType
{
foo,
bar,
zee,
brah
}
/// <summary>
/// This is the child process called by System.Diagnostics.Process
/// </summary>
class Program
{
public static void Main()
{
while (true)
{
int command = int.Parse(Console.ReadLine());
if (command == -1)
Environment.Exit(0);
else
addInterception((InterceptorType)command, "some location", "0");
}
}
private static void addInterception(InterceptorType type, string Location, string Number)
{
switch (type)
{
case InterceptorType.foo: Console.WriteLine("bind foo"); break;
case InterceptorType.bar: Console.WriteLine("bind bar"); break;
default: Console.WriteLine("default bind zee"); break;
}
}
static void interceptor_MessageReceived(object sender, EventArgs e)
{
//Do something
}
}
}
Note that codeplex has a managed service library.
EDIT
It seems that people are misunterstanding your question (or I am) so here's some clarification on how I'm seeing the problem.
You have an console app that takes in command-line parameters. These parameters are used for something (the what is irrelevant actually). You want to be able to add parameters after the app is already running by calling the app with new command line args.
What is happening is that when you call the app any time after teh first, a new instance of the process starts up instead of the command-line arguments going to the existing, already running application.
END EDIT
The solution is fairly straightforward and requires two pieces.
You need a named mutex. For whatever (poor) reason, the CF doesn't expose a version of a mutex that takes a name, so you have to P/Invoke CreateMutex or use a library (like the SDF) that already has it. Your app needs to create the mutex at startup and check to see if it already exists. if it doesn't you're the first running instance and run as normal. If the mutex exists, you need to pass your command line args to the one that is already running via a P2P queue then simply exits.
After checking the mutex, the first instance spawns a worker thread. This thread listens on a P2P queue for messages. When they come in, you handle them.