Console.Writeline from EventHandler - c#

I've written a simple async TCP-Server - it works well. But now I want to make an output of the received Data on the Console-Testprogram. And the problem is, this isn't working! If I connect the MainThread to the EventHandler the program does nothing. Debug is showing that the sc05Server_DataAvailable was called but then nothing happened. The program is still responsive.
Here The Code:
private void ReadCallback(IAsyncResult asyncResult)
{
Sc05BdClient sc05BdClient = asyncResult.AsyncState as Sc05BdClient;
if (sc05BdClient == null) return;
NetworkStream networkStream = sc05BdClient.NetworkStream;
int read = networkStream.EndRead(asyncResult);
if (read == 0)
{
lock (clients)
{
clients.Remove(sc05BdClient);
return;
}
}
string data = Encoding.GetString(sc05BdClient.Buffer, 0, read);
System.Diagnostics.Debug.Print(data);
OnDataAvailable(this, new DataAvailableEventArgs(data)); <---- here Handler is called
networkStream.BeginRead(sc05BdClient.Buffer, 0, sc05BdClient.Buffer.Length, ReadCallback, sc05BdClient);
}
public event EventHandler<DataAvailableEventArgs> DataAvailable;
protected virtual void OnDataAvailable(object sender, DataAvailableEventArgs e)
{
EventHandler<DataAvailableEventArgs> handler = DataAvailable;
if (handler != null)
handler(sender, e);
}
public class DataAvailableEventArgs : EventArgs
{
public string Data;
public DataAvailableEventArgs(string data)
{
Data = data;
}
}
The Main program:
class Program
{
static void Main()
{
Sc05BdServer sc05BdServer = new Sc05BdServer(IPAddress.Any, 2006);
sc05BdServer.DataAvailable += sc05BdServer_DataAvailable;
sc05BdServer.Start();
Console.ReadKey();
sc05BdServer.Stop();
}
static void sc05BdServer_DataAvailable(object sender, DataAvailableEventArgs e)
{
Console.WriteLine(e.Data); <--- this is called once
}
}
I think it has something to do with Threading - but I have no idea how to work with them.

You are probably experiencing some kind of race issue although Console should be immune to that. Check this question but note that I couldn't reproduce the problem: Strange behaviour of Console.ReadKey() with multithreading

Related

Updating GUI from different Class and Thread in c#

