【问题标题】:Reset timer when mouse is moved anywhere in windows当鼠标在窗口中的任何位置移动时重置计时器
【发布时间】:2013-12-12 19:38:13
【问题描述】:

我有一个 Visual Basic 应用程序,我希望它在 60 秒后执行某些任务,而无需用户单击任何内容或与屏幕交互。

所以基本上我希望在用户移动、点击或按下键盘上的某个键时重置计时器。

我知道如何使用表单上的特定控件来执行此操作,但即使焦点在我的应用程序之外,我也希望它能够注册。有什么想法吗?

对此的任何帮助将不胜感激。

【问题讨论】:

  • 点击鼠标后 - 我们应该首先激活主窗口,然后调用弹出窗口内控件的 .Focus() 即可按预期工作。
  • 看看这个:stackoverflow.com/a/20434431/897326 它回答你的问题了吗?
  • 在自定义类中实现 IMessageFilter 并在其 WndProc() 中捕获所有所需的“操作”。当在您的时间限制内未检测到任何活动时,使该类引发自定义事件。
  • @Idle_Mind:这是一个令人兴奋的答案。 :) 虽然它可能会起作用,但 OP 可能会感到困惑。你能详细说明一下答案吗?
  • @Neolisk...当然,请查看我下面的帖子。

标签: vb.net timer keydown mousemove onmouseclick


【解决方案1】:

这是一个使用 IMessageFilter 的快速示例,正如我之前在 cmets 中提到的那样。 IMessageFilter 在这里特别受欢迎,因为它适用于所有形式的应用程序,而不仅仅是主要的:

Public Class Form1

    Private WithEvents IdleDetector As IdleTimeout

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        IdleDetector = New IdleTimeout(New TimeSpan(0, 1, 0)) ' one minute timeout duration
        Application.AddMessageFilter(IdleDetector)
        IdleDetector.Reset()
    End Sub

    Private Sub IdleDetector_UserIsIdle() Handles IdleDetector.UserIsIdle

        ' ... do something in here ...
        MessageBox.Show("Inactivity detected...locking some feature!")
        Button1.Enabled = False

        ' restart the timeout period?
        ' IdleDetector.Reset()
    End Sub

End Class

Public Class IdleTimeout
    Implements IMessageFilter

    Public Event UserIsIdle()

    Private Const WM_MOUSEMOVE As Integer = &H200
    Private Const WM_LBUTTONDOWN As Integer = &H201
    Private Const WM_LBUTTONUP As Integer = &H202
    Private Const WM_LBUTTONDBLCLK As Integer = &H203
    Private Const WM_RBUTTONDOWN As Integer = &H204
    Private Const WM_RBUTTONUP As Integer = &H205
    Private Const WM_RBUTTONDBLCLK As Integer = &H206
    Private Const WM_MBUTTONDOWN As Integer = &H207
    Private Const WM_MBUTTONUP As Integer = &H208
    Private Const WM_MBUTTONDBLCLK As Integer = &H209
    Private Const WM_MOUSEWHEEL As Integer = &H20A
    Private Const WM_KEYDOWN As Integer = &H100
    Private Const WM_KEYUP As Integer = &H101
    Private Const WM_SYSKEYDOWN As Integer = &H104
    Private Const WM_SYSKEYUP As Integer = &H105

    Private IdleTimeoutDuration As TimeSpan
    Private WithEvents tmr As System.Timers.Timer
    Private TargetDateTime As DateTime
    Private SC As WindowsFormsSynchronizationContext

    Public Sub New(ByVal TimeoutDuration As TimeSpan)
        SC = System.Windows.Forms.WindowsFormsSynchronizationContext.Current
        Me.IdleTimeoutDuration = TimeoutDuration
        tmr = New System.Timers.Timer
        tmr.Interval = 1000
        Me.Reset()
    End Sub

    Public Sub Reset()
        TargetDateTime = DateTime.Now.Add(IdleTimeoutDuration)
        tmr.Start()
    End Sub

    Public Function PreFilterMessage(ByRef m As System.Windows.Forms.Message) As Boolean Implements System.Windows.Forms.IMessageFilter.PreFilterMessage
        Select Case m.Msg
            Case WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, WM_SYSKEYUP, _
                    WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, _
                    WM_RBUTTONDOWN, WM_RBUTTONUP, WM_RBUTTONDBLCLK, _
                    WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MBUTTONDBLCLK

                TargetDateTime = DateTime.Now.Add(IdleTimeoutDuration)
        End Select

        Return False
    End Function

    Private Sub tmr_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles tmr.Elapsed
        If TargetDateTime.Subtract(DateTime.Now).TotalMilliseconds < 0 Then
            tmr.Stop()

            If Not IsNothing(SC) Then
                SC.Post(New System.Threading.SendOrPostCallback(AddressOf RaiseIdleEvent), Nothing)
            End If
        Else
            ' This is a one second timer so you could raise a "time out duration left" type event if you wanted to ...
        End If
    End Sub

    Private Sub RaiseIdleEvent(ByVal x As Object)
        ' ... do not call directly ...
        ' Called via SC.Post() which puts this in the main UI thread!
        RaiseEvent UserIsIdle()
    End Sub

End Class

【讨论】:

  • "即使焦点在我的应用程序之外"
  • 无论哪个应用程序具有焦点,它都会起作用。 ...但我将问题解释为仅针对他的应用程序的空闲检测器,但我可以看到它如何被解释为系统范围。如果是这种情况,那么您可以使用 SomeNickName 的方法,或者实现低级鼠标/键盘挂钩。
【解决方案2】:

您必须使用它来获取系统的空闲时间(即 lastinputinfo 时间)

<DllImport("user32.dll")> _
Shared Function GetLastInputInfo(ByRef plii As LASTINPUTINFO) As Boolean
End Function

参考here

它是 C#,但我希望它对你有所帮助。

【讨论】:

    【解决方案3】:

    我从 LastInputInfo 获取空闲时间。
    我使用一个计时器来检索它并每 500 毫秒对其进行测试,代码如下所示(它已经投入生产几年了):

    Private ATimer As DispatcherTimer
    
    Public Sub New()
            ....
    
        ATimer = New DispatcherTimer
        AddHandler ATimer.Tick, AddressOf Me.ATimer_Tick
        ATimer.Interval = TimeSpan.FromMilliseconds(500)  'Checks for idle every 500ms
        ATimer.Start()
    End Sub
    
    
    Public Structure LASTINPUTINFO
        Public cbSize As Integer
        Public dwTime As Integer
    End Structure
    
    Public Declare Function GetLastInputInfo Lib "User32.dll" _
                                  (ByRef lii As LASTINPUTINFO) As Boolean
    
    Private Sub ATimer_Tick(ByVal sender As Object, ByVal e As EventArgs)
    
        MyLastInputInfo = New LASTINPUTINFO
        MyLastInputInfo.cbSize = Runtime.InteropServices.Marshal.SizeOf(MyLastInputInfo)
    
       ' get last input info from Windows
        If GetLastInputInfo(MyLastInputInfo) Then     ' if we have an input info     
           ' compute idle time
           Dim sysIdleTime_ms As Integer = (GetTickCount() - MyLastInputInfo.dwTime)
    
           ' ... Now you have the idle time in ms, do whatever you want with it :=)
     End Sub
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多