I need to be able to shutdown an console application gracefully on demand.
Using this post i was able to control exit when CTRL events are received.
Yet, i can't stop the application on demand, using taskkill.
If i try the command:
taskkill /im myapp
it responds:
ERROR: The process "MyApp.exe" with PID 23296 could not be
terminated. Reason: This process can only be terminated forcefully
(with /F option).
Based on the many contributions added to this post and also detecting-console-application-exit-in-c i build the following helper, to gracefully stop a console application on taskkill signal but also to stop with any other close controls signals:
public class StopController : Form, IMessageFilter
{
//logger
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
static private CancellationTokenSource cancellationTokenSource;
public StopController(CancellationTokenSource cancellationTokenSource)
{
StopController.cancellationTokenSource = cancellationTokenSource;
System.Windows.Forms.Application.AddMessageFilter(this);
SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.WindowState = FormWindowState.Minimized;
this.ShowInTaskbar = false;
}
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == 16)
{
log.Warn("Receiveing WF_Close event. Cancel was fired.");
cancellationTokenSource.Cancel();
}
return true;
}
public static void Activate(CancellationTokenSource cancellationTokenSource)
{
Task.Run(() => Application.Run(new StopController(cancellationTokenSource)));
}
#region unmanaged
//must be static.
private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
{
// Put your own handler here
switch (ctrlType)
{
case CtrlTypes.CTRL_C_EVENT:
log.Warn("CTRL+C received!. Cancel was fired.");
cancellationTokenSource.Cancel();
break;
case CtrlTypes.CTRL_BREAK_EVENT:
log.Warn("CTRL+BREAK received!. Cancel was fired.");
cancellationTokenSource.Cancel();
break;
case CtrlTypes.CTRL_CLOSE_EVENT:
log.Warn("Program being closed!. Cancel was fired.");
cancellationTokenSource.Cancel();
break;
case CtrlTypes.CTRL_LOGOFF_EVENT:
case CtrlTypes.CTRL_SHUTDOWN_EVENT:
log.Warn("User is logging off!. Cancel was fired.");
cancellationTokenSource.Cancel();
break;
default:
log.Warn($"unknow type {ctrlType}");
break;
}
return true;
}
// Declare the SetConsoleCtrlHandler function
// as external and receiving a delegate.
[DllImport("Kernel32")]
public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);
// A delegate type to be used as the handler routine
// for SetConsoleCtrlHandler.
public delegate bool HandlerRoutine(CtrlTypes CtrlType);
// An enumerated type for the control messages
// sent to the handler routine.
public enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
#endregion
}
Usage:
StopController.Activate(cancellationTokenSource);
There is no need to change to windows application.
Related
i want to just stop my backgroundworker when i press a button :
Code looking like :
Button :
private void button6_Click(object sender, EventArgs e)
{
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
if (isOn == true)
{
isOn = false;
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
this.button6.ForeColor = System.Drawing.Color.Lime;
}
}
else
{
isOn = true;
this.button6.ForeColor = System.Drawing.Color.Red;
backgroundWorker1.CancelAsync();
//////backgroundWorker1.Dispose();
}
And my Backgroundworker_DoWork look like :
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (backgroundWorker1.CancellationPending && backgroundWorker1.IsBusy)
{
e.Cancel = true;
return;
}
while (true)
{
if (backgroundWorker1.CancellationPending && backgroundWorker1.IsBusy)
{
e.Cancel = true;
break;
}
backgroundWorker1.Dispose();
click_na_default(hwnd1);
click_F8(hwnd1);
click_na_YELLS(hwnd1);
click_ENTER(hwnd1);
Thread.Sleep(100);
click_na_trade(hwnd1);
Thread.Sleep(100);
click_F8(hwnd1);
click_ENTER(hwnd1);
Thread.Sleep(100);
click_na_default(hwnd1);
Thread.Sleep(4000);
}
if (((BackgroundWorker)sender).CancellationPending)
{
e.Cancel = true;
//set this code at the end of file processing
return;
}
}
And the problem is : I can't .CancelAsync(); just immediately after button press again . My code just DoWork untill just Thread.Sleep(4000); is over.
When i press my button to stop work this gonna stop just after end while loop.
I know i can add
if (backgroundWorker1.CancellationPending && backgroundWorker1.IsBusy)
{
e.Cancel = true;
return;
}
After everyline in my Backgroundworker_DoWork but it's so stupid and when i get Thread.Sleep(10000); it gonna takes 10 sec...
Is any way to just kill instantly my background worker?
Thanks for help!
I think that standard BackgroundWorker is not suitable for your case and you should do something custom that better support combination of sleep and cancellation. Following code is an idea of what you might want to do:
CancellableBackgroundWorker.cs
This is a class similar to standard BackgroundWorker but providing some callbacks for your goal (see ICancellationProvider and FinishedEvent).
public delegate void CancellableBackgroundJob(ICancellationProvider cancellation);
public interface ICancellationProvider
{
bool CheckForCancel();
void CheckForCancelAndBreak();
void SleepWithCancel(int millis);
}
public class CancellableBackgroundWorker : Component, ICancellationProvider
{
private readonly ManualResetEvent _canceledEvent = new ManualResetEvent(false);
private readonly CancellableBackgroundJob _backgroundJob;
private volatile Thread _thread;
private volatile bool _disposed;
public EventHandler FinishedEvent;
public CancellableBackgroundWorker(CancellableBackgroundJob backgroundJob)
{
_backgroundJob = backgroundJob;
}
protected override void Dispose(bool disposing)
{
Cancel();
_disposed = true;
}
private void AssertNotDisposed()
{
if (_disposed)
throw new InvalidOperationException("Worker is already disposed");
}
public bool IsBusy
{
get { return (_thread != null); }
}
public void Start()
{
AssertNotDisposed();
if (_thread != null)
throw new InvalidOperationException("Worker is already started");
_thread = new Thread(DoWorkWrapper);
_thread.Start();
}
public void Cancel()
{
AssertNotDisposed();
_canceledEvent.Set();
}
private void DoWorkWrapper()
{
_canceledEvent.Reset();
try
{
_backgroundJob(this);
Debug.WriteLine("Worker thread completed successfully");
}
catch (ThreadAbortException ex)
{
Debug.WriteLine("Worker thread was aborted");
Thread.ResetAbort();
}
finally
{
_canceledEvent.Reset();
_thread = null;
EventHandler finished = FinishedEvent;
if (finished != null)
finished(this, EventArgs.Empty);
}
}
#region ICancellationProvider
// use explicit implementation of the interface to separate interfaces
// I'm too lazy to create additional class
bool ICancellationProvider.CheckForCancel()
{
return _canceledEvent.WaitOne(0);
}
void ICancellationProvider.CheckForCancelAndBreak()
{
if (((ICancellationProvider)this).CheckForCancel())
{
Debug.WriteLine("Cancel event is set, aborting the worker thread");
_thread.Abort();
}
}
void ICancellationProvider.SleepWithCancel(int millis)
{
if (_canceledEvent.WaitOne(millis))
{
Debug.WriteLine("Sleep aborted by cancel event, aborting the worker thread");
_thread.Abort();
}
}
#endregion
}
The main trick is to use ManualResetEvent.WaitOne instead of Thread.Sleep for sleeping. With such approach working thread might be safely woken up (for cancellation) from a different (UI) thread. Another trick is to use ThreadAbortException via Thread.Abort to enforce quick end of the background thread execution (and don't forget about Thread.ResetAbort at the end of stack unwinding).
You may use this class as following:
public partial class Form1 : Form
{
private readonly CancellableBackgroundWorker _backgroundWorker;
public Form1()
{
InitializeComponent();
_backgroundWorker = new CancellableBackgroundWorker(DoBackgroundJob);
_backgroundWorker.FinishedEvent += (s, e) => UpdateButton();
// ensure this.components is created either by InitializeComponent or by us explicitly
// so we can add _backgroundWorker to it for disposal
if (this.components == null)
this.components = new System.ComponentModel.Container();
components.Add(_backgroundWorker);
}
private void UpdateButton()
{
// Ensure we interact with UI on the main thread
if (InvokeRequired)
{
Invoke((Action)UpdateButton);
return;
}
button1.Text = _backgroundWorker.IsBusy ? "Cancel" : "Start";
}
private void button1_Click(object sender, EventArgs e)
{
if (_backgroundWorker.IsBusy)
{
_backgroundWorker.Cancel();
}
else
{
_backgroundWorker.Start();
}
UpdateButton();
}
private void DoBackgroundJob(ICancellationProvider cancellation)
{
Debug.WriteLine("Do something");
// if canceled, stop immediately
cancellation.CheckForCancelAndBreak();
Debug.WriteLine("Do something more");
if (cancellation.CheckForCancel())
{
// you noticed cancellation but still need to finish something
Debug.WriteLine("Do some necessary clean up");
return;
}
// Sleep but cancel will stop and break
cancellation.SleepWithCancel(10000);
Debug.WriteLine("Last bit of work");
}
}
Consider the following snippet. The thread spawned by the main windows service thread will crash because it tries to open a null path. Then the crashing of the windows service will follow.
namespace ThreadCrashService {
class Program {
public const string ServiceName = "ThreadCrashServiceTest";
private static Timer _timer = null;
private static int _timerInterval = 60000;
static void Main(string[] args) {
if (!Environment.UserInteractive) {
// running as service
using (var service = new Service1()) System.ServiceProcess.ServiceBase.Run(service);
} else {
string parameter = string.Concat(args);
switch (parameter) {
case "--install":
if (IsServiceInstalled()) {
UninstallService();
}
InstallService();
break;
case "--uninstall":
if (IsServiceInstalled()) {
UninstallService();
}
break;
default:
Program program = new Program();
program.Start();
return;
}
}
}
private static void InstallService() {
ManagedInstallerClass.InstallHelper(new[] { Assembly.GetExecutingAssembly().Location });
}
private static bool IsServiceInstalled() {
return System.ServiceProcess.ServiceController.GetServices().Any(s => s.ServiceName == ServiceName);
}
private static void UninstallService() {
ManagedInstallerClass.InstallHelper(new[] { "/u", Assembly.GetExecutingAssembly().Location });
}
public void Start() {
try {
Thread thread = new Thread(() => ThreadMethodThatWillCrash());
thread.Start();
} catch {
// do nothing
}
}
public void ThreadMethodThatWillCrash() {
// ArgumentNullException
File.Open(null, FileMode.Open);
}
}
}
I know in windows form application, we can use
System.Windows.Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;
and
System.Windows.Forms.Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
to catch the global exceptions not handled by UI threads. But for a console application, we can only use
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);
to log the exception. But this is not able to prevent the thread crashing the windows service. What else can I do to prevent the thread crashing the windows service? I can't change the way how the thread is created because it's a in a third-party library.
You can use the main thread to spool up Tasks from TPL in order to preserve the state of the process.
https://stackoverflow.com/a/27384788/376550
https://msdn.microsoft.com/en-us/library/dd537609(v=vs.110).aspx
I have a console application which uses a BackgroundWorker to run an infinite loop.
I'm trying to catch the close event and do some stuff.
I used some googled solutions, and came up with the following:
class Program
{
private static bool keepAlive = false;
private static BackgroundWorker bgWorker = new BackgroundWorker();
private static void runThread ()
{
while (keepAlive) {}
}
private bool ConsoleClosingCheck(CtrlTypes ctrlType)
{
switch (ctrlType)
{
case CtrlTypes.CTRL_CLOSE_EVENT:
keepAlive = false;
break;
}
return true;
}
[DllImport("Kernel32")]
public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);
public delegate bool HandlerRoutine(CtrlTypes CtrlType);
public enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
public static void Start ()
{
keepAlive = true;
bgWorker.DoWork += (sender, e) => runThread();
bgWorker.RunWorkerAsync();
}
public static void Main(string[] args)
{
Program p = new Program();
SetConsoleCtrlHandler(new HandlerRoutine(p.ConsoleClosingCheck), true);
p.Start();
}
}
When I debug it and close the console using the "X" (normal close) it doesn't stop at the case, instead the application crashes.
Any ideas?
The example code doesn't compile for me:
Member 'Program.Start()' cannot be accessed with an instance reference;
I suspect that the problem is just because the 'p' object that you create does not have a managed root (no managed class keeps a reference to it), there is only an unmanaged callback that refers to it.
This means that p can get garbage collected, causing a failure when calling the callback.
I tried converting the example Program class to be all static. It didn't wait in the Start() method so I added a wait on BackgroundWorker.IsBusy at the end of the start method.
I also added such a wait in the callback; otherwise, its just a race to see if the rest of the doWork method gets to execute:
case CtrlTypes.CTRL_CLOSE_EVENT:
{
keepAlive = false;
while (bgWorker.IsBusy)
{
Thread.Sleep(100);
}
break;
}
I have a main thread of a Console Application that runs few external processes this way
private static MyExternalProcess p1;
private static MyExternalProcess p2;
private static MyExternalProcess p3;
public void Main() {
p1 = new MyExternalProcess();
p2 = new MyExternalProcess();
p3 = new MyExternalProcess();
p1.startProcess();
p2.startProcess();
p3.startProcess();
}
public static void killEveryoneOnExit() {
p1.kill();
p2.kill();
p3.kill();
}
class MyExternalProcess {
private Process p;
...
public void startProces() {
// do some stuff
PlayerProcess = new Process();
....
PlayerProcess.Start();
// do some stuff
}
public void kill() {
// do some stuff
p.Kill();
}
}
What I need to do is: when the Main thread is interrupted (exit button or ctrl+c), the other processes should be killed.
How do I trigger my method killEveryoneOnExit on CTRL+C or Exit (X) button?
Based on your question there are two events you need to catch.
First there is the console close event which is explained here: "On Exit" for a Console Application
Second you want to catch control c which is explained here: How do I trap ctrl-c in a C# console app
If you put these two together with your example you get something like this:
static ConsoleEventDelegate handler;
private delegate bool ConsoleEventDelegate(int eventType);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
private static MyExternalProcess p1;
public static void Main()
{
Console.CancelKeyPress += delegate
{
killEveryoneOnExit();
};
handler = new ConsoleEventDelegate(ConsoleEventCallback);
SetConsoleCtrlHandler(handler, true);
p1 = new MyExternalProcess();
p1.startProcess();
}
public static void killEveryoneOnExit()
{
p1.kill();
}
static bool ConsoleEventCallback(int eventType)
{
if (eventType == 2)
{
killEveryoneOnExit();
}
return false;
}
For a working ctrl c (fun intended) paste example: http://pastebin.com/6VV4JKPY
I'm learning C# and I need help, please.
My question: how to know whether a USB-disk has been mounted/unmounted?
I found an answer for WndProd
const int WM_DEVICECHANGE = 0x0219;
const int DBT_DEVICEARRIVAL = 0x8000;
const int DBT_DEVICEREMOVECOMPLETE = 0x8004;
[StructLayout(LayoutKind.Sequential)]
public struct DEV_BROADCAST_HDR
{
public int dbch_size;
public int dbch_devicetype;
public int dbch_reserved;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_DEVICECHANGE)
{
int EventCode = m.WParam.ToInt32();
Log(string.Format("WM_DEVICECHANGE. Код={0}", EventCode));
switch (EventCode)
{
case DBT_DEVICEARRIVAL:
{
Log("Добавление устройства");
break;
}
case DBT_DEVICEREMOVECOMPLETE:
{
Log("Удаление устройства");
break;
}
}
}
base.WndProc (ref m);
}
and this version
public class WMIReceiveEvent
{
public WMIReceiveEvent()
{
try
{
WqlEventQuery query = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent");
ManagementEventWatcher watcher = new ManagementEventWatcher(query);
Console.WriteLine("Waiting for an event...");
watcher.EventArrived += new EventArrivedEventHandler(HandleEvent);
// Start listening for events
watcher.Start();
// Do something while waiting for events
System.Threading.Thread.Sleep(20000);
// Stop listening for events
//watcher.Stop();
//return;
}
catch (ManagementException err)
{
}
}
private void HandleEvent(object sender, EventArrivedEventArgs e)
{
Console.WriteLine("Win32_DeviceChangeEvent event occurred. "+ e.NewEvent.ClassPath.ClassName.ToString());
Console.WriteLine("2_Win32_DeviceChangeEvent event occurred. " + e.NewEvent.Properties.ToString());
Console.ReadLine();
}
}
but I would like version for DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE without WinForm. Because for WndProc need System.Windows.Form and Class must be the successor ":Form"
And for WMIReceiveEvent not the best solution for my task.
You can use NativeWindow instead of Form and still use WndProc(ref Message msg).
It's practically an invisible form, see example:
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
public class MyMessageHandler : NativeWindow
{
private event EventHandler<MyEventArgs> messageReceived;
public event EventHandler<MyEventArgs> MessageReceived
{
add
{
if (messageReceived == null || !messageReceived.GetInvocationList().Contains(value))
messageReceived += value;
}
remove
{
messageReceived -= value;
}
}
public MyMessageHandler()
{
var cp = new CreateParams();
CreateHandle(cp);
}
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message msg)
{
var handler = messageReceived;
if (handler != null)
handler(this, new MyEventArgs(msg));
base.WndProc(ref msg);
}
}
The problem with writing a Console application for this is that it doesn't have a message loop (at least, not by default; you would have to write your own).
The simpler solution is to create a Windows Forms project, but just don't show any forms. You would essentially be creating a "background" application that doesn't display any user interface. WinForms applications provide a message pump for you automatically, allowing you to catch the messages you're interested in.
Depending on the requirements of your application you might as well do polling. You build a loop checking all possible drive letters like
System.IO.Directory.Exists(driveLetter);
and compare it to an existing drive letter array or struct or whatever. Create an event as soon as they are different.
That would be the easy way though not as fantastic regarding performance. But as I said, it depends on your requirements.