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.
Related
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.
I have some operations in my application which rely on short timers. Using the example code below I have timers firing every ~5ms as required.
On an Intel i5 10400H CPU the timings are observed to be off, and the callback occurs after ~15ms (or a multiple of 15). Using the ClockRes sysinternals tool shows that the machine has a system timer resolution of 15ms even when run after the call to timeBeginPeriod(1) made in the code below.
Using https://cms.lucashale.com/timer-resolution/ to set the resolution to the maximum supported value (0.5ms) does not change the behaviour of the example code.
From what I can see the machine is using the Invariant TSC acpi timer, and forcing it to use HPET (with bcdedit /set useplatformclock true and rebooting) did not change the behaviour.
I can't see anything in the CPU documentation or errata that would explain this.
I don't know where the problem lies and if it is something that is fixable on my end, any ideas?
Edit: Having this program (DPC Latency Checker) open results in the timer queue firing when expected, so it's solveable.
Example code:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
using (new TimePeriod(1))
RunTimer();
}
public static void RunTimer()
{
var completionEvent = new ManualResetEvent(false);
var stopwatch = Stopwatch.StartNew();
var i = 0;
var previous = 0L;
using var x = TimerQueue.Default.CreateTimer((s) =>
{
if (i > 100)
completionEvent.Set();
i++;
var now = stopwatch.ElapsedMilliseconds;
var gap = now - previous;
previous = now;
Console.WriteLine($"Gap: {gap}ms");
}, "", 10, 5);
completionEvent.WaitOne();
}
}
public class TimerQueueTimer : IDisposable
{
private TimerQueue MyQueue;
private TimerCallback Callback;
private object UserState;
private IntPtr Handle;
internal TimerQueueTimer(
TimerQueue queue,
TimerCallback cb,
object state,
uint dueTime,
uint period,
TimerQueueTimerFlags flags)
{
MyQueue = queue;
Callback = cb;
UserState = state;
bool rslt = TQTimerWin32.CreateTimerQueueTimer(
out Handle,
MyQueue.Handle,
TimerCallback,
IntPtr.Zero,
dueTime,
period,
flags);
if (!rslt)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Error creating timer.");
}
}
~TimerQueueTimer()
{
Dispose(false);
}
public void Change(uint dueTime, uint period)
{
bool rslt = TQTimerWin32.ChangeTimerQueueTimer(MyQueue.Handle, ref Handle, dueTime, period);
if (!rslt)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Error changing timer.");
}
}
private void TimerCallback(IntPtr state, bool bExpired)
{
Callback.Invoke(UserState);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private IntPtr completionEventHandle = new IntPtr(-1);
public void Dispose(WaitHandle completionEvent)
{
completionEventHandle = completionEvent.SafeWaitHandle.DangerousGetHandle();
this.Dispose();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
bool rslt = TQTimerWin32.DeleteTimerQueueTimer(MyQueue.Handle,
Handle, completionEventHandle);
if (!rslt)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Error deleting timer.");
}
disposed = true;
}
}
}
public class TimerQueue : IDisposable
{
public IntPtr Handle { get; private set; }
public static TimerQueue Default { get; private set; }
static TimerQueue()
{
Default = new TimerQueue(IntPtr.Zero);
}
private TimerQueue(IntPtr handle)
{
Handle = handle;
}
public TimerQueue()
{
Handle = TQTimerWin32.CreateTimerQueue();
if (Handle == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Error creating timer queue.");
}
}
~TimerQueue()
{
Dispose(false);
}
public TimerQueueTimer CreateTimer(
TimerCallback callback,
object state,
uint dueTime,
uint period)
{
return CreateTimer(callback, state, dueTime, period, TimerQueueTimerFlags.ExecuteInPersistentThread);
}
public TimerQueueTimer CreateTimer(
TimerCallback callback,
object state,
uint dueTime,
uint period,
TimerQueueTimerFlags flags)
{
return new TimerQueueTimer(this, callback, state, dueTime, period, flags);
}
private IntPtr CompletionEventHandle = new IntPtr(-1);
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(WaitHandle completionEvent)
{
CompletionEventHandle = completionEvent.SafeWaitHandle.DangerousGetHandle();
Dispose();
}
private bool Disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!Disposed)
{
if (Handle != IntPtr.Zero)
{
bool rslt = TQTimerWin32.DeleteTimerQueueEx(Handle, CompletionEventHandle);
if (!rslt)
{
int err = Marshal.GetLastWin32Error();
throw new Win32Exception(err, "Error disposing timer queue");
}
}
Disposed = true;
}
}
}
public enum TimerQueueTimerFlags : uint
{
ExecuteDefault = 0x0000,
ExecuteInTimerThread = 0x0020,
ExecuteInIoThread = 0x0001,
ExecuteInPersistentThread = 0x0080,
ExecuteLongFunction = 0x0010,
ExecuteOnlyOnce = 0x0008,
TransferImpersonation = 0x0100,
}
public delegate void Win32WaitOrTimerCallback(
IntPtr lpParam,
[MarshalAs(UnmanagedType.U1)] bool bTimedOut);
static public class TQTimerWin32
{
[DllImport("kernel32.dll", SetLastError = true)]
public extern static IntPtr CreateTimerQueue();
[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool DeleteTimerQueue(IntPtr timerQueue);
[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool DeleteTimerQueueEx(IntPtr timerQueue, IntPtr completionEvent);
[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool CreateTimerQueueTimer(
out IntPtr newTimer,
IntPtr timerQueue,
Win32WaitOrTimerCallback callback,
IntPtr userState,
uint dueTime,
uint period,
TimerQueueTimerFlags flags);
[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool ChangeTimerQueueTimer(
IntPtr timerQueue,
ref IntPtr timer,
uint dueTime,
uint period);
[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool DeleteTimerQueueTimer(
IntPtr timerQueue,
IntPtr timer,
IntPtr completionEvent);
}
public sealed class TimePeriod : IDisposable
{
private const string WINMM = "winmm.dll";
private static TIMECAPS timeCapabilities;
private static int inTimePeriod;
private readonly int period;
private int disposed;
[DllImport(WINMM, ExactSpelling = true)]
private static extern int timeGetDevCaps(ref TIMECAPS ptc, int cbtc);
[DllImport(WINMM, ExactSpelling = true)]
private static extern int timeBeginPeriod(int uPeriod);
[DllImport(WINMM, ExactSpelling = true)]
private static extern int timeEndPeriod(int uPeriod);
static TimePeriod()
{
int result = timeGetDevCaps(ref timeCapabilities, Marshal.SizeOf(typeof(TIMECAPS)));
if (result != 0)
{
throw new InvalidOperationException("The request to get time capabilities was not completed because an unexpected error with code " + result + " occured.");
}
}
internal TimePeriod(int period)
{
if (Interlocked.Increment(ref inTimePeriod) != 1)
{
Interlocked.Decrement(ref inTimePeriod);
throw new NotSupportedException("The process is already within a time period. Nested time periods are not supported.");
}
if (period < timeCapabilities.wPeriodMin || period > timeCapabilities.wPeriodMax)
{
throw new ArgumentOutOfRangeException("period", "The request to begin a time period was not completed because the resolution specified is out of range.");
}
int result = timeBeginPeriod(period);
if (result != 0)
{
throw new InvalidOperationException("The request to begin a time period was not completed because an unexpected error with code " + result + " occured.");
}
this.period = period;
}
internal static int MinimumPeriod
{
get
{
return timeCapabilities.wPeriodMin;
}
}
internal static int MaximumPeriod
{
get
{
return timeCapabilities.wPeriodMax;
}
}
internal int Period
{
get
{
if (this.disposed > 0)
{
throw new ObjectDisposedException("The time period instance has been disposed.");
}
return this.period;
}
}
public void Dispose()
{
if (Interlocked.Increment(ref this.disposed) == 1)
{
timeEndPeriod(this.period);
Interlocked.Decrement(ref inTimePeriod);
}
else
{
Interlocked.Decrement(ref this.disposed);
}
}
[StructLayout(LayoutKind.Sequential)]
private struct TIMECAPS
{
internal int wPeriodMin;
internal int wPeriodMax;
}
}
}
This seem to be an issue with windows 10 2004. I would guess that it has nothing to do with the processor/motherboard.
A possible workaround might be to use a stopwatch and spinwait on a thread. This would be inadvisable for regular consumer applications since it would consume a full thread, but might be feasible if you have full control of the system.
I encountered the exact same problem under Windows 10 2004. Previous versions did not seem to exhibit the same behavior. CreateTimerQueueTimer does not seem to honor timeBeginPeriod anymore and its minimum period seems to be 15ms (good old 15 ms...).
There are a few people complaining about this problem around, but not a lot. (see this forum entry for example.
I do not know if this is a bug introduced in v2004, or a power-saving "feature" that got sneaked past us.
That being said, official documentation never linked TimerQueueTimers and timeBeginPeriod, so if might have been a bug to begin with that they honored the timeBeginPeriod setting.
In any case, I ended up re-implementing a TimerQueue on top of timeBeginPeriod/timeSetEvent to achieve the required timer frequency.
Running into the same problem, I'm using CreateTimerQueueTimer. What still works is timeSetEvent. You'll loose some precision as it's in whole milliseconds, but it's better than nothing.
I have a program WFA that also has and command Window. I open the window with AllocConsole(); When I close the console window, I use FreeConsole(); but when I open it again with AllocConsole(); I wanna write and read from it and it throws an exeption.
The code:
namespace WindowsFormsApplication2
{
class classx
{
[DllImport("kernel32.dll")]
public static extern Int32 AllocConsole();
[DllImport("kernel32.dll")]
public static extern bool FreeConsole();
[DllImport("kernel32")]
public static extern bool AttachConsole();
[DllImport("kernel32")]
public static extern bool GetConsoleWindow();
public static bool z = false;
[DllImport("kernel32")]
public static extern bool SetConsoleCtrlHandler(HandlerRoutine HandlerRoutine, bool Add);
public delegate bool HandlerRoutine(uint dwControlType);
}
public partial class Form1 : Form
{
NotifyIcon icontask;
Icon iconone_active;
Icon iconone_inactive;
/*Icon icontwo;
Icon iconthree;
Icon iconfour;
Icon iconfive;*/
Thread Threadworkermy;
public Form1()
{
InitializeComponent();
this.WindowState = FormWindowState.Minimized;
this.ShowInTaskbar = false;
iconone_active = new Icon(".../iconone_active.ico");
iconone_inactive = new Icon(".../iconone_inactive.ico");
icontask = new NotifyIcon();
icontask.Icon = iconone_active;
icontask.Visible = true;
Threadworkermy = new Thread(new ThreadStart(checkActivityThread));
Threadworkermy.Start();
MenuItem Nameapp = new MenuItem("xr");
MenuItem quitappitem = new MenuItem("quit program");
MenuItem OpenGUI = new MenuItem("Open GUI");
MenuItem Advancedmodewindow = new MenuItem("x");
ContextMenu contextmenu = new ContextMenu();
quitappitem.Click += quitappitem_click;
OpenGUI.Click += OpenGUI_click;
Advancedmodewindow.Click += Advancedmodewindow_click;
contextmenu.MenuItems.Add(Nameapp);
contextmenu.MenuItems[0].Enabled = false;
contextmenu.MenuItems.Add("-");
contextmenu.MenuItems.Add(OpenGUI);
contextmenu.MenuItems.Add(Advancedmodewindow);
contextmenu.MenuItems.Add("-");
contextmenu.MenuItems.Add(quitappitem);
icontask.ContextMenu = contextmenu;
icontask.Icon = iconone_active;
icontask.Visible = true;
}
private void Advancedmodewindow_click(object sender, EventArgs e)
{
classx.AllocConsole();
Console.WriteLine("X");
classx.FreeConsole();
}
private void OpenGUI_click(object sender, EventArgs e)
{
this.ShowInTaskbar = true;
this.WindowState = FormWindowState.Normal;
}
private void quitappitem_click(object sender, EventArgs e)
{
Threadworkermy.Abort();
icontask.Dispose();
this.Close();
}
public void checkActivityThread()
{
try
{
while(true)
{
Thread.Sleep(100);
}
} catch(ThreadAbortException tbe)
{
}
}
private void button1_Click(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Minimized;
this.ShowInTaskbar = false;
}
}
}
Exception that it throws out 'System.IO.IOException' in mscorlib.dll
Additional information: The handle is invalid.
To those who will be saying to change the type, I can't. (it needs to be WFA application)
there seems to be an issue with destroying the consolewindow, so you could just hide it.
For hiding the window you need an additional DllImport from user32.dll and change the returnvalue of GetConsoleWindow to IntPtr:
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
Now check if a console-handle already exists. If it does show the console otherwise create the consolewindow:
private void Advancedmodewindow_click(object sender, EventArgs e)
{
IntPtr handle = classx.GetConsoleWindow();
if (handle == IntPtr.Zero)
{
classx.AllocConsole();
handle = classx.GetConsoleWindow();
}
else
{
//shows the window with the given handle
classx.ShowWindow(handle, 8);
}
Console.WriteLine("X");
//hides the window with the given handle
classx.ShowWindow(handle, 0);
}
The original solution can be found here:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/cdee5d88-3325-47ce-9f6b-83aa4447f8ca/console-exception-on-windows-8?forum=clr
class WakeUP
{
[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 WakeUP()
{
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
}
public void SetWakeUpTime(DateTime time, String tName)
{
// Create a 7-tuple.
var wutargs = new Tuple<string, string>(time.ToFileTime().ToString(), tName.ToString());
bgWorker.RunWorkerAsync(wutargs);
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (Woken != null)
{
Woken(this, new EventArgs());
}
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
var thetuple = e.Argument;
long wakeuptime = (long)thetuple.Item1;
using (SafeWaitHandle handle = CreateWaitableTimer(IntPtr.Zero, true, "Timer"))
{
//if (SetWaitableTimer(handle, ref "12:00 AM", 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());
//}
}
}
Visual Studio is telling me in regards to:
long wakeuptime = (long)thetuple.Item1;
Error 1 'object' does not contain a definition for 'Item1' and no extension method 'Item1' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) C:\Users\esepich\Documents\Visual Studio 2013\Projects\SepysAlarmV1ecs\SepysAlarmV1\WakeUP.cs 57 46 SepysAlarmV1
How am I supposed to access the elements of the tuple?
Thank you for posting...
The argument could be any type. It's passed as an object, so cast it back to the original type:
var thetuple = (Tuple<string, string>)e.Argument;
You're going to get a compiler error on the next line too:
long wakeuptime = (long)thetuple.Item1;
Consider using Convert.ToInt64 or Int64.TryParse.
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.