Console app only terminates when holding key down while running batch script - c#

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

Related

How do I stop my C# console program from stopping when executing "console.readline();" twice

i'm really new to C# and i've been working on this really simple command line style program (that has custom commands and such). Now the commands work great but every time I allow the user to go back to enter another command or just anything it closes the program when I press enter. But only the second time I execute a command. I think this has something to do with console.WriteLine();
Here's my code (I've searched everywhere on how to fix this and nothing that i've found has worked)
using System;
namespace ConsoleProgram
{
class Program
{
private static string userEnteredCommand;
static void Main(string[] args)
{
Console.Title = "IAO Systems Service Console";
onCommandLineStart();
void onCommandLineStart()
{
Console.WriteLine("Copyright (C) 2018 IAO Corporation");
Console.WriteLine("IAO Systems Service Console (type 'sinfo' for more information.");
userEnteredCommand = Console.ReadLine();
}
void onCommandLineReturn()
{
userEnteredCommand = Console.ReadLine();
}
// Commands
if (userEnteredCommand == "sinfo")
{
Console.WriteLine(" ");
Console.WriteLine("Program information:");
Console.WriteLine("Created for IAO Corporation, by Zreddx");
Console.WriteLine("This program controls doors, gates and e.t.c within IAO Terratory.");
Console.WriteLine(" ");
Console.WriteLine("This program is protected by copyright, do not redistribute. ");
}
else
{
Console.WriteLine("That command does not exist, do 'programs' for a list of actions.");
}
onCommandLineReturn();
}
}
}
Console applications close when they get to the end of Main. It's exiting after the Console.ReadLine in onCommandLineReturn();.
Add a bool variable called keepLooping, set it to true, and wrap your code in a while(keepLooping) statement. Somewhere in your program flow, check for input like "quit" or "exit" and set the keepLooping variable to false.
Here's an example of it in a dotnetfiddle: https://dotnetfiddle.net/Jguj5k

Check for open Windows application and wait until it is closed?

I have a pretty simple program where it runs certain steps. Each step should run pragmatically. I am having trouble with a bit of my code. Where I am relying on an application to close (waiting for user to close OUTLOOK ) to execute my next block of code. It launches the first file fine but it reads OUTLOOK as open then it wont work. I wish to make it that when the user closes outlook it will continue and open the next HTML file I have tried to Google for something like wait for exit on this line of code Process[] localByName = Process.GetProcessesByName("OUTLOOK");
but I couldnt find anything
static void Main(string[] args)
{
var myProcess = new Process { StartInfo = new ProcessStartInfo(#"c:\TestFile1.html") };
myProcess.Start();
//Launches the html file
Thread.Sleep(1000);
Process[] localByName = Process.GetProcessesByName("OUTLOOK");
//used for detecting whether outlook is open
if (localByName.Length == 0)
{
//Only runs when outlook is closed by user
var myProcess2 =
new Process { StartInfo = new ProcessStartInfo(#"c:\TESTFILE2.html") };
myProcess2.Start();
}
else
{
Console.WriteLine("Im not going to work " + localByName.Length);
Console.ReadLine();
}
}
You are searching for the Process.WaitForExit()Method ( https://msdn.microsoft.com/library/fb4aw7b8(v=vs.110).aspx)
You can use it like:
foreach(var process in localByName) {
if(!process.HasExited()) {
process.WaitForExit();
}
}

Ways of console pausing

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.

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();

Redirect console input and output to a textbox

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));
}
}

Categories