console catch close event thread - c#

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

Related

How to avoid force kill to stop a console application?

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.

Referencing variable in class from within the same class

I am currently for the first time in need to rewrite my app into library. I have succes so far, but I need to somehow make auto repeated process, which could be started by simply camShield.start().
But I am not able to reference the enabled from anywhere. The idea here is that I would start thread with timer, which would be checking on the enabled variable. But to do that, I need another function, like stop(), which would set the enabled variable to false.
Is there any better way to implement such a function?
---EDIT----
I need to write functions CamShield.start() and CamShield.stop(), which would be able to access the CamShield.enabled variable.
Here is part of code, where I am trying to solve it (It is Class Library)
using SharpAdbClient;
using System;
using System.Diagnostics;
using System.Threading;
namespace BaReader
{
public class Private
{
public class CamShield
{
internal bool enabled = true;
public static void start()
{
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
Timer camShieldTimer = new Timer(tap, null, 0, 20000);
}).Start();
}
}
internal static void tap(Object o)
{
AdbClient.Instance.ExecuteRemoteCommand("input tap 600 900", Device.lookup(), null);
Debug.WriteLine("Tapped");
}
}
}
Thanks in advance for any ideas.
You have declared methods static and your variable enabled as non static so you were not able to access it,
public class CamShield
{
internal bool enabled = false;
public void start()
{
if(!enabled)
{
enabled = true;
//your code to start
}
}
public void stop()
{
if(enabled)
{
//your code to stop
enabled = false;
}
}
}
I am sure you can instantiate the CamShield class and access start and stop methods from outside.
In order to stop the thread, you need to kill it using Abort. The attached question will provide you with enough tools and knowledge to get there.
second, you cannot access the enabled because of your scope. Take another look at the code:
public class Private
{
public class CamShield
{
internal bool enabled = true;
public static void start()
{
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
Timer camShieldTimer = new Timer(tap, null, 0, 20000);
}).Start();
}
}
internal static void tap(Object o)
{
AdbClient.Instance.ExecuteRemoteCommand("input tap 600 900", Device.lookup(), null);
Debug.WriteLine("Tapped");
}
}
Your internal bool enabled is in the scope of CamShield class and won't be available to your tap method unless you initialize CamShield Class.
in order to use your internal bool enabled you need to declare it in your private class and then use it in tap:
public class Private
{
internal bool enabled = true;
public class CamShield
{
enabled = false;
public static void start()
{
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
Timer camShieldTimer = new Timer(tap, null, 0, 20000);
}).Start();
}
}
internal static void tap(Object o)
{
enabled = true;
AdbClient.Instance.ExecuteRemoteCommand("input tap 600 900", Device.lookup(), null);
Debug.WriteLine("Tapped");
}
}

C# wait timeout before calling method and reset timer on consecutive calls

