Raising Custom Class Events In Windows Service C# - c#

I did write a windows service that can connect to a network device using a dll. so everything works fine, but The event handler does not work in win service! here is my code :
My Custom Class Code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyNewService
{
public class zkemkeeperHandler
{
public event EventHandler OnFinger;
public event EventHandler<VerifyEventArgs> OnVerify;
private System.Diagnostics.EventLog eventLog1 = new System.Diagnostics.EventLog();
public zkemkeeper.CZKEMClass axCZKEM1 = new zkemkeeper.CZKEMClass();
private bool bIsConnected = false;
private int iMachineNumber = 1;
public zkemkeeperHandler()
{
((System.ComponentModel.ISupportInitialize)(this.eventLog1)).BeginInit();
this.eventLog1.Log = "DoDyLog";
this.eventLog1.Source = "DoDyLogSource";
((System.ComponentModel.ISupportInitialize)(this.eventLog1)).EndInit();
eventLog1.WriteEntry("zkemkeeperHandler constructor");
}
public void startService()
{
eventLog1.WriteEntry("start service for (192.168.0.77:4370)");
bIsConnected = axCZKEM1.Connect_Net("192.168.0.77", Convert.ToInt32("4370"));
if (bIsConnected == true)
{
eventLog1.WriteEntry("bIsConnected == true !");
iMachineNumber = 1;
if (axCZKEM1.RegEvent(iMachineNumber, 65535))
{
this.axCZKEM1.OnFinger += new kemkeeper._IZKEMEvents_OnFingerEventHandler(axCZKEM1_OnFinger);
this.axCZKEM1.OnVerify += new zkemkeeper._IZKEMEvents_OnVerifyEventHandler(axCZKEM1_OnVerify);
//This Log Appears in Event Viewer
eventLog1.WriteEntry("Define events (OnFingers and OnVerify) !");
//This Line Fires Event in Service1.cs for testing event handler
Finger(EventArgs.Empty);
}
}
else
{
eventLog1.WriteEntry("Unable to connect the device");
}
}
public void stopService()
{
if (bIsConnected) {axCZKEM1.Disconnect(); bIsConnected = false;}
}
//This method doesn't run :(
private void axCZKEM1_OnFinger()
{
Finger(EventArgs.Empty);
}
//This method doesn't run too :(
private void axCZKEM1_OnVerify(int iUserID)
{
VerifyEventArgs args = new VerifyEventArgs();
args.UserID = iUserID;
Verify(args);
}
public class VerifyEventArgs : EventArgs
{
public int UserID { get; set; }
}
protected virtual void Finger(EventArgs e)
{
EventHandler handler = OnFinger;
if (handler != null)
handler(this, e);
}
protected virtual void Verify(VerifyEventArgs e)
{
EventHandler<VerifyEventArgs> handler = OnVerify;
if (handler != null)
handler(this, e);
}
}
}
My Main Service Class Code :
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Linq;
using System.Threading;
namespace MyNewService
{
public class Service1 : System.ServiceProcess.ServiceBase
{
private System.Diagnostics.EventLog eventLog1;
private System.ComponentModel.Container components = null;
zkemkeeperHandler zkh;
public Service1()
{
InitializeComponent();
if (!System.Diagnostics.EventLog.SourceExists("DoDyLogSource"))
{
System.Diagnostics.EventLog.CreateEventSource("DoDyLogSource", "DoDyLog");
}
eventLog1.Source = "DoDyLogSource";
eventLog1.Log = "DoDyLog";
eventLog1.WriteEntry("Preparing to start service");
try
{
startZKHandler();
}
catch (Exception ex)
{
eventLog1.WriteEntry(ex.InnerException.Message);
}
}
private void startZKHandler()
{
eventLog1.WriteEntry("creating zkemkeeper handler class");
zkh = new zkemkeeperHandler();
zkh.OnFinger += OnFinger;
zkh.OnVerify += OnVerify;
zkh.startService();
}
private void stopZKHandler()
{
eventLog1.WriteEntry("Disconnecting from device (192.168.0.77)...");
zkh.stopService();
}
private void writeLog2DB(string message)
{
try
{
eventLog1.WriteEntry("writing to database");
DB.DBase.LogTable.AddObject(new LogTable
{
ID = ++DB.IDCounter,
deviceLog = message
});
DB.DBase.SaveChanges();
}
catch (Exception ex)
{
eventLog1.WriteEntry(ex.Message + " - " + ex.InnerException.Message);
}
this.EventLog.Log = "Event Stored in DB.";
}
// The main entry point for the process
static void Main()
{
System.ServiceProcess.ServiceBase[] ServicesToRun;
ServicesToRun = new System.ServiceProcess.ServiceBase[] { new MyNewService.Service1()};
System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}
private void InitializeComponent()
{
this.eventLog1 = new System.Diagnostics.EventLog();
((System.ComponentModel.ISupportInitialize)(this.eventLog1)).BeginInit();
this.eventLog1.Log = "DoDyLog";
this.eventLog1.Source = "DoDyLogSource";
this.ServiceName = "MyNewService";
((System.ComponentModel.ISupportInitialize)(this.eventLog1)).EndInit();
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
eventLog1.WriteEntry("my service started");
}
protected override void OnStop()
{
// TODO: Add code here to perform any tear-down necessary to stop your service.
eventLog1.WriteEntry("my service stoped");
stopZKHandler();
}
protected override void OnContinue()
{
eventLog1.WriteEntry("my service is continuing in working");
}
private void OnFinger(object sender, EventArgs e)
{
eventLog1.WriteEntry("Finger Event Raised");
}
private void OnVerify(object sender, zkemkeeperHandler.VerifyEventArgs e)
{
eventLog1.WriteEntry("Verify Event Raised");
}
}
}
What is my mistake? please help me!
The Windows Service that I wrote, can raise custom events but cannot raise my dll events!