Im new to programming and just wanted to know if a solution for a problem I got is appropriate.
I wanted to write a status (string) into a textbox from a class which is creating a Socket and the class listens for data to receives (in an other thread).
This is what i did:
Create the Class whithin the Form.cs with a button click:
private void button_Create_Click(object sender, EventArgs e)
{
int port;
Int32.TryParse(textBox_Port.Text, out port);
ServerSocketClass serverSocket = new ServerSocketClass(port, this);
}
The ServerSocketClass looks like:
class ServerSocketClass
{
Socket ServerSocket;
Socket Accepted;
IPEndPoint LocalEndpoint;
int Port = 1337; // just for fun
Messenger MainForm;
public ServerSocketClass(int port, Messenger form)
{
MainForm = form;
if (port != 0)
Port = port;
ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
LocalEndpoint = new IPEndPoint(IPAddress.Any, Port);
MainForm.writeToMessages("Binding Endpoint to Socket...");
ServerSocket.Bind(LocalEndpoint);
MainForm.writeToMessages("Starting ServerListener Thread...");
Thread ServerListenThread = new Thread(startListening);
ServerListenThread.Name = "ServerListenerThread";
ServerListenThread.Start();
}
private void startListening()
{
ServerSocket.Listen(5);
MainForm.writeToMessages("Whaiting for incoming connections...");
Accepted = ServerSocket.Accept();
whaitForData();
}
and to update the GUI in the forms class i created a delegate and a "update" method with an invoke:
public delegate void writeMessege(string message);
public writeMessege MessegeDelegate;
public void writeToMesseges(string messege)
{
if (InvokeRequired)
{
this.Invoke(MessegeDelegate, new object[] { messege });
return;
}
textBox_Messeges.AppendText("SYSTEM: " + messege + "\n");
}
It works, but I wanted to know if this is a "valid" way to do it or if I should go to the developer hell ;-)
thanks in advance
Locke
It's a perfectly valid way to do that, although whether it is "right" depends very much on the context - how often you call it, what you want to do inside it, and the code that you need to call it. There are many different ways of doing it without invoke, but there is nothing wrong with using InvokeRequired/Invoke - that's what it's there for. You could just use an update method that invokes itself, which is almost the same as your code, but slightly less verbose:
public void WriteMessages(string message)
{
if (InvokeRequired)
{ this.Invoke(new Action<string>(WriteMessages), new object[] { message }); }
else
{ textBox_Messages.AppendText("SYSTEM: " + message + "\n"); }
}
There are a lot of posts already on Invoke/InvokeRequired. As a starting point, check:
Isn't blindly using InvokeRequired just bad practice?
I had a similar situation, where I had a class that was called from other classes with many separate threads and I had to update one specific form from all these other threads. So creating a delegate and an event in the class with a handler in the form was the answer. So I wanted to share it as it seems simpler (even if not necessarily a better solution).
The solution that worked for me:
I created an event in the class I wanted to do the update on another form. (First of course I instantiated the form (called SubAsstToolTipWindow) in the class.
Then I used this event (ToolTipShow) to create an event handler on the form I wanted to update the label on. Worked like a charm.
I used this description to devise my own code below in the class that does the update:
public static class SubAsstToolTip
{
private static SubAsstToolTipWindow ttip = new SubAsstToolTipWindow();
public delegate void ToolTipShowEventHandler();
public static event ToolTipShowEventHandler ToolTipShow;
public static void Show()
{
// This is a static boolean that I set here but is accessible from the form.
Vars.MyToolTipIsOn = true;
if (ToolTipShow != null)
{
ToolTipShow();
}
}
public static void Hide()
{
// This is a static boolean that I set here but is accessible from the form.
Vars.MyToolTipIsOn = false;
if (ToolTipShow != null)
{
ToolTipShow();
}
}
}
Then the code in my form that was updated:
public partial class SubAsstToolTipWindow : Form
{
public SubAsstToolTipWindow()
{
InitializeComponent();
// Right after initializing create the event handler that
// traps the event in the class
SubAsstToolTip.ToolTipShow += SubAsstToolTip_ToolTipShow;
}
private void SubAsstToolTip_ToolTipShow()
{
if (Vars.MyToolTipIsOn) // This boolean is a static one that I set in the other class.
{
// Call other private method on the form or do whatever
ShowToolTip(Vars.MyToolTipText, Vars.MyToolTipX, Vars.MyToolTipY);
}
else
{
HideToolTip();
}
}
long time ago, but I wanted you all know how I finally solved this to my full satisfaction (solved it with Events - of course ;-)):
I defined an EventArgs to pass all the Information I wanted to pass:
public class IncomingMessageEventArgs : EventArgs
{
private Message _message;
public Message Message
{
get
{
return _message;
}
}
public IncomingMessageEventArgs(Message message)
{
_message = message;
}
}
On the Class that publishes the information (to the WPF - Form) define the Event and its Handler:
public delegate void IncomingMessageEventHandler(object sender, IncomingMessageEventArgs e);
public event IncomingMessageEventHandler IncomingMessageEvent;
protected void OnIncomingMessageEvent(IncomingMessageEventArgs e)
{
if (IncomingMessageEvent != null)
IncomingMessageEvent(this, e);
}
and of course Raise the event, if the WPF Form needs to be updated (also on the "information sending class"):
OnIncomingMessageEvent(new IncomingMessageEventArgs(message));
on the WPF Class you need to listen to the events but first define a EventHandler because your information comes from a differen Thread!! :
private delegate void writeMessageToChatEventHandler(object sender, IncomingMessageEventArgs e);
now we write our method witch will handle the raised event:
// Write to Chat
private void writeMessageToChat(object sender, IncomingMessageEventArgs e)
{
try
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.Invoke(new writeMessageToChatEventHandler(writeMessageToChat), new object[] { sender, e } );
return;
}
textBox_Chat.AppendText(e.Message.getFormatedMessageText() + "\n");
}
catch (Exception ex)
{
writeLogToChat(this, new IncomingLogEventArgs("ERROR: " + ex.Message));
}
}
and finally, we need to subscribe to the event of course (the first method, you can ignore, its just to meet the MS Nameing conventions:
private void ClientSocket_IncomingMessageEvent(object sender, IncomingMessageEventArgs e)
{
writeMessageToChat(sender, e);
}
ClientSocket.IncomingMessageEvent += ClientSocket_IncomingMessageEvent;
Hopefully I made this understandable :P
Thanks to all the people how helped me!
bye

A single thread to which you can subscribe methods, to be executed in an unordered fashion

I need the following logic implemented:
a thread to which you can subscribe/unsubscribe methods at runtime.
It's fine for all these methods to have an header such as (Object sender, EventArgs e) and to return void.
These methods scope must be the scope of the class where they are defined lexically.
There's no guarantee about the order of execution
I've come up with the following implementation, which seems to do exactly what I need: basically I start an internal thread which triggers an event each x milliseconds. You can subscribe/unsubscribe delegates to this event through appropriate methods.
Before sticking to it I'd like to know if there may be subtle issues following this approach.
public class Orchestrator
{
private Thread _mainThread;
private event MethodDelegate _mainEvent;
public delegate void MethodDelegate (Object sender, EventArgs e);
private bool _stop = false;
private short _ms = 100;
public short PollingInterval { get { return _ms; }
set
{
_ms = value;
}
}
public Orchestrator()
{
_mainThread = new Thread(new ThreadStart(_execute));
}
public void Start()
{
_stop = false;
_mainThread.Start();
}
public void Stop()
{
_stop = true;
}
public void Clear()
{
_mainEvent = null;
}
public void Push(MethodDelegate method)
{
_mainEvent += method;
}
public void Pop(MethodDelegate method)
{
_mainEvent -= method;
}
private void _execute()
{
while(!_stop)
{
if (_mainEvent != null)
try
{
_mainEvent(this, new EventArgs());
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Thread.Sleep(_ms);
}
}
}
That's basically fine. You need to make _stop volatile. In C# the event accessor methods are thread-safe so that works fine.
The exception handling is very questionable. Do you really want to spam errors to the console? Define an event OnError and report errors to the consumer of your class.
You could use a timer or await Task.Delay to save a thread. This would make sense if there are a lot of such class instances at the same time. If there is just one this is likely not worth the effort.
You have a race condition which could cause a NullReferenceException, in:
while(!_stop)
{
if (_mainEvent != null)
try
{
_mainEvent(this, new EventArgs());
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Thread.Sleep(_ms);
}
Some other thread could unsubscribe from the event or call Clear() inbetween if (_mainEvent != null) and the call of _mainEvent.
To avoid this, you should copy _mainEvent into a local variable and check that against null, and use that instead:
var mainEvent = _mainEvent;
if (mainEvent != null)
try
{
mainEvent(this, new EventArgs());
In any case, I think you should be using a Timer for this rather than rolling-your-own.

Problems with receiving UDP messages in Metro-Apps

I'm currently working on a project and one of the featured devices is a Windows Tablet. To "connect" it to other devices (like some Raspberry Pi) in the project environment UDP is used to send messages. The Windows Tablet is intended to be some controlling device with soem touch functionality. Therefore I'm writing an App (and the intention of the App is not to put it into the Windows Store). The UDP part in this work is quite painful because I had to do much research since I started with no experience in App programming. More painful than the programming is, that I practically finished the work only to start over again because the App didn't receive UDP anymore.
Here's my code (I removed elements not relevant to the actual problem). I apologize for the bad coding....
App.xaml.cs:
sealed partial class App : Application
{
NetworkInterface ni = new NetworkInterface();
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
ni.MessageReceived += OnMessageReceived;
ni.Connect(new HostName("127.0.0.1"), "5556");
}
private void OnMessageReceived(object sender, MessageReceivedEventArgs e)
{
Debug.WriteLine("Processing");
Debug.WriteLine(e.Message.Data);
}
public static new App Current
{
get { return Application.Current as App; }
}
private DatagramSocket _socket;
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Update_Timer();
}
DispatcherTimer timer = new DispatcherTimer();
private void Update_Timer()
{
timer.Start();
timer.Interval = new TimeSpan(0,0,0,0,500);
timer.Tick += alive;
}
private void alive(object sender, object e)
{
if (start == 0) {
Debug.WriteLine("App-Startup");
ni.SendMessage("Startup...");
start++;
}
else
{
Debug.WriteLine("App-Alive");
ni.SendMessage("alive");
start++;
}
}
}
This part of code is to send and receive Messages in the backgrond in the whole App.
And a NetworkInterface class:
class NetworkInterface
{
private DatagramSocket _socket;
public bool IsConnected { get; set; }
public NetworkInterface()
{
IsConnected = false;
_socket = new DatagramSocket();
_socket.MessageReceived += OnSocketMessageReceived;
}
public async void Connect(HostName remoteHostName, string remoteServiceNameOrPort)
{
if (IsConnected != true)
{
await _socket.BindServiceNameAsync("5321");
await _socket.ConnectAsync(remoteHostName, remoteServiceNameOrPort);
}
IsConnected = true;
}
public void alive(object sender, object e)
{
Debug.WriteLine("alive");
}
public event EventHandler<MessageReceivedEventArgs> MessageReceived;
private void OnSocketMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
{
var reader = args.GetDataReader();
var count = reader.UnconsumedBufferLength;
var data = reader.ReadString(count);
Debug.WriteLine(args);
if (MessageReceived != null)
{
var ea = new MessageReceivedEventArgs();
ea.Message = new Message() { Data = data };
ea.RemoteHostName = args.RemoteAddress;
ea.RemotePort = args.RemotePort;
MessageReceived(this, ea);
}
}
DataWriter _writer = null;
public async void SendMessage(string message)
{
if (_writer == null)
{
var stream = _socket.OutputStream;
_writer = new DataWriter(stream);
}
_writer.WriteString(message);
await _writer.StoreAsync();
}
}
The main problems are:
If I dont send something before receiving, I won't be able top get an message.
If I send before I have random Faults at this line:
var reader = args.GetDataReader();
If nothing fails, I'm not able to receive messages from a local Python script (which works) but I can send messages from a local program which the App receives.
Does anyone know how I can fix these problems?
Thanks in advance!

C# ProgressBar Not Filling

I am developing an application that uses a progressbar to show the user any development in the program. The program I am writing uses a progressbar to show the user how many packets of data have been sent in the course of execution. I have created an event in the underlying library that triggers once another packet has been transmitted. Here are the event lines I have set up:
public delegate void ChangedEventHandler(object sender, EventArgs e);
public event ChangedEventHandler PercentageUpdate;
protected internal virtual void OnPercentageChanged(EventArgs e)
{
if (PercentageUpdate != null) PercentageUpdate(this, e);
}
Note that the above code is set up in the underlying library. I have the event triggered in this library like so:
//reinitializing is extraneous, as Receive() calls are overwriting
try
{
fsa.rnd = fsa.TransferSocket.Receive(fsa.File_Buffer, 0, FSArgs.BlockSize, SocketFlags.None);
}
catch (Exception e)
{
fsa.Dispose();
throw new GeneralNetworkingException("FileSocket receive() failed!", e);
}
fsa.tot += Convert.ToInt64(fsa.rnd);
fsa.TransferPercentage = (fsa.tot/fsa.FileSize) * 100;
fsa.OnPercentageChanged(EventArgs.Empty); //throw event for form
Finally, in the application, I have the event initialized like so:
public delegate void UpdatePerc(int index);
f_list[f_list.Count-1].fsa.PercentageUpdate += (sender, e) => filesocket_percentage_updated(sender, e, sel_index);
private void filesocket_percentage_updated(object sender, EventArgs e, int index)
{
UpdateP(index);
}
private void UpdateP(int index)
{
if (progressBar1.InvokeRequired)
{
this.BeginInvoke(new UpdatePerc(UpdateP), new object[] { index });
}
else
{
if (sel_index == index) //then redraw progressbar
{
if (x_list[index] > 1)
{
y_list[index]++;
if (((int)(y_list[index] % x_list[index])) == 0)
{
z_list[index]++;
progressBar1.Increment(1);
progressBar1.Update();
}
}
else
{
z_list[index]++;
progressBar1.Increment(1);
progressBar1.Update();
richTextBox1.Text += (z_list[index]+ '\n');
}
}
else
{
if (x_list[index] > 1)
{
y_list[index]++;
if (((int)(y_list[index] % x_list[index])) == 0)
{
z_list[index]++;
}
}
else
{
z_list[index]++;
}
}
}
}
As you can see I am trying to invoke the progressbar to update on a parallel thread. However, nothing happens at all. Any help would be appreciated!
Thanks.

Stuck on GenerateConsoleCtrlEvent in C# with console apps

I'm having the hardest time trying to get this to work, hoping one of you has done this before.
I have a C# console app that is running a child process which inherits its console. I want a ctrl-c caught by the outer app to be passed along to the inner app so that it can have a chance to shut down nicely.
I have some very simple code. I start a Process, then poll it with WaitForExit(10). I also have a CancelKeyPress handler registered, which sets a bool to true when it fires. The polling loop also checks this, and when it's true, it calls GenerateConsoleCtrlEvent() (which I have mapped through pinvoke).
I've tried a lot of combinations of params to GenerateConsoleCtrlEvent(). 0 or 1 for the first param, and either 0 or the child process's ID for the second param. Nothing seems to work. Sometimes I get a false back and Marshal.GetLastWin32Error() returns 0, and sometimes I get true back. But none cause the child app to receive a ctrl-c.
To be absolutely sure, I wrote a test C# app to be the child app which prints out what's going on with it and verified that manually typing ctrl-c when it runs does properly cause it to quit.
I've been banging my head against this for a couple hours. Can anyone give me some pointers on where to go with this?
Not so sure this is a good approach. This only works if the child process is created with the CREATE_NEW_PROCESS_GROUP flag for CreateProcess(). The System.Diagnostics.Process class however does not support this.
Consider using the return value from the Main() method. There is already a unique value defined in the Windows SDK for Ctrl+C aborts, STATUS_CONTROL_C_EXIT or 0xC000013A. The parent process can get that return code from the Process.ExitCode property.
Did you have any luck with this? My understanding is that when you press CTRL+C in a console, by default all the processes attached to the console receive it, not just the parent one. Here's an example:
Child.cs:
using System;
public class MyClass
{
public static void CtrlCHandler(object sender, ConsoleCancelEventArgs args)
{
Console.WriteLine("Child killed by CTRL+C.");
}
public static void Main()
{
Console.WriteLine("Child start.");
Console.CancelKeyPress += CtrlCHandler;
System.Threading.Thread.Sleep(4000);
Console.WriteLine("Child finish.");
}
}
Parent.cs:
using System;
public class MyClass
{
public static void CtrlCHandler(object sender, ConsoleCancelEventArgs args)
{
Console.WriteLine("Parent killed by CTRL+C.");
}
public static void Main()
{
Console.CancelKeyPress += CtrlCHandler;
Console.WriteLine("Parent start.");
System.Diagnostics.Process child = new System.Diagnostics.Process();
child.StartInfo.UseShellExecute = false;
child.StartInfo.FileName = "child.exe";
child.Start();
child.WaitForExit();
Console.WriteLine("Parent finish.");
}
}
Output:
Y:\>parent
Parent start.
Child start.
Parent killed by CTRL+C.
Child killed by CTRL+C.
^C
Y:\>parent
Parent start.
Child start.
Child finish.
Parent finish.
So I wouldn't have thought you'd need to do anything special. However, if you really need to generate CTRL+C events yourself, things might not be so easy. I'm not sure about the problems you describe, but as far as I can tell you can only send CTRL+C events to all the processes attached to a console window. If you detach a process, you can't send it CTRL+C events. If you want to be selective in which processes to send the CTRL+C events, you seem to need to create new console windows for every one. I've no idea if there's some way to do it without visible windows or when you want to redirect I/O using pipes.
Here is my solution for sending ctrl-c to a process. FYI, I never got GenerateConsoleCtrlEvent to work.
Rather than using GenerateConsoleCtrlEvent, here is how I have found to send CTRL-C to a process. FYI, in this case, I didn't ever need to find the group process ID.
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public class ConsoleAppManager
{
private readonly string appName;
private readonly Process process = new Process();
private readonly object theLock = new object();
private SynchronizationContext context;
private string pendingWriteData;
public ConsoleAppManager(string appName)
{
this.appName = appName;
this.process.StartInfo.FileName = this.appName;
this.process.StartInfo.RedirectStandardError = true;
this.process.StartInfo.StandardErrorEncoding = Encoding.UTF8;
this.process.StartInfo.RedirectStandardInput = true;
this.process.StartInfo.RedirectStandardOutput = true;
this.process.EnableRaisingEvents = true;
this.process.StartInfo.CreateNoWindow = true;
this.process.StartInfo.UseShellExecute = false;
this.process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
this.process.Exited += this.ProcessOnExited;
}
public event EventHandler<string> ErrorTextReceived;
public event EventHandler ProcessExited;
public event EventHandler<string> StandartTextReceived;
public int ExitCode
{
get { return this.process.ExitCode; }
}
public bool Running
{
get; private set;
}
public void ExecuteAsync(params string[] args)
{
if (this.Running)
{
throw new InvalidOperationException(
"Process is still Running. Please wait for the process to complete.");
}
string arguments = string.Join(" ", args);
this.process.StartInfo.Arguments = arguments;
this.context = SynchronizationContext.Current;
this.process.Start();
this.Running = true;
new Task(this.ReadOutputAsync).Start();
new Task(this.WriteInputTask).Start();
new Task(this.ReadOutputErrorAsync).Start();
}
public void Write(string data)
{
if (data == null)
{
return;
}
lock (this.theLock)
{
this.pendingWriteData = data;
}
}
public void WriteLine(string data)
{
this.Write(data + Environment.NewLine);
}
protected virtual void OnErrorTextReceived(string e)
{
EventHandler<string> handler = this.ErrorTextReceived;
if (handler != null)
{
if (this.context != null)
{
this.context.Post(delegate { handler(this, e); }, null);
}
else
{
handler(this, e);
}
}
}
protected virtual void OnProcessExited()
{
EventHandler handler = this.ProcessExited;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
protected virtual void OnStandartTextReceived(string e)
{
EventHandler<string> handler = this.StandartTextReceived;
if (handler != null)
{
if (this.context != null)
{
this.context.Post(delegate { handler(this, e); }, null);
}
else
{
handler(this, e);
}
}
}
private void ProcessOnExited(object sender, EventArgs eventArgs)
{
this.OnProcessExited();
}
private async void ReadOutputAsync()
{
var standart = new StringBuilder();
var buff = new char[1024];
int length;
while (this.process.HasExited == false)
{
standart.Clear();
length = await this.process.StandardOutput.ReadAsync(buff, 0, buff.Length);
standart.Append(buff.SubArray(0, length));
this.OnStandartTextReceived(standart.ToString());
Thread.Sleep(1);
}
this.Running = false;
}
private async void ReadOutputErrorAsync()
{
var sb = new StringBuilder();
do
{
sb.Clear();
var buff = new char[1024];
int length = await this.process.StandardError.ReadAsync(buff, 0, buff.Length);
sb.Append(buff.SubArray(0, length));
this.OnErrorTextReceived(sb.ToString());
Thread.Sleep(1);
}
while (this.process.HasExited == false);
}
private async void WriteInputTask()
{
while (this.process.HasExited == false)
{
Thread.Sleep(1);
if (this.pendingWriteData != null)
{
await this.process.StandardInput.WriteLineAsync(this.pendingWriteData);
await this.process.StandardInput.FlushAsync();
lock (this.theLock)
{
this.pendingWriteData = null;
}
}
}
}
}
Then, in actually running the process and sending the CTRL-C in my main app:
DateTime maxStartDateTime = //... some date time;
DateTime maxEndDateTime = //... some later date time
var duration = maxEndDateTime.Subtract(maxStartDateTime);
ConsoleAppManager appManager = new ConsoleAppManager("myapp.exe");
string[] args = new string[] { "args here" };
appManager.ExecuteAsync(args);
await Task.Delay(Convert.ToInt32(duration.TotalSeconds * 1000) + 20000);
if (appManager.Running)
{
// If stilll running, send CTRL-C
appManager.Write("\x3");
}
For details, please see Redirecting standard input of console application and Windows how to get the process group of a process that is already running?

Categories