Override Console Close - c#

I know the GUI has private void Form1_Closing(object sender, System.ComponentModel.EventArgs e)
{
//do stuff
}
But how can i do the same thing in a console application?
C#/.NET3.5

Here's how:
// 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
}
private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
{
// Put your own handler here
return true;
}
...
SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);

Related

Pass delegate as argument to a method from one library to another library and invoke it from receiving library

I wanted to pass a delegate from one library to another library.
For ex:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void CallbackDelegate();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int RegisterCallback(IntPtr libPtr, CallbackDelegate func);
protected RegisterCallback registerCallBackDelegate;
public class Class
{
public bool Register()
{
CallbackDelegate callback = new CallbackDelegate(OnEvent);
int result = registerCallBackDelegate(context, callback);
}
public void OnEvent()
{
Console.WriteLine("event received");
}
In the above example callback delegate is sent as an arg to another library which is also written in c# like below
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void CallbackDelegate();
private static IntPtr callBackPtr;
[UnmanagedCallersOnly(EntryPoint = "RegisterCallback", CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })]
public static int RegisterCallback(IntPtr context, IntPtr callback)
{
callBackPtr = callback;
return 0;
}
private static void OnExternalEvent(object sender, FileSystemEventArgs e)
{
var callbackDelegate = (CallbackDelegate)Marshal.GetDelegateForFunctionPointer(callBackPtr, typeof(CallbackDelegate));
callbackDelegate();
}
Now the issue is on the above method. Getting the bellow error.
'[A]CallbackDelegate cannot be cast to [B]CallbackDelegate. Type A originates from Lib1. Type B originates from Lib2.
Any idea how to convert this IntPtr to delegate and call it without this cast issue?

How can I avoid crashing when creating a precision timer with WinMM.dll?