I know this thread is old, but I had this problem yesterday, and now I have finally found a solution, after many hours wasted.
The problem is that, the COM object must be created from an STA Thread, and then, for the events to be dispatched correctly, the same STA thread (exactly the same) must be pumping COM messages. This can be done calling Application.DoEvents() in a loop or Application.Run().
So here is my working code (it works, even as a non-interactive Windows Service in Vista+, I am using Windows 8.1)
Thread createComAndMessagePumpThread = new Thread(() =>
{
this.Device = new CZKEMClass(); //Here create COM object
Application.Run();
});
createComAndMessagePumpThread.SetApartmentState(ApartmentState.STA);
createComAndMessagePumpThread.Start();
After the device gets created you can register the events from any thread, and they get dispatched by the STA thread, that created the COM object.
In Windows Forms application, this worked without doing this, because the STA main thread run the form calling Application.Run(Form). Application.Run() then dispatch events like COM events and Windows GUI events, so there is no need to to the trick above.

Reviving this question as I've just been dealing with a related one. Apparently, the OP is using some COM STA objects which need an STA thread and a functional message pump loop to operate properly. The Windows Service execution model doesn't have that by default. Visit the linked answer for more details.

You cannot use events in Windows Service. Exists several causes why not but I would like to offer a solution just for zkemkeeper:
ZK released a zkemkeeper.dll as COM object for working with Windows Application. All device events will fired and not raised in your application when you run it as windows service. Try to add a reference System.Windows.Forms to the project and after successfully connect add row:
Application.Run();

Related

How to share static event from static class with multiple threads

I am writing a C# Script control (WinForms). This is based on Dockpanelsuite, Scintilla, CSharpScriptingLibrary and a lot of code I wrote/collected in the last year. I want to include a prebuild "Logging" and a "ProgressBar" feature. So far I got everything to work as expected, except the fact it wont work correctly when using Timers.
The code written in the editor is compiled at runtime, the "Programm"-type is loaded and the "Main"-method is invoked. If the created assembly includes a "Log"-type the "LogMessage"-event will be connected to a method in the hosting script control, which forwards the messages to the Output-window.
As you can see the "Hello World" message is written to the Output-window, but each time the timer fires the LogMessage event in the static Log-Class is null.
Any idea what I have to change to share the LogMessage event from the UI thread with the Timer thread?
Programm.cs
using System;
using System.Timers;
namespace ScriptControl
{
public class Programm
{
Timer timer = null;
int step = 0, steps = 10;
public void Main()
{
Log.Write("Hello World");
try
{
timer = new Timer();
timer.Interval = 1000;
timer.Elapsed += TimerHandler;
timer.Enabled = true;
}
catch(Exception ex)
{
Log.Write(ex);
}
}
private void TimerHandler(object sender, object args)
{
if(this.step == 1) timer.Enabled = false;
Log.Write(step++.ToString());
Progress.Set(this.step, steps);
}
}
}
Log.cs
public delegate void LogHandler(object message);
public static class Log
{
public static event LogHandler LogMessage;
public static void Write(object message)
{
if(LogMessage != null)
LogMessage(message);
else
System.Windows.Forms.MessageBox.Show("LogMessage is null");
}
}

C# WindowsForms Raise, consume Events

