using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace NPT.Common
{
/// <summary>
/// Polls the system periodically and raises the IsIdle event if the
/// user interface has been idle for at least the specfied number of seconds.
/// Stops polling once the IsIdle is fired, use Start() to resume polling.
/// </summary>
public class IdleTimer : IDisposable
{
[DllImport("user32.dll")]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
internal struct LASTINPUTINFO
{
public Int32 cbSize;
public Int32 dwTime;
}
private int timeoutSeconds = 0;
private int pollSeconds = 0;
private System.Timers.Timer timer = null;
private System.ComponentModel.ISynchronizeInvoke sychronizingObject = null;
//ctor
public IdleTimer(int idleTimeoutSeconds, System.ComponentModel.ISynchronizeInvoke sychronizingObject = null)
{
if (System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debug.WriteLine("IdleTimer: Created");
pollSeconds = 5;
if (idleTimeoutSeconds < 5)
{
idleTimeoutSeconds = 5;
pollSeconds = 4;
}
this.timeoutSeconds = idleTimeoutSeconds;
this.sychronizingObject = sychronizingObject;
public void Start()
{
System.Diagnostics.Debug.WriteLine("IdleTimer: Started");
StartPolling();
}
public void Stop()
{
System.Diagnostics.Debug.WriteLine("IdleTimer: Stopped");
StopPolling();
}
//event
public delegate void IsIdleEventHandler(object sender, IdleTimerEventArgs e);
public event IsIdleEventHandler IsIdle;
protected virtual void OnIsIdle(IdleTimerEventArgs e)
{
//raise event
StopPolling();
if (IsIdle != null) IsIdle(this, e);
}
//start polling idle time
private void StartPolling()
{
if (timer == null)
{
timer = new System.Timers.Timer(pollSeconds * 1000);
timer.Elapsed += new System.Timers.ElapsedEventHandler(Poll);
if (sychronizingObject != null) timer.SynchronizingObject = sychronizingObject;
}
timer.Start();
}
//stop polling idle time
private void StopPolling()
{
if (timer != null) timer.Stop();
}
//poll idle time
private void Poll(object sender, System.Timers.ElapsedEventArgs e)
{
StopPolling();
int idleSeconds = GetIdleTime();
if (idleSeconds >= timeoutSeconds)
{
if (System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debug.WriteLine("IdleTimer: Idle - Timeout = {1} seconds, Idle time = {0} seconds", idleSeconds, timeoutSeconds);
}
//system has been idle for at least timeoutSeconds seconds
OnIsIdle(new IdleTimerEventArgs(idleSeconds:idleSeconds,timeoutSeconds:timeoutSeconds));
}
StartPolling();
}
/// <summary>
/// Returns system idle time in seconds
/// </summary>
/// <returns></returns>
public int GetIdleTime()
{
int systemUptime = Environment.TickCount;
int lastInputTicks = 0;
int idleTicks = 0;
LASTINPUTINFO lastInputInfo = new LASTINPUTINFO();
lastInputInfo.cbSize = (Int32)Marshal.SizeOf(lastInputInfo);
lastInputInfo.dwTime = 0;
if (GetLastInputInfo(ref lastInputInfo))
{
lastInputTicks = (int)lastInputInfo.dwTime;
idleTicks = systemUptime - lastInputTicks;
}
Int32 seconds = idleTicks / 1000;
return seconds;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
StopPolling();
timer = null;
sychronizingObject = null;
if (System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debug.WriteLine("IdleTimer: Disposed");
}
}
}
}
public class IdleTimerEventArgs : EventArgs
{
public IdleTimerEventArgs(int idleSeconds, int timeoutSeconds)
{
IdleSeconds = idleSeconds;
TimeoutSeconds = timeoutSeconds;
}
public int IdleSeconds { get; set; }
public int TimeoutSeconds {get; set;}
}
}
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Threading;
//obsolete: [assembly: SecurityPermission(SecurityAction.RequestMinimum, UnmanagedCode = true)]
namespace System.Windows.Forms
{
public class MessageBoxEx
{
public static DialogResult Show(string text, uint timeoutSeconds)
{
Setup("", timeoutSeconds);
return MessageBox.Show(text);
}
public static DialogResult Show(string text, string caption, uint timeoutSeconds)
{
Setup(caption, timeoutSeconds);
return MessageBox.Show(text, caption);
}
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, uint timeoutSeconds)
{
Setup(caption, timeoutSeconds);
return MessageBox.Show(text, caption, buttons);
}
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, uint timeoutSeconds)
{
Setup(caption, timeoutSeconds);
return MessageBox.Show(text, caption, buttons, icon);
}
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defButton, uint timeoutSeconds)
{
Setup(caption, timeoutSeconds);
return MessageBox.Show(text, caption, buttons, icon, defButton);
}
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defButton, MessageBoxOptions options, uint timeoutSeconds)
{
Setup(caption, timeoutSeconds);
return MessageBox.Show(text, caption, buttons, icon, defButton, options);
}
public static DialogResult Show(IWin32Window owner, string text, uint timeoutSeconds)
{
Setup("", timeoutSeconds);
return MessageBox.Show(owner, text);
}
public static DialogResult Show(IWin32Window owner, string text, string caption, uint timeoutSeconds)
{
Setup(caption, timeoutSeconds);
return MessageBox.Show(owner, text, caption);
}
public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, uint timeoutSeconds)
{
Setup(caption, timeoutSeconds);
return MessageBox.Show(owner, text, caption, buttons);
}
public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, uint timeoutSeconds)
{
Setup(caption, timeoutSeconds);
return MessageBox.Show(owner, text, caption, buttons, icon);
}
public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defButton, uint timeoutSeconds)
{
Setup(caption, timeoutSeconds);
return MessageBox.Show(owner, text, caption, buttons, icon, defButton);
}
public static DialogResult Show(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defButton, MessageBoxOptions options, uint timeoutSeconds)
{
Setup(caption, timeoutSeconds);
return MessageBox.Show(owner, text, caption, buttons, icon, defButton, options);
}
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
public delegate void TimerProc(IntPtr hWnd, uint uMsg, UIntPtr nIDEvent, uint dwTime);
public const int WH_CALLWNDPROCRET = 12;
public const int WM_DESTROY = 0x0002;
public const int WM_INITDIALOG = 0x0110;
public const int WM_TIMER = 0x0113;
public const int WM_USER = 0x400;
public const int DM_GETDEFID = WM_USER + 0;
[DllImport("User32.dll")]
public static extern UIntPtr SetTimer(IntPtr hWnd, UIntPtr nIDEvent, uint uElapse, TimerProc lpTimerFunc);
[DllImport("User32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
[DllImport("user32.dll")]
public static extern int UnhookWindowsHookEx(IntPtr idHook);
[DllImport("user32.dll")]
public static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int maxLength);
[DllImport("user32.dll")]
public static extern int EndDialog(IntPtr hDlg, IntPtr nResult);
[StructLayout(LayoutKind.Sequential)]
public struct CWPRETSTRUCT
{
public IntPtr lResult;
public IntPtr lParam;
public IntPtr wParam;
public uint message;
public IntPtr hwnd;
};
private const int TimerID = 42;
private static HookProc hookProc;
private static TimerProc hookTimer;
private static uint hooktimeoutSeconds;
private static string hookCaption;
private static IntPtr hHook;
static MessageBoxEx()
{
hookProc = new HookProc(MessageBoxHookProc);
hookTimer = new TimerProc(MessageBoxTimerProc);
hooktimeoutSeconds = 0;
hookCaption = null;
hHook = IntPtr.Zero;
}
private static void Setup(string caption, uint timeoutSeconds)
{
if (hHook != IntPtr.Zero)
throw new NotSupportedException("multiple calls are not supported");
hooktimeoutSeconds = timeoutSeconds*1000;
hookCaption = caption != null ? caption : "";
//"AppDomain.GetCurrentThreadId()" is obsolete, however, its replacement "Thread.CurrentThread.ManagedThreadId" isn't correct this context.
//See: http://stackoverflow.com/questions/772354/appdomain-getcurrentthreadid-vs-thread-managedthreadid-for-windows-api-calls
#pragma warning disable 618 //disable the obsolete warning
hHook = SetWindowsHookEx(WH_CALLWNDPROCRET, hookProc, IntPtr.Zero, AppDomain.GetCurrentThreadId()); //obsolete:
#pragma warning restore 618 //enable the obsolete warning
}
private static IntPtr MessageBoxHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0)
return CallNextHookEx(hHook, nCode, wParam, lParam);
CWPRETSTRUCT msg = (CWPRETSTRUCT)Marshal.PtrToStructure(lParam, typeof(CWPRETSTRUCT));
IntPtr hook = hHook;
if (hookCaption != null && msg.message == WM_INITDIALOG)
{
int nLength = GetWindowTextLength(msg.hwnd);
StringBuilder text = new StringBuilder(nLength + 1);
GetWindowText(msg.hwnd, text, text.Capacity);
if (hookCaption == text.ToString())
{
hookCaption = null;
SetTimer(msg.hwnd, (UIntPtr)TimerID, hooktimeoutSeconds, hookTimer);
UnhookWindowsHookEx(hHook);
hHook = IntPtr.Zero;
}
}
return CallNextHookEx(hook, nCode, wParam, lParam);
}
private static void MessageBoxTimerProc(IntPtr hWnd, uint uMsg, UIntPtr nIDEvent, uint dwTime)
{
if (nIDEvent == (UIntPtr)TimerID)
{
short dw = (short)SendMessage(hWnd, DM_GETDEFID, IntPtr.Zero, IntPtr.Zero);
EndDialog(hWnd, (IntPtr)dw);
}
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using NPT.Common;
namespace WinFormsIdleTimerTest
{
public partial class BaseForm : Form
{
private IdleTimer idleTimer = null;
public BaseForm()
{
InitializeComponent();
}
protected int IdleTimeoutSeconds { get; set; }
protected virtual void StartIdleTimer()
{
if (idleTimer == null)
{
StopIdleTimer();
idleTimer = new IdleTimer(idleTimeoutSeconds: IdleTimeoutSeconds, sychronizingObject: this);
idleTimer.IsIdle += new IdleTimer.IsIdleEventHandler(IdleTimer_IsIdle);
}
idleTimer.Start();
}
protected virtual void StopIdleTimer()
{
if (idleTimer != null) idleTimer.Stop();
}
protected virtual void IdleTimer_IsIdle(object sender, IdleTimerEventArgs e)
{
//handle IsIdle event, close the form (override in inherting classes to do something different)
Close();
}
public new void Close()
{
StopIdleTimer();
base.Close();
}
public new void Hide()
{
StopIdleTimer();
base.Hide();
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using NPT.Common;
namespace WinFormsIdleTimerTest
{
public partial class MainForm : BaseForm
{
public MainForm()
{
InitializeComponent();
IdleTimeoutSeconds = 5;
StartIdleTimer();
}
private void CancelButton_Click(object sender, EventArgs e)
{
Close();
}
protected override void IdleTimer_IsIdle(object sender, IdleTimerEventArgs e)
{
//an optional override of the BaseForm implementation to do something more than just closing the form
//MessageBoxEx is an implementation of MessageBox that dismisses itself after a specified number of seconds
var response = MessageBoxEx.Show(
"Is there anybody out there?",
string.Format("System Idle Detected ({0} seconds)", e.IdleSeconds),
MessageBoxButtons.YesNo,
MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2,
10);
if (response == System.Windows.Forms.DialogResult.No)
{
MessageBoxEx.Show(
"\tGoodbye blue sky.",
string.Empty,
MessageBoxButtons.OK,
MessageBoxIcon.None,
MessageBoxDefaultButton.Button1,
3);
Close();
return;
}
StartIdleTimer();
}
}
}