我使用了几种不同的技术来解决问题,最终得到了一个相当不错的解决方案。我使用GetLastInput来计算系统上次被触摸的时间。这在其他地方都有很好的文档,但这是我的方法:
public static class User32Interop
{
public static TimeSpan GetLastInput()
{
var plii = new LASTINPUTINFO();
plii.cbSize = (uint)Marshal.SizeOf(plii);
if (GetLastInputInfo(ref plii))
return TimeSpan.FromMilliseconds(Environment.TickCount - plii.dwTime);
else
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
struct LASTINPUTINFO {
public uint cbSize;
public uint dwTime;
}
}
这只告诉我系统何时处于闲置状态,而不是应用程序。如果用户点击Word并在那里工作一个小时,我仍然想要超时。为了处理这种情况,我只需通过覆盖应用程序对象上的OnDeactivated和OnActivated方法来记住我的应用程序何时失去焦点:
override protected void OnDeactivated(EventArgs e)
{
this._lostFocusTime = DateTime.Now;
base.OnDeactivated(e);
}
protected override void OnActivated(EventArgs e)
{
this._lostFocusTime = null;
base.OnActivated(e);
}
我将IsIdle例程添加到应用程序对象中。它处理应用程序具有焦点但没有任何操作发生的全局情况(IsMachineIdle),以及在用户做其他事情时应用程序失去焦点的特定情况(isAppIdle):
public bool IsIdle
{
get
{
TimeSpan activityThreshold = TimeSpan.FromMinutes(1);
TimeSpan machineIdle = Support.User32Interop.GetLastInput();
TimeSpan? appIdle = this._lostFocusTime == null ? null : (TimeSpan?)DateTime.Now.Subtract(_lostFocusTime.Value);
bool isMachineIdle = machineIdle > activityThreshold ;
bool isAppIdle = appIdle != null && appIdle > activityThreshold ;
return isMachineIdle || isAppIdle;
}
}
我做的最后一件事是创建一个定时器循环,每隔几秒钟轮询此标志事件。
这似乎运作良好。