I have this code below:
using System;
using System.Windows.Forms;
namespace MyCode
{
public partial class Main_GUI : Form
{
//Attributes
private Processes process;
//Constructor
public Main_GUI()
{
InitializeComponent(); //a form with a button named BUTTON_Start, and a label named LABEL_log
p = new Processes();
}
//OnClickStart
private void BUTTON_Start_Click(object sender, EventArgs e)
{
try
{
LABEL_log.Text = "Started...";
p.start();
}
catch (Exception ex)
{
//do something with the exception
}
}
}//End of Class
public class Processes
{
//Constructor
public Processes() { }
//Methods
public void start()
{
try
{
//Do something
//...
//when finished send an event the Main_GUI Class (Form) in order to change the LABEL_log.Text value to "finished !"
}
catch (Exception e)
{
//do something with the exception
}
}
}
}
I ve tried a lot to create some events, I even use this example :
http://www.codeproject.com/Articles/11541/The-Simplest-C-Events-Example-Imaginable
but I cant understant how to create an event with my classes...
I such a fool I know but I really need your help !
Thanks the team !!
Regards.
FB
Define the event in the Process class:
public event EventHandler Finished;
Then in the same class define a method that raises the event "safely":
protected void RaiseFinished()
{
// Make sure the event has at least one subscriber.
if(Finished != null)
{
Finished(this, EventArgs.Empty);
}
}
You call the method where you want your event to be raised, in your case the start method:
public void Start()
{
try
{
//Do something
//...
RaiseFinished();
}
catch (Exception e)
{
//do something with the exception
}
}
Then in your Main_GUI class constructor subscribe to the event defining an handler:
//Constructor
public Main_GUI()
{
InitializeComponent(); //a form with a button named BUTTON_Start, and a label named LABEL_log
p = new Processes();
// Subscribe to the event.
p.Finished += p_Finished;
}
// This will get called when the Finished event is raised.
private void p_Finished(object sender, EventArgs e)
{
LABEL_log.Text = "Finished!";
}

C# callback on new thread

i am having trouble creating a callback on a newly started thread.
I have 2 classes, an API, and the Form.cs. I start a thread running a method in API, from Form.cs, i want to notify a method in Form.cs from inside the method in API.
I am familiar with delegation in Obj-C, but not in C#.
I only included the relevant code.
public partial class Main: Form
{
private Api Connect = new Api();
private void StartStopButton_Click(object sender, EventArgs e)
{
//new thread
Thread ThreadConnect = new Thread(Connect.startAttemptingWithUsername);
ThreadConnect.Start();
}
public void AttemptingWithPasswordMessage(string password)
{
// i want to notify this method from the API
}
}
class Api : UserAgent
{
public void startAttemptingWithUsername()
{
_shouldStop = false;
while (!_shouldStop)
{
Console.WriteLine(username);
// How would i notify AttemptingWithPasswordMessage from here?
System.Threading.Thread.Sleep(1000);
}
}
}
Provide an event to your other class, and fire that event whenever it is relevant based on the processing:
class Api : UserAgent
{
public event Action<string> SomeEvent;//TODO give better name
public void startAttemptingWithUsername()
{
_shouldStop = false;
while (!_shouldStop)
{
Console.WriteLine(username);
var handler = SomeEvent;
if (handler != null)
handler("asdf");
// How would i notify AttemptingWithPasswordMessage from here?
System.Threading.Thread.Sleep(1000);
}
}
}
Then add a handler for that event: (And marshal back to the UI thread)
private void StartStopButton_Click(object sender, EventArgs e)
{
//new thread
Thread ThreadConnect = new Thread(Connect.startAttemptingWithUsername);
ThreadConnect.Start();
Connect.SomeEvent += (data) => Invoke(
new Action(()=>AttemptingWithPasswordMessage(data)));
}

Logging information to a windowed textbox