I'm trying to create a precision timer. I found an example created with WinMM.dll. The sample works really fine. But it crashes with the first garbage collector.
How can I prevent the garbage collector from blocking the timer?
public class WinMMWrapper : IDisposable
{
[DllImport("WinMM.dll", SetLastError = true)]
public static extern uint timeSetEvent(int msDelay, int msResolution,
TimerEventHandler handler, ref int userCtx, int eventType);
[DllImport("Winmm.dll", CharSet = CharSet.Auto)] // <=== ADD THIS
static extern uint timeKillEvent(uint uTimerID); // <=== ADD THIS
public delegate void TimerEventHandler(uint id, uint msg, ref int userCtx,
int rsv1, int rsv2);
public enum TimerEventType
{
OneTime = 0,
Repeating = 1,
}
private readonly Action _elapsedAction;
private readonly int _elapsedMs;
private readonly int _resolutionMs;
private readonly TimerEventType _timerEventType;
private uint _timerId; // <=== ADD THIS
private bool _disposed; // <=== ADD THIS
public WinMMWrapper(int elapsedMs, int resolutionMs, TimerEventType timerEventType, Action elapsedAction)
{
_elapsedMs = elapsedMs;
_resolutionMs = resolutionMs;
_timerEventType = timerEventType;
_elapsedAction = elapsedAction;
}
public bool StartElapsedTimer() // <=== RETURN bool
{
StopTimer(); // Stop any started timer
int myData = 1;
// === SET _timerId
_timerId = timeSetEvent(_elapsedMs, _resolutionMs / 10, new TimerEventHandler(TickHandler), ref myData, (int)_timerEventType);
return _timerId != 0;
}
public void StopTimer() // <=== ADD THIS
{
if (_timerId != 0)
{
timeKillEvent(_timerId);
_timerId = 0;
}
}
private void TickHandler(uint id, uint msg, ref int userctx, int rsv1, int rsv2)
{
_elapsedAction();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!_disposed && disposing)
StopTimer();
_disposed = true;
}
~WinMMWrapper()
{
Dispose(false);
}
}
My Static Class
public static class Global
{
public static WinMMWrapper timer;
}
Create WinMMWrapper
private void TimerStart_Click(object sender, RoutedEventArgs e)
{
Global.timer = new WinMMWrapper(1, 1, WinMMWrapper.TimerEventType.Repeating, Tick);
Global.timer.StartElapsedTimer();
}
Tick Function
private static void Tick()
{
Console.WriteLine("Time : " + DateTime.Now.ToString("hh:mm:ss:ffff"));
}
Error Message
Managed Debugging Assistant 'CallbackOnCollectedDelegate' : A callback was made on the garbage-collected delegate of type 'CanBusRandomDataGenerator!CanBusRandomDataGenerator.WinMMWrapper+TimerEventHandler::Invoke'. This can cause app crashes, corruption, and data loss. When delegating to unmanaged code, it must be kept alive by the managed application until it is guaranteed that the delegates will never be called.'
The code is now exactly the same. It works for about 2 3 seconds, then it crashes to the following error. Error occurs within WinMMWrapper function without falling into Dispose.
You must keep the timer variable alive as long as you are using the timer. If it is a local variable, it will be reclaimed by the GC when you leave the method. Do so by converting this local variable to a class field (possibly static). In a Console application you can still use a local variable, but you must add a Console.ReadKey(); to prevent the application to exit prematurely.
Also, stop the timer before this variable becomes eligible for garbage collection. To do so, let WinMMWrapper implement IDisposable.
Make sure that the object where the callback Action lives stays alive and is not disposed! Probably this is the object where you call new WinMMWrapper(..., theAction).
public class WinMMWrapper : IDisposable
{
[DllImport("WinMM.dll", SetLastError = true)]
public static extern uint timeSetEvent(int msDelay, int msResolution,
TimerEventHandler handler, ref int userCtx, int eventType);
[DllImport("Winmm.dll", CharSet = CharSet.Auto)] // <=== ADD THIS
static extern uint timeKillEvent(uint uTimerID); // <=== ADD THIS
public delegate void TimerEventHandler(uint id, uint msg, ref int userCtx,
int rsv1, int rsv2);
public enum TimerEventType
{
OneTime = 0,
Repeating = 1,
}
private readonly Action _elapsedAction;
private readonly int _elapsedMs;
private readonly int _resolutionMs;
private readonly TimerEventType _timerEventType;
private iuint _timerId; // <=== ADD THIS
private bool _disposed; // <=== ADD THIS
public WinMMWrapper(int elapsedMs, int resolutionMs, TimerEventType timerEventType, Action elapsedAction)
{
_elapsedMs = elapsedMs;
_resolutionMs = resolutionMs;
_timerEventType = timerEventType;
_elapsedAction = elapsedAction;
}
public bool StartElapsedTimer() // <=== RETURN bool
{
Stop(); // Stop any started timer
int myData = 1;
// === SET _timerId
_timerId = timeSetEvent(_elapsedMs, _resolutionMs / 10, new TimerEventHandler(TickHandler), ref myData, (int)_timerEventType);
return _timerId != 0;
}
public void StopTimer() // <=== ADD THIS
{
if (_timerId != 0)
{
timeKillEvent(_timerId);
_timerId = 0;
}
}
private void TickHandler(uint id, uint msg, ref int userctx, int rsv1, int rsv2)
{
_elapsedAction();
}
// === ADD Dispose and finalizer ===
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!_disposed && disposing)
StopTimer();
}
_disposed = true;
}
~MMTimer()
{
Dispose(false);
}
}
Then you can do this in a Console application:
using (var timer = new WinMMWrapper(1, 1, WinMMWrapper.TimerEventType.Repeating,
() => Console.WriteLine("Time : " + DateTime.Now.ToString("hh:mm:ss:fff"))) {
Console.Writeline("Hit a key to stop the timer and quit the application!");
Console.ReadKey();
} // <= Here timer.Dispose() gets automatically called by using.
If you cannot use a using statement because your timer will be stopped at another place in your code, you can also call timer.Dispose(); explicitly.
To make this code thread-safe, enclose your start and stop timer code in a lock(this { ... } statement.

c# waitable timer - wake up event fires instantly

The following code example (from here) creates a waitable timer and set the system in hibernation mode. After resuming back I expected an event with executable code:
class WakeUpTimer
{
[DllImport("kernel32.dll")]
public static extern SafeWaitHandle CreateWaitableTimer(IntPtr lpTimerAttributes,
bool bManualReset,
string lpTimerName);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWaitableTimer(SafeWaitHandle hTimer,
[In] ref long pDueTime,
int lPeriod,
IntPtr pfnCompletionRoutine,
IntPtr lpArgToCompletionRoutine,
bool fResume);
public event EventHandler Woken;
private BackgroundWorker bgWorker = new BackgroundWorker();
public WakeUpTimer()
{
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
}
public void SetWakeUpTime(DateTime time)
{
bgWorker.RunWorkerAsync(time.ToFileTime());
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (Woken != null)
{
Woken(this, new EventArgs());
}
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
long waketime = (long)e.Argument;
using (SafeWaitHandle handle = CreateWaitableTimer(IntPtr.Zero, true, this.GetType().Assembly.GetName().Name.ToString() + "Timer"))
{
if (SetWaitableTimer(handle, ref waketime, 0, IntPtr.Zero, IntPtr.Zero, true))
{
using (EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.AutoReset))
{
wh.SafeWaitHandle = handle;
wh.WaitOne();
}
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
}
Usage of this class is as follows:
private void setHibernation(DateTime dateTime)
{
WakeUpTimer wakeUpTimer = new WakeUpTimer();
wakeUpTimer.Woken += backFromHibernate;
wakeUpTimer.SetWakeUpTime(dateTime);
Application.SetSuspendState(PowerState.Hibernate, false, false);
Debug.WriteLine("---Hibernation starts at " + DateTime.Now + " ---");
}
private void backFromHibernate(object sender, EventArgs e)
{
Debug.WriteLine("I execute this now " + DateTime.Now);
}
As I tested before this scenario several times, the code in the method backFromHibernation is executed immediately (1 sec after setSupsendState duo to debug logs).
Am I wrong or should the method be called when the system is back up from hibernation?
Would appriciate some help, would need such an event - fired automatically after wake up.
EDIT: Set hibernation for 2 min, all works great system comes up automatically.

NullReferenceException on managing close event of theconsole

this is my scenario: I have a console application and I'm trying to handle the "close event" of the console with this code:
static void Main(string[] args)
{
SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), 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
}
private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
{
writeOnLogFile("Closed Manually");
logWriter.WriteLine();
logWriter.WriteLine();
logWriter.Close();
return true;
}
Sometimes, when I close the console with the "X" button, this is the page that is shown to me: No Source available.
I try to look in the stack call, but there is nothing that is useful.
So, how i can resolve this?
I managed to solve this problem changing my code:
Before
public delegate bool HandlerRoutine(CtrlTypes CtrlType);
static void Main(string[] args)
{
SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);
.......
}
After
public delegate bool HandlerRoutine(CtrlTypes CtrlType);
public static HandlerRoutine rout = new HandlerRoutine(ConsoleCtrlCheck);
static void Main(string[] args)
{
SetConsoleCtrlHandler(rout, true);
.......
}
Initizialing an "HandlerRoutine" before it works perfectly.
Thank you anyway! I hope it can be useful to someone!
Look, whether the handler is really attached every time.
This looks, as if the handler is not always properly attached.
Do You have a log entry, when this exception occurrs ?

Capture console exit C# in windows 7

Does anyone know how to react to the ctrl+c event in a console in c# in windows?
this question: Capture console exit C# says how to do it, but I've tried and it only captures the event when the user click the close X in the top of the console window.
Nothing happens when the user types ctrl+c, it doesn't even hit the handler when debugging.
Thanks
Here is my code
namespace EventCloseConsole
{
using System.Runtime.InteropServices;
using System;
class Program
{
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
private delegate bool EventHandler(CtrlType sig);
static EventHandler _handler;
enum CtrlType
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 1,
CTRL_CLOSE_EVENT = 2,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT = 6
}
private static bool Handler(CtrlType sig)
{
switch (sig)
{
case CtrlType.CTRL_C_EVENT:
case CtrlType.CTRL_LOGOFF_EVENT:
case CtrlType.CTRL_SHUTDOWN_EVENT:
case CtrlType.CTRL_CLOSE_EVENT:
Console.WriteLine("Closing");
System.Threading.Thread.Sleep(500);
return false;
default:
return true;
}
}
static void Main(string[] args)
{
_handler += new EventHandler(Handler);
SetConsoleCtrlHandler(_handler, true);
Console.ReadLine();
}
}
}
That works for me on Windows 7. Closing with x-button
the secret is the variable static ConsoleEventDelegate _d
private static void Main(string[] args)
{
ConsoleEventHooker.Closed += ConsoleEventHooker_Closed;
}
static void ConsoleHooker_Closed(object sender, EventArgs e)
{
}
ConsoleEventHooker.cs
namespace System
{
internal static class ConsoleEventHooker
{
private static bool _initedHooker;
private static EventHandler _closed;
private static EventHandler _shutdown;
private static ConsoleEventDelegate _d;
public static event EventHandler Closed
{
add
{
Init();
_closed += value;
}
remove { _closed -= value; }
}
public static event EventHandler Shutdown
{
add
{
Init();
_shutdown += value;
}
remove { _shutdown -= value; }
}
private static void Init()
{
if (_initedHooker) return;
_initedHooker = true;
_d = ConsoleEventCallback;
SetConsoleCtrlHandler(_d, true);
}
private static bool ConsoleEventCallback(CtrlTypes eventType)
{
if (eventType == CtrlTypes.CTRL_CLOSE_EVENT)
{
if(_closed != null) _closed(null,new EventArgs());
}
if (eventType == CtrlTypes.CTRL_SHUTDOWN_EVENT)
{
if (_shutdown != null) _shutdown(null, new EventArgs());
}
return false;
}
// A delegate type to be used as the handler routine
// for SetConsoleCtrlHandler.
delegate bool ConsoleEventDelegate(CtrlTypes ctrlType);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
}
// 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
}
You need to wire up the Console.CancelKeyPress event to a handler. Here is a great article on the topic.

Categories

Resources