Is there a way to subscribe to an event when a given service is shut down.
My program occasionally sends commands to a third party windows service, but I have no way of knowing if it is up until I send my request, which then causes an exception since no one is listening.
So I would like to subscribe to an event if possible, to let my user know, that he can't interact with the service at the moment, rather than giving him an error when he tries.
I could have a timer that runs now and then which then checks if the PID is still alive, but I'd rather be told instantly by the OS.
You could try to implement the list of started services and look for yours in there so that you dont have to send and wait for an error or similar results.
On Windows (7 and higher) you should get an almost complete list of running services by accessing the local or remote servicecontroller.
Here is more: https://learn.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicecontroller.getservices?view=netframework-4.8
https://answers.unity.com/questions/971269/subscribing-to-event-from-external-class.html
I ended up with this class, if it can be useful to someone else
public class ExternalProcessMonitor
{
Process process;
Action action;
string name;
public ExternalProcessMonitor(string _name, Action _action)
{
action = _action;
name = _name;
}
public bool StartMonitor()
{
if (process != null)
process.Exited -= Process_Exited;
var list = Process.GetProcesses().OrderBy(r => r.ProcessName);
process = Process.GetProcessesByName(name).FirstOrDefault();
if (process != null)
{
process.Exited += Process_Exited;
process.EnableRaisingEvents = true;
}
return process != null;
}
private void Process_Exited(object sender, EventArgs e)
{
action();
}
}
Related
I am trying to get a thread to run in the following unfinished code. The basics of which are as follows; when the console app starts, it should start a thread which will go off, navigate to a web page (which will eventually do some processing) before stopping and killing off the separate thread. In conjunction, the main application will just provide a menu to the user until the app is exited. Eventually the navigation thread will be put into a separate method so that it is periodically called every so often but this should not be relevant to this question, I don't think...
My understanding is that the separate thread should just run alongside the main console application and terminate when it has completed its task just like a console would if you don't prevent it exiting?????
What it actually looks like is that it is not starting in the first place as I get no response by way of the browser_DocumentCompleted event triggering (I know the IP address is alive and active, as I've checked!!)
Can anyone shed any light on why the separate thread is not running, or appears not to be?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace ConsoleThreadTest
{
class Program
{
public delegate void Callback(string Status);
static void Main(string[] args)
{
NavigateToIPAddress GEIPA = new NavigateToIPAddress(new Uri("http://192.168.1.254"), new Callback(ResultCallback));
Thread PerformThreadTask = new Thread(new ThreadStart(GEIPA.PerformThreadTask));
PerformThreadTask.SetApartmentState(ApartmentState.STA);
PerformThreadTask.Start();
Console.WriteLine("{0}","Press escape key to exit");
while (true)
{
if (Console.KeyAvailable)
{
ConsoleKeyInfo key = Console.ReadKey(true);
switch (key.Key)
{
case ConsoleKey.Escape:
//Kill off thread if it is still running.
if (PerformThreadTask.ThreadState == ThreadState.Running)
{
PerformThreadTask.Abort();
}
Environment.Exit(0);
break;
default:
break;
}
}
}
}
public static void ResultCallback(string Status)
{
Console.WriteLine("{0}\t{1}", DateTime.Now.ToString("h:mm:ss"), Status);
}
public class NavigateToIPAddress
{
private Uri WebAddress;
private bool WebBrowserNavigationComplete = false;
// Delegate used to execute the callback method when the task is complete.
private Callback callback;
// The constructor obtains the state information and the callback delegate.
public NavigateToIPAddress(Uri IPAddressToNavigateTo, Callback callbackDelegate)
{
WebAddress = IPAddressToNavigateTo;
callback = callbackDelegate;
}
// The thread procedure performs the task and then invokes the callback delegate with the status.
public void PerformThreadTask()
{
var br = new WebBrowser();
br.DocumentCompleted += browser_DocumentCompleted;
try
{
br.Navigate(WebAddress);
}
catch (Exception e)
{
Console.WriteLine("{0}\tSome error occurred: {1}", DateTime.Now.ToString("h:mm:ss"), e.Message);
}
Application.Run();
while (WebBrowserNavigationComplete == false)
{
}
if (callback != null)
callback("Summit occurred");
}
private void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var br = sender as WebBrowser;
if (br.Url == e.Url)
{
Console.WriteLine("{0}\tNavigated to {1}", DateTime.Now.ToString("h:mm:ss"), e.Url);
WebBrowserNavigationComplete = true;
}
}
}
}
}
Here I have a catch 22 situation. On one hand, if I leave the code as is above, anything after the application.run() method does not get executed which means the WebBrowserNavigationComplete flag will never change and the callback will never be returned.
If however I move application.run() after
if (callback != null)
callback("Summit occurred");
the code will never reach this point in order to call application.run() as it is stuck in the while loop waiting for the WebBrowserNavigationComplete flag which will never change as the message loop is never started!!
I cannot believe I am the first to do anything like this? What is the normal way to overcome this deadlock?
Thanks
WebBrowser is a winforms construct, and requires an application loop be set up to process messages for it. Since you have a console application and no message loop, it won't function properly.
You'll need to explicitly create a new application loop using Application.Run (which needs to be run from an STA thread) for it to work.
Basically, this question also summarizes my issue:
SystemEvents.SessionEnding not fired until a Process (opened before) gets closed
But there is no answer to it yet. I have a Console app that starts another process from within itself. The app also listens for SystemEvents.SessionSwitch. If I comment out the code that starts the additional process, the event handler for SessionSwitch is hit. However, if I uncomment the code that starts the additional process, the handler is not hit. I'm 100% confident that the event handler not being hit is due to starting a new process from within my app... I just don't know why.
I tagged this as a possible multithreading issue because that's what some of the comments made in the question posted above seemed to indicate. However, I'm not sure at all what could be causing it.
Here's some of the code.
[STAThread]
static void Main(string[] args)
{
SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
_myFoo = new _myFoo();
_processManager = new ProcessManager();
// If I comment out this code block, the SessionSwitch event handler is hit
// ------------------------------------------------------
if (args.Length == 0)
{
// creates a duplicate process to monitor the current (main) process
_processManager.StartObserverProcess();
}
else
{
// start monitoring the main process
_processManager.ObserveMainProcess(int.Parse(args[0]));
}
// ----------------------------------------------------
_myFoo.Start();
}
// this method does not get hit if we start the 'duplicate'
// monitoring process from within ProcessManager
private static void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
if (e.Reason == SessionSwitchReason.SessionLock)
{
// Do something when session locked
}
if (e.Reason == SessionSwitchReason.SessionUnlock)
{
// Do something when session unlocked
}
}
The ProcessManager basically starts another 'duplicate' process that watches to see if the current process exits (I know the term 'duplicate' here is probably not accurate). Here's an excerpt:
public class ProcessManager
{
// create a new process to monitor the current process
// passing in the current process id as args
public void StartObserverProcess()
{
_mainProcess = Process.GetCurrentProcess();
_mainProcessId = _mainProcess.Id;
_observerProcess = new Process
{
StartInfo =
{
FileName = _mainProcess.MainModule.FileName,
Arguments = _mainProcessId.ToString()
},
EnableRaisingEvents = true
};
_observerProcess.Exited += OnObserverProcessExit;
_observerProcess.Start();
}
private void OnObserverProcessExit(object sender, EventArgs e)
{
// do something on main process exit
}
}
I have some code that creates a Process instance and later starts it. There's some logic that need to check if the Process has been started. HasExited can be used to check if a started process has been exited, but I can not find a similar function for HasStarted. At first glance StartTime looked like a good option, but this function will throw if the process has exited. Also, the documentation says that StartTime only has meaning for started processes.
What is the "correct" approach for determining if a process has started (has been started, but might have quit)?
While the methods suggested by others will work, it is not the most efficient way to handle such things. If you keep a loop checking whether the Process has exited or not, you will waste a lot of system resources.
Your concern should be to just know when the process is exiting, and not sit looping for it to check whether it has exited. So, the correct way is to handle Events.
The code below explains how to do that using Events.
// Declare your process object with WithEvents, so that events can be handled.
private Process withEventsField_MyProcess;
Process MyProcess {
get { return withEventsField_MyProcess; }
set {
if (withEventsField_MyProcess != null) {
withEventsField_MyProcess.Exited -= MyProcess_Exited;
}
withEventsField_MyProcess = value;
if (withEventsField_MyProcess != null) {
withEventsField_MyProcess.Exited += MyProcess_Exited;
}
}
}
bool MyProcessIsRunning;
private void Button1_Click(System.Object sender, System.EventArgs e)
{
// start the process. this is an example.
MyProcess = Process.Start("Notepad.exe");
// enable raising events for the process.
MyProcess.EnableRaisingEvents = true;
// set the flag to know whether my process is running
MyProcessIsRunning = true;
}
private void MyProcess_Exited(object sender, System.EventArgs e)
{
// the process has just exited. what do you want to do?
MyProcessIsRunning = false;
MessageBox.Show("The process has exited!");
}
EDIT:
Knowing whether the process has started or not should be easy since are starting the process somewhere in the code. So you can set a flag there and set it to false when the process is exiting. I updated the code above to show how such a flag can be set easily.
Search your process in Process.GetProcesses();, the list returned by this method give all processes currently running on the machine.
You can use the Process.GetProcesses method (in the System.Diagnostics
namespace) to get a list of processes currently running on the PC.
Process.GetProcessesByName() can also be used to just get a list of
instances of a particular program.
// Get all instances of Notepad running on the local computer.
Process [] localByName = Process.GetProcessesByName("YourProcess");
You could check that there is atleast one thread in the process. This would indicate that the process is started and running.
Edit:
You could also check the process Id. It will throw an exception if the process hasn't started.
Edit 2:
Actually Threads will also throw an exception if the Id is not set:
bool ProcessIsRunning(Process p)
{
bool isRunning;
try {
isRunning = !p.HasExited && p.Threads.Count > 0;
}
catch(SystemException sEx)
{
isRunning = false;
}
catch(PlatformNotSupportedException pnsEx)
{
throw;
}
return isRunning;
}
I have wrote a simple client that use TcpClient in dotnet to communicate. In order to wait for data messages from server i use a Read() thread that use blocking Read() call on socket. When i receive something i have to generate various events. These event occur in the worker thread and thus you cannot update a UI from it directly. Invoke() can be use but for end developer its difficult as my SDK would be use by users who may not use UI at all or use Presentation Framework. Presentation framework have different way of handling this. Invoke() on our test app as Microstation Addin take a lot of time at the moment. Microstation is single threaded application and call invoke on its thread is not good as it is always busy doing drawing and other stuff message take too long to process.
I want my events to generate in same thread as UI so user donot have to go through the Dispatcher or Invoke.
Now i want to know how can i be notified by socket when data arrive? Is there a build in callback for that. I like winsock style receive event without use of separate read thread. I also do not want to use window timer to for polling for data.
I found IOControlCode.AsyncIO flag in IOControl() function which help says
Enable notification for when data is
waiting to be received. This value is
equal to the Winsock 2 FIOASYNC
constant.
I could not found any example on how to use it to get notification. If i am right in MFC/Winsock we have to create a window of size(0,0) which was just used for listening for the data receive event or other socket events. But i don't know how to do that in dotnet application.
Ok I got it up and running. What I was really looking to was how to seamlessly post events to an UI thread, in which my connection is created. After going through framework code I came up with following proof of concept. SynchronizationContext can be use to bind my component to the UI thread that created it. Then I can post events to that UI thread directly, without using Invoke.
In the following example I created a ThreadUISafeTimer which uses a seperate thread, just like my socket client that uses one for reading and raising events. In this case, context is used to post the event if not null, otherwise the event is raised using the worker thread.
[DefaultEvent("Tick")]
public class ThreadUISafeTimer : Component
{
private const int True = 1;
private const int False = 0;
private int enabled = False;
private SynchronizationContext context;
public event EventHandler Tick = delegate { };
[DefaultValue(false)]
public ushort Interval { get; set; }
public ThreadUISafeTimer() {
Interval = 100;
this.Events.AddHandler("Tick", Tick);
//If this class is created by a UI thread it will always post the Tick event to it.
//otherwise it would be null and Tick would occur in a seperate thread.
context = SynchronizationContext.Current;
}
protected override bool CanRaiseEvents {
get {
return true;
}
}
[DefaultValue(false)]
public bool Enabled {
get {
return enabled == True;
}
set {
int newval = value ? True : False;
if (enabled != newval) {
if (newval == False)
Thread.VolatileWrite(ref enabled, False);
else {
enabled = True;
ThreadPool.QueueUserWorkItem(
new WaitCallback(delegate(object o) {
try {
do {
try {
Thread.Sleep(Interval);
if (Thread.VolatileRead(ref enabled) == True) {
var callback = new SendOrPostCallback(delegate(object arg) {
try {
Tick(this, EventArgs.Empty);
}
catch (Exception exp) {
Application.OnThreadException(exp);
return;
}
});
//If context is null raise Tick event from current thread
if (context == null)
callback(null);
else
//otherwise post it to the UI thread that owns this timer.
context.Post(callback, null);
}
}
catch (ThreadInterruptedException) {
}
} while (Thread.VolatileRead(ref enabled) == True);
}
catch (ThreadAbortException) {
}
}), null);
}
}
}
}
Take a look at this question which is roughly the same and solved by using the Event Broker pattern.
Sending instructions to a thread which is waiting for TCP?
Basically you would have one object with an event that all your threads subscribe to. It will also have a method that can be called which will invoke the event. It maybe sounds complicated, but its fairly simple.
Example code is here http://msforge.net/blogs/paki/archive/2007/11/20/EventBroker-implementation-in-C_2300_-full-source-code.aspx.
I have a process, i can start, and hide working fine, but i want to read from the console program, when i runs, not after, i tried to run a timer, anbd read at the tick, but my program just crashes and when it not do, i get nothing at all.
startInfo= new ProcessStartInfo("cmd.exe");
startInfo.Arguments ="/C uus.exe "+ arg.ToString();
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
this.timer1.Enabled=true;
this.listBox1.Items.Clear();
p= Process.Start(startInfo);
Application.DoEvents();
void Timer1Tick(object sender, EventArgs e)
{
string str="";
str=p.StandardOutput.ReadLine();
if(str != null)
{
this.Text=str.ToString();
this.listBox1.Items.Add(str);
}
Application.DoEvents();
}
So what do i do to solve this?
Update:
I tried bender suggestion
now My program don't crash anymore, but also don't recvie any data
proc.StartInfo.UseShellExecute=false;
proc.StartInfo.CreateNoWindow=true;
proc.StartInfo.RedirectStandardOutput=true;
proc.StartInfo.RedirectStandardError=true;
proc.StartInfo.FileName="uus.exe";
proc.StartInfo.Arguments=arg;
proc.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(SortOutputHandler);
proc.Start();
proc.BeginOutputReadLine();
void SortOutputHandler(object o,System.Diagnostics.DataReceivedEventArgs e)
{
string str="";
string str2="";
str=e.Data.ToString();
if(str!=null && str!="")
{
this.listBox1.Items.Add(str.ToString());
this.Text=str.ToString();
}
str2=proc.StandardOutput.ReadLine();
if(str2!=null && str2!="")
{
this.lsw1.Items.Add(str2.ToString());
}
}
hmm?
Update:
I have changed the handler, because i have being tell, it can't do it, that it wil be cross thread operation, usualyy i wille have get an error if it was.
private delegate void TextAdderDelegate(string str);
void TextAdder(string str)
{
if(this.lsw1.InvokeRequired==true)
{
Invoke(new TextAdderDelegate(TextAdder),new object[] {str});
}
else
{
this.lsw1.Items.Add(str);
}
}
void SortOutputHandler(object o,System.Diagnostics.DataReceivedEventArgs e)
{
string str="";
if(e!=null)
{
if(e.Data!=null)
{
str=e.Data.ToString();
}
}
TextAdder(str);
}
The problem is that you're running on one thread and trying to write using another. When you created your background thread using the Timer's tick event, it can't have frontend user input.
Perhaps if you explained the big picture of what you're trying to accomplish, we can better help you.
In the meantime, you might want to create threadsafe writes. This article will help you to understand the problem and solution to writing to form controls on different threads.
You may create the Process instance explicitly (e.g. new Process)and use the OutputDataReceived event, the method BeginOutputReadLine() and, when finished CancelOutputRead() for that.
The event OutputDataReceived will be repeatedly called asynchronously from a different thread as soon output data is available.
I assume you get an 'thread cross exception', this may be caused because you're updating your form controls on an other thread then the UI thread.