I am working on converting a console application into a windowed format and as someone who knows little about it but has had experience with a similar application already in window format in the past I figured it wouldn't be too hard.
So I created a form and added a textbox to it just to get the logging information to start with.
This console app used to run in a single thread, I have since added a second thread to allow the form to run side by side with the console for testing. (it runs fine in a single thread strangely now too).
This is the code I am using to write text to the form except that I am not getting ANYTHING at all on the form.
static Form1 f = new Form1();
delegate void SetTextCallback(string s);
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (f.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
f.textBox1.Invoke(d, new object[] { text });
}
else
{
f.textBox1.AppendText(text);
}
}
I can confirm that there is text entering the "text" variable but it is not getting to the form.
Any help would be appreciated.
This is the full file:
using System;
using System.Windows.Forms;
using Chraft.Properties;
using System.IO;
using Chraft.Plugins.Events.Args;
using Chraft.Plugins.Events;
namespace Chraft
{
public class Logger
{
private StreamWriter WriteLog;
private Server Server;
internal Logger(Server server, string file)
{
Server = server;
try
{
WriteLog = new StreamWriter(file, true);
WriteLog.AutoFlush = true;
}
catch
{
WriteLog = null;
}
}
~Logger()
{
try
{
WriteLog.Close();
}
catch
{
}
}
public void Log(LogLevel level, string format, params object[] arguments)
{
Log(level, string.Format(format, arguments));
}
public void Log(LogLevel level, string message)
{
//Event
LoggerEventArgs e = new LoggerEventArgs(this, level, message);
Server.PluginManager.CallEvent(Event.LOGGER_LOG, e);
if (e.EventCanceled) return;
level = e.LogLevel;
message = e.LogMessage;
//End Event
LogToConsole(level, message);
LogToForm(level, message);
LogToFile(level, message);
}
private void LogToConsole(LogLevel level, string message)
{
if ((int)level >= Settings.Default.LogConsoleLevel)
{
Console.WriteLine(Settings.Default.LogConsoleFormat, DateTime.Now, level.ToString().ToUpper(), message);
}
}
static Form1 f = new Form1();
delegate void SetTextCallback(string s);
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (f.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
f.textBox1.Invoke(d, new object[] { text });
}
else
{
f.textBox1.AppendText(text);
}
}
private void LogToForm(LogLevel level, string message)
{
if ((int)level >= Settings.Default.LogConsoleLevel)
{
SetText(DateTime.Now + level.ToString().ToUpper() + message);
}
}
private void LogToFile(LogLevel level, string message)
{
if ((int)level >= Settings.Default.LogFileLevel && WriteLog != null)
WriteLog.WriteLine(Settings.Default.LogFileFormat, DateTime.Now, level.ToString().ToUpper(), message);
}
public void Log(Exception ex)
{
//Event
LoggerEventArgs e = new LoggerEventArgs(this, LogLevel.Debug, ex.ToString(), ex);
Server.PluginManager.CallEvent(Event.LOGGER_LOG, e);
if (e.EventCanceled) return;
//End Event
Log(LogLevel.Debug, ex.ToString());
}
public enum LogLevel : int
{
Trivial = -1,
Debug = 0,
Info = 1,
Warning = 2,
Caution = 3,
Notice = 4,
Error = 5,
Fatal = 6
}
}
}
The problem is that you are creating two Form objects. One that is created in your Program.cs file:
Application.Run(new Form1());
And the one you created in your logger class
Form f = new Form1();
The one passed to Application.Run is the one that the user is interacting with. It has become visible and responds to user interaction because of the Application.Run call.
The one you created on your logger class just sits there in memory. Its TextBox is happily adding the text you ask it to, but that one isn't visible anywhere.
There are many ways to handle this situation. You could gain access to the correct Form object through Application.OpenForms, but a more appropriate way to handle it would be to add an event on the logger that the form can subscribe to and it can handle updating the TextBox in response to the event.
Updated
class LoggerLogEventArgs : EventArgs
{
public LoggerLogEventArgs(string message)
{
this.message = message;
}
private string message;
public string Message { get { return message; } }
}
class Logger
{
public event EventHandler<LoggerLogEventArgs> Logged;
protected virtual void OnLogged(LoggerLogEventArgs e)
{
EventHandler<LoggerLogEventArgs> handler = Logged;
if (handler != null)
handler(this, e);
}
// I would change this method name to LogToEvent
private void LogToForm(LogLevel level, string message)
{
if ((int)level >= Settings.Default.LogConsoleLevel)
{
OnLogged(new LoggerLogEventArgs(message));
}
}
}
class Form1 : Form
{
// Subscribe to the logger only when we are ready to display text
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
GetLog().Logged += new EventHandler<LoggerLogEventArgs>(logger_Logged);
}
// Unsubscribe from the logger before we are no longer ready to display text
protected override void OnHandleDestroyed(EventArgs e)
{
GetLog().Logged -= new EventHandler<LoggerLogEventArgs>(logger_Logged);
base.OnHandleDestroyed(e);
}
private void logger_Logged(object sender, LoggerLogEventArgs e)
{
if (InvokeRequired)
BeginInvoke(new EventHandler<LoggerLogEventArgs>(logger_Logged), e);
else
textBox1.AppendText(e.Message);
}
}
hello i try this it works ( I make a console application and I add a windows form)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Permissions;
using System.Windows.Forms;
namespace ConsoleApplication6
{
class Program
{
delegate void SetTextCallback(string s);
static Form1 f;
static void Main(string[] args)
{
f = new Form1();
f.Show();
SetText("test");
Console.ReadLine();
}
private static void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (f.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
f.textBox1.Invoke(d, new object[] { text });
}
else
{
f.textBox1.AppendText(text);
}
}
}
}

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