I have a event in my code that can possibly get fired multiple times a second at some moment.
However I would like to implement a way to make that method wait 500ms before really firing, if the method gets called again before those 500ms are over, reset the timer and wait for 500ms again.
Coming from javascript I know this is possible with setTimeout or setInterval. However I'm having trouble figuring out how I could implement such a thing in C#.
You could use a System.Timers.Timer wrapped in a class to get the behaviour you need:
public class DelayedMethodCaller
{
int _delay;
Timer _timer = new Timer();
public DelayedMethodCaller(int delay)
{
_delay = delay;
}
public void CallMethod(Action action)
{
if (!_timer.Enabled)
{
_timer = new Timer(_delay)
{
AutoReset = false
};
_timer.Elapsed += (object sender, ElapsedEventArgs e) =>
{
action();
};
_timer.Start();
}
else
{
_timer.Stop();
_timer.Start();
}
}
}
This can then be used in the following manner:
public class Program
{
static void HelloWorld(int i)
{
Console.WriteLine("Hello World! " + i);
}
public static void Main(string[] args)
{
DelayedMethodCaller methodCaller = new DelayedMethodCaller(500);
methodCaller.CallMethod(() => HelloWorld(123));
methodCaller.CallMethod(() => HelloWorld(123));
while (true)
;
}
}
If you run the example, you will note that "Hello World! 123" is only displayed once - the second call simply resets the timer.
If you need to reset the timer when the method is called again, consider looking at the ManualResetEvent class:
https://msdn.microsoft.com/en-us/library/system.threading.manualresetevent(v=vs.110).aspx
You can use this to notify one or more waiting threads that an event has occurred.
You can use Thread.Sleep() with locking
private object locking = new object();
lock (locking )
{
Thread.Sleep(500);
//Your code to run here
}
https://msdn.microsoft.com/en-us/library/system.threading.thread.sleep(v=vs.110).aspx
Just writen super simple class with System.Threading.Thread; With a little different approach Usage.
var delayedCaller = new DelayedTimeout(() => HelloWorld(123), 500, false);
delayedCaller.ResetTimer();
delayedCaller.ResetTimer();
Currently, you can do it very simple with the following class
public class DelayedTimeout
{
readonly Timer _timer;
readonly int _timeoutMs;
public DelayedTimeout(TimerCallback callback, int timeoutMs, bool startNow)
{
_timeoutMs = timeoutMs;
// Should we start now
var currentTimeoutMs = startNow ? _timeoutMs : Timeout.Infinite;
_timer = new Timer(callback, null, currentTimeoutMs, Timeout.Infinite);
}
// Constructor overloading
public DelayedTimeout(Action callback, int timeoutMs, bool startNow) :
this(delegate (object? obj) { callback.Invoke(); }, timeoutMs, startNow)
{}
public void ResetTimer()
{
_timer.Change(Timeout.Infinite, Timeout.Infinite); // Stop the timer
_timer.Change(_timeoutMs, Timeout.Infinite); // Stop the timer
}
}

Catching ctrl+c event in console application (multi-threaded)

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

Windows Forms - How to kick of a seperate thread and hold current thread

I have a windows app and an an dll(windows form) that im trying to open (ActivationCheck), im trying to pause the current thread open a new thread (ActivationCheck) wait for that form event to return true then continue the main thread.
Could someone explain\show me what im doing wrong - thanks.
static class Program
{
private static SplashScreen splash;
private static bool quitApp;
private static bool activationFinished;
[STAThread]
static void Main()
{
Thread thread = new Thread(ActivationCheck);
thread.Start();
do
{
Thread.Sleep(1000);
} while (activationFinished);
if (!quitApp)
{
Thread.Sleep(0);
// WizardRun();
Application.Run(new Main(ref splash));
}
}
}
private static void ActivationCheck()
{
splash.SetStatus = "Checking License...";
Guid productId = new Guid(Properties.Settings.Default.ProductId);
Guid versionId = new Guid(Properties.Settings.Default.VersionId);
Client.UI.EntryPoint entryPoint = new EntryPoint();
activationFinished = false;
Client.BLL.ProductActivation.GenerateTrialLicense(productId1, versionId2, EditionId3);
entryPoint.IniatePlugin(productId, versionId);
entryPoint.PluginFinished += new EventHandlers.PluginFinishEventHandler(entryPoint_PluginFinished);
}
static void entryPoint_PluginFinished(bool forceQuit)
{
quitApp = forceQuit;
activationFinished = true;
}
You could just do thread.Join()? To be honest, though, I'm not quite sure what the point is of starting a second thread and pausing the first; just do the work on the original thread?
The problem with the code is possibly that activationFinished is being held in a register; try marking it as volatile, or alternatively use a lock at both places that access this variable. Even better would be to use a ManualResetEvent or similar, and open it from the activation code.
using System;
using System.Threading;
static class Program
{
static void Main()
{
new Thread(DoActivation).Start();
Console.WriteLine("Main: waiting for activation");
activation.WaitOne();
Console.WriteLine("Main: and off we go...");
}
static void DoActivation(object state)
{
Console.WriteLine("DoActivation: activating...");
Thread.Sleep(2000); // pretend this takes a while
Console.WriteLine("DoActivation: activated");
activation.Set();
// any other stuff on this thread...
}
static ManualResetEvent activation = new ManualResetEvent(false);
}

Categories

Resources