As I'm new to C#, I searched Google for various stuff which I used to use in C++. One of them is a pause possibility in a console app.
A lot of people suggested different ways like
System.Console.ReadKey(true);
System.Console.WriteLine();
Others even showed self-made functions which 'should' be more efficient than others.
And that's a real headache to decide which one is a better solution.
Could anyone give any examples of how C# interpret them and which way should be the most efficient?
Run the program using any of the following methods:
ctrl + F5
OR as Rajesh suggested
Console.ReadKey() //pauses for any key
Console.ReadLine() //pauses for enter key
I usually use do and while to pause the console. Then, if necessary, the console should resume if you press a specific key.
Example
do
{
/* while (!Console.KeyAvailable) //Continue if pressing a Key press is not available in the input stream
{
//Do Something While Paused
}
*/
} while (Console.ReadKey(true).Key != ConsoleKey.Escape); //Resume if Escape was pressed
If you leave this as //Do Something While Paused, the console will only resume if the Esc key was pressed doing nothing while paused.
However, if you would not like the console application to resume, you can use while (true); instead of while (Console.ReadKey(true).Key != ConsoleKey.Escape);
Example
do
{
//break; //Resume
} while (true); //Continue while True (Always True)
Notice: The console application will pause because by doing do { } while (Condition); you are simply telling the console application that you are doing something. So, the console application will wait for the operation to execute. Then, normally close when there's nothing to do.
Notice: The while is used to loop. So, the application will not close unless the condition becomes false.
Thanks,
I hope you find this helpful :)
If you're talking about the built-in "pause" command, you could always call it -- even though it's ugly.
Here's what I use:
static void Pause()
{
Console.WriteLine();
var pauseProc = Process.Start(
new ProcessStartInfo()
{
FileName = "cmd",
Arguments = "/C pause",
UseShellExecute = false
});
pauseProc.WaitForExit();
}
Normally like so:
if (Environment.UserInteractive())
Pause();
Hope this helps.
Or you can use what Pat did but for Arguments instead of
Arguments = "/C pause"
you can use
Arguments = "/C TIMEOUT /t 4 /nobreak > NUL"
where number 4 is number of seconds console will pause before executing rest of the program.
And the whole function would be
static void Pause(int sec)
{
Console.WriteLine();
var pauseProc = Process.Start(
new ProcessStartInfo()
{
FileName = "cmd",
Arguments = "/C TIMEOUT /t " + sec + " /nobreak > NUL",
UseShellExecute = false
});
pauseProc.WaitForExit();
}
and you will call it with Pause function with number of seconds to pause.
Pause(4);
Hope it helps.
you can write "Console.ReadLine();" this too for pupose.
Related
I'm trying to run an external binary from a C# code using the ProcessEx.RunAsync command. However, this binary doesn't have a quiet mode and whenever it's done operating, it prompts "press enter to continue" in order to exit the program. How do I handle this so I won't get a timeout return?
The functionality you describe is similar to what expect tool provides in *nix world.
The program automates interactions with programs that expose a text terminal interface.
There are various options available for you to use and if you are interested in using an existing C# library then the dotnetexpect might solve it for you; note it is quite old.
If you are looking for all hand-coding, here is a PoC to explain the concept. As usual: don't use as-is this is just to demonstrate the concept.
namespace ToExpectOrNotToExpect
{
class Program
{
static void RunAndManage(string command,string args)
{
Process processHandle = new Process();
processHandle.StartInfo.FileName = command;
processHandle.StartInfo.Arguments = args;
processHandle.StartInfo.UseShellExecute = false;
processHandle.StartInfo.RedirectStandardOutput = true;
processHandle.StartInfo.RedirectStandardInput = true;
processHandle.StartInfo.CreateNoWindow = false;
processHandle.Start();
string data = "";
while (!processHandle.StandardOutput.EndOfStream)
{
// Cheap and cheeky buffer: improve
char[] buffer = new char[256];
int n = processHandle.StandardOutput.Read(buffer, 0, 100);
data += new string(buffer, 0, n);
Console.WriteLine("GOT: [[ " + data + "]]");
if (data.IndexOf("Press any key") >= 0)
{
processHandle.StandardInput.Write('\r');
// Keeping it simple and exiting for the PoC: improve this in your implementation
break;
}
}
}
static void Main(string[] args)
{
RunAndManage("cmd.exe", " /c echo This was fun && echo Really && echo Really && pause && echo Wowzer Pause Bypassed 1>&2 ");
}
}
}
Output would look something like this (notice that we sent CarriageReturn once we saw the pause text being emitted):
Detailed and expansive examples are plenty on this idea on Stackoverflow and elsewhere. Here is one.
I am trying to create a C# console application that repeatedly runs a .bat file and saves the output into a variable to be modified later. The script is meant to get open TCP connections on a connected device using adb.exe.
I want the application to quit when the Esc key is pressed (once). To accomplish this I followed this answer and implemented it like so:
Program.cs
static void Main(string[] args)
{
Console.WriteLine("Press escape to quit");
do
{
while (!Console.KeyAvailable)
{
// Console.WriteLine("Application is running");
RunBatch();
}
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
Console.WriteLine("While loop was exited");
Console.ReadLine();
}
static void RunBatch()
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = #"C:\Dev\Batch\GetTcpConnections.bat";
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine(output);
}
GetTcpConnections.bat
#echo off
echo %time%
adb.exe shell cat /proc/net/tcp
timeout /t 1 /nobreak>nul
The expected result would be, that the console application exits the loop and prints While loop was exited directly upon pressing Esc. It works like this when I comment RunBatch() and un-comment Console.WriteLine("Application is running". However, when I am running the batch script, I need to hold down Esc for about a second or two before the program exits the while loop, instead of being instantaneous.
At first I thought that the input may be blocked by timeout /t 1 /nobreak>nul in the batch script, but removing this line made no difference. Am I missing something else here that could block inputs?
As soon as your consoleapp starts adb.exe, it looses the focus. When an applicaties does not have the focus, it does not receive any keyboard input, because the keyboard input goes to another focused application.
You can reclaim the focus by selecting the consoleapp with your mouse, while adb.exe is running, and than press ESC. But I guess that is not what you want.
I see serveral "solutions":
You could find a way to make your consoleapp always the top-level applicatie.
Make it a Desktop / Winform-application which has a bug "QUIT"-button.
The code below should solve your problem. Note that I have moved the timeout away from the batch file and placed it within the while loop.
Program.cs
private static void Main(string[] args)
{
Console.WriteLine("Press escape to quit");
do
{
while (!Console.KeyAvailable)
{
RunBatch();
Thread.Sleep(1000);
}
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
Console.WriteLine("While loop has exited");
Console.ReadLine();
}
private static void RunBatch()
{
var process = new Process
{
StartInfo =
{
UseShellExecute = false,
RedirectStandardOutput = true,
FileName = #"C:\Dev\Batch\GetTcpConnections.bat"
}
};
process.Start();
Console.WriteLine(process.StandardOutput.ReadToEnd());
}
GetTcpConnections.bat
#echo off
echo %time%
adb.exe shell cat /proc/net/tcp
First of all, I've read all related topics and they gave general idea but implementation doesn't work for me:
Send strings from one console application to another
How to send input to the console as if the user is typing?
Sending input/getting output from a console application (C#/WinForms)
I have a console application that is doing some actions in background until cancellation is requested. Typical usage scenario is :
1) Execute application
2) Enter input data
3) Issue start command
4) After some time passes, enter stop command
5) Exit application
Child application Program.cs :
static void Main()
{
Console.WriteLine("Enter input parameter : ");
var inputParameter = Console.ReadLine();
Console.WriteLine("Entered : " + inputParameter);
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Task.Factory.StartNew(() =>
{
while (true)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("Stopping actions");
return;
}
// Simulating some actions
Console.Write("*");
}
}, token);
if (Console.ReadKey().KeyChar == 'c')
{
tokenSource.Cancel();
Console.WriteLine("Stop command");
}
Console.WriteLine("Finished");
Console.ReadLine();
}
What I'm looking for is some sort of host utility to control this application - spawn multiple instances and perform required user actions on each instance.
Host application Program.cs :
static void Main()
{
const string exe = "Child.exe";
var exePath = System.IO.Path.GetFullPath(exe);
var startInfo = new ProcessStartInfo(exePath)
{
RedirectStandardOutput = true,
RedirectStandardInput = true,
WindowStyle = ProcessWindowStyle.Hidden,
WindowStyle = ProcessWindowStyle.Maximized,
CreateNoWindow = true,
UseShellExecute = false
};
var childProcess = new Process { StartInfo = startInfo };
childProcess.OutputDataReceived += readProcess_OutputDataReceived;
childProcess.Start();
childProcess.BeginOutputReadLine();
Console.WriteLine("Waiting 5s for child process to start...");
Thread.Sleep(5000);
Console.WriteLine("Enter input");
var msg = Console.ReadLine();
// Sending input parameter
childProcess.StandardInput.WriteLine(msg);
// Sending start command aka any key
childProcess.StandardInput.Write("s");
// Wait 5s while child application is working
Thread.Sleep(5000);
// Issue stop command
childProcess.StandardInput.Write("c");
// Wait for child application to stop
Thread.Sleep(20000);
childProcess.WaitForExit();
Console.WriteLine("Batch finished");
Console.ReadLine();
}
When I run this tool, after first input it crashes with "has stopped working" error and prompt to send memory dump to Microsoft. Output window in VS shows no exceptions.
Guess this problem occurs somewhere between applications and may be because of output stream buffer overflow (child app is writing a lot of stars each second which mimics real output which may be huge) and I yet have no idea how to fix it. I don't really need to pass child's output to host (only send start-stop commands to child), but commenting RedirectStandardOutput and OutputDataReceived doesn't fix this problem. Any ideas how to make this work?
I would recommend using NamedPipeServerStream and NamedPipeClientStream, which allows you to open a stream which will communicate between processes on a given machine.
First, this will create a pipe server stream and wait for someone to connect to it:
var stream = new NamedPipeServerStream(this.PipeName, PipeDirection.InOut);
stream.WaitForConnection();
return stream;
Then, this will connect to that stream (from your other process), allowing you to read / write in either direction:
var stream = new NamedPipeClientStream(".", this.PipeName, PipeDirection.InOut);
stream.Connect(100);
return stream;
Another alternative is to use MSMQ, you can find a good tutorial here
I would advise to look to the Working with memory mapped files in .NET 4
http://blogs.msdn.com/b/salvapatuel/archive/2009/06/08/working-with-memory-mapped-files-in-net-4.aspx
It's fast and efficient.
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();
I wrote a quick and dirty wrapper around svn.exe to retrieve some content and do something with it, but for certain inputs it occasionally and reproducibly hangs and won't finish. For example, one call is to svn list:
svn list "http://myserver:84/svn/Documents/Instruments/" --xml --no-auth-cache --username myuser --password mypassword
This command line runs fine when I just do it from a command shell, but it hangs in my app. My c# code to run this is:
string cmd = "svn.exe";
string arguments = "list \"http://myserver:84/svn/Documents/Instruments/\" --xml --no-auth-cache --username myuser --password mypassword";
int ms = 5000;
ProcessStartInfo psi = new ProcessStartInfo(cmd);
psi.Arguments = arguments;
psi.RedirectStandardOutput = true;
psi.WindowStyle = ProcessWindowStyle.Normal;
psi.UseShellExecute = false;
Process proc = Process.Start(psi);
StreamReader output = new StreamReader(proc.StandardOutput.BaseStream, Encoding.UTF8);
proc.WaitForExit(ms);
if (proc.HasExited)
{
return output.ReadToEnd();
}
This takes the full 5000 ms and never finishes. Extending the time doesn't help. In a separate command prompt, it runs instantly, so I'm pretty sure it's unrelated to an insufficient waiting time. For other inputs, however, this seems to work fine.
I also tried running a separate cmd.exe here (where exe is svn.exe and args is the original arg string), but the hang still occurred:
string cmd = "cmd";
string arguments = "/S /C \"" + exe + " " + args + "\"";
What could I be screwing up here, and how can I debug this external process stuff?
EDIT:
I'm just now getting around to addressing this. Mucho thanks to Jon Skeet for his suggestion, which indeed works great. I have another question about my method of handling this, though, since I'm a multi-threaded novice. I'd like suggestions on improving any glaring deficiencies or anything otherwise dumb. I ended up creating a small class that contains the stdout stream, a StringBuilder to hold the output, and a flag to tell when it's finished. Then I used ThreadPool.QueueUserWorkItem and passed in an instance of my class:
ProcessBufferHandler bufferHandler = new ProcessBufferHandler(proc.StandardOutput.BaseStream,
Encoding.UTF8);
ThreadPool.QueueUserWorkItem(ProcessStream, bufferHandler);
proc.WaitForExit(ms);
if (proc.HasExited)
{
bufferHandler.Stop();
return bufferHandler.ReadToEnd();
}
... and ...
private class ProcessBufferHandler
{
public Stream stream;
public StringBuilder sb;
public Encoding encoding;
public State state;
public enum State
{
Running,
Stopped
}
public ProcessBufferHandler(Stream stream, Encoding encoding)
{
this.stream = stream;
this.sb = new StringBuilder();
this.encoding = encoding;
state = State.Running;
}
public void ProcessBuffer()
{
sb.Append(new StreamReader(stream, encoding).ReadToEnd());
}
public string ReadToEnd()
{
return sb.ToString();
}
public void Stop()
{
state = State.Stopped;
}
}
This seems to work, but I'm doubtful that this is the best way. Is this reasonable? And what can I do to improve it?
One standard issue: the process could be waiting for you to read its output. Create a separate thread to read from its standard output while you're waiting for it to exit. It's a bit of a pain, but that may well be the problem.
Jon Skeet is right on the money!
If you don't mind polling after you launch your svn command try this:
Process command = new Process();
command.EnableRaisingEvents = false;
command.StartInfo.FileName = "svn.exe";
command.StartInfo.Arguments = "your svn arguments here";
command.StartInfo.UseShellExecute = false;
command.StartInfo.RedirectStandardOutput = true;
command.Start();
while (!command.StandardOutput.EndOfStream)
{
Console.WriteLine(command.StandardOutput.ReadLine());
}
I had to drop an exe on a client's machine and use Process.Start to launch it.
The calling application would hang - the issue ended up being their machine assuming the exe was dangerous and preventing other applications from starting it.
Right click the exe and go to properties. Hit "Unblock" toward the bottom next to the security warning.
Based on Jon Skeet's answer this is how I do it in modern day (2021) .NET 5
var process = Process.Start(processStartInfo);
var stdErr = process.StandardError;
var stdOut = process.StandardOutput;
var resultAwaiter = stdOut.ReadToEndAsync();
var errResultAwaiter = stdErr.ReadToEndAsync();
await process.WaitForExitAsync();
await Task.WhenAll(resultAwaiter, errResultAwaiter);
var result = resultAwaiter.Result;
var errResult = errResultAwaiter.Result;
Note that you can't await the standard output before the error, because the wait will hang in case the standard error buffer gets full first (same for trying it the other way around).
The only way is to start reading them asynchronously, wait for the process to exit, and then complete the await by using Task.WaitAll
I know this is an old post but maybe this will assist someone. I used this to execute some AWS (Amazon Web Services) CLI commands using .Net TPL tasks.
I did something like this in my command execution which is executed within a .Net TPL Task which is created within my WinForm background worker bgwRun_DoWork method which holding a loop with while(!bgwRun.CancellationPending). This contains the reading of the Standard Output from the Process via a new Thread using the .Net ThreadPool class.
private void bgwRun_DoWork(object sender, DoWorkEventArgs e)
{
while (!bgwRun.CancellationPending)
{
//build TPL Tasks
var tasks = new List<Task>();
//work to add tasks here
tasks.Add(new Task(()=>{
//build .Net ProcessInfo, Process and start Process here
ThreadPool.QueueUserWorkItem(state =>
{
while (!process.StandardOutput.EndOfStream)
{
var output = process.StandardOutput.ReadLine();
if (!string.IsNullOrEmpty(output))
{
bgwRun_ProgressChanged(this, new ProgressChangedEventArgs(0, new ExecutionInfo
{
Type = "ExecutionInfo",
Text = output,
Configuration = s3SyncConfiguration
}));
}
if (cancellationToken.GetValueOrDefault().IsCancellationRequested)
{
break;
}
}
});
});//work Task
//loop through and start tasks here and handle completed tasks
} //end while
}
I know my SVN repos can run slow sometimes, so maybe 5 seconds isn't long enough? Have you copied the string you are passing to the process from a break point so you are positive it's not prompting you for anything?