A background process to detect user activity / How to get VB.NET to use a C# class?

Hello all,

I have a multi-form application that is running fine, but now I would like to add code that would revert the application to its startup form if there is no user activity (mouse or keyboard).

I have 27 forms in my application, and instead of going into each form indiviudually and adding a timer, I was wondering if there were a method to simply have a separate application (or hidden form in the same application) that would detect mouse and keyboard activity running in the background much like the method that your Windows screen saver uses to detect whether there is activity or not before showing the screen saver.

I have looked into keyboard and mouse hooks but ran into a dead end; if I understand the documentation directly, .NET does not support global hooks which is what I am looking for here I guess.

Cheers all.

N. Farr



Answer this question

A background process to detect user activity / How to get VB.NET to use a C# class?

  • Chidu

    Thanks Ryan. I'll give it a go. N. Farr
  • Wayne Arant II

    Hie guys, I'm quite new to .Net I'm programming in VB .Net 2003, for my Final year project at Uni. I'm facing a similar problem in trying to achieve the same and would like some help.

    Thanks in advance.


  • mixxie

    The "ApplicationIdle" codeproj is not the proper answer - it analyze counters once a sec. So if you moved mouse in between probes - your app wont notice that. Nice approach for the problem but I think is not sensible.

    The Win32 hooks offered in first answer a bit scary - too many constants and author note is - I was too afraid to test it so I decided to help you out.

    I have found similar to his solution - http://www.codeproject.com/csharp/globalhook.asp it is commented better and has much less constants and it almost guaranteed not to blow up anything.

    Cheers


  • MeDave

    Hi,

    A while back I had a similar requirement on detecting operating system keyboard/mouse activity, at the time I did put together some code using SetWindowsHookEx API. However in the end I didn't incorporate this into my application because of the following reasons:

    1. I'm not too familiar with native Win32 API and PInvoke

    2. Low-level hooks are dangeous, they can easily lock your keyboard/mouse if not careful.

    3. Didn't have much time to thoroughly test the code

    That being said, this should give you something to start with, please use it at your own discretion(credit goes to pinvoke.net for most of the signatures):

    This is the module containing all the pinvoke signatures:

    Imports System.Runtime.InteropServices

    Module HookLib

    Delegate Function HookProc(ByVal code As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer

    Delegate Function LowLevelKeyboardProc(ByVal nCode As Integer, ByVal wParam As WindowMessages, ByVal lParam As KBDLLHOOKSTRUCT) As Integer

    Delegate Function LowLevelMouseProc(ByVal nCode As Integer, ByVal wParam As WindowMessages, ByVal lParam As MSLLHOOKSTRUCT) As Integer

    <DllImport("user32.dll")> _

    Public Function SetWindowsHookEx(ByVal hook As HookType, ByVal callback As HookProc, ByVal hMod As IntPtr, ByVal dwThreadId As UInt32) As IntPtr

    End Function

    <DllImport("user32.dll", SetLastError:=True)> _

    Public Function SetWindowsHookEx(ByVal hook As HookType, ByVal callback As LowLevelKeyboardProc, ByVal hMod As IntPtr, ByVal dwThreadId As UInt32) As IntPtr

    End Function

    <DllImport("user32.dll", SetLastError:=True)> _

    Public Function SetWindowsHookEx(ByVal hook As HookType, ByVal callback As LowLevelMouseProc, ByVal hMod As IntPtr, ByVal dwThreadId As UInt32) As IntPtr

    End Function

    <DllImport("user32.dll")> _

    Public Function CallNextHookEx(ByVal hhk As IntPtr, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr

    End Function

    <DllImport("user32.dll")> _

    Public Function UnhookWindowsHookEx(ByVal hhk As IntPtr) As Boolean

    End Function

    End Module

    <StructLayout(LayoutKind.Sequential)> _

    Public Class MSLLHOOKSTRUCT

    Public pt As POINT

    Public mouseData As Integer

    Public flags As Integer

    Public time As Integer

    Public dwExtraInfo As IntPtr

    End Class

    <System.Runtime.InteropServices.StructLayout(Runtime.InteropServices.LayoutKind.Sequential)> _

    Public Structure POINT

    Public X As Integer

    Public Y As Integer

    Public Sub New(ByVal X As Integer, ByVal Y As Integer)

    Me.X = X

    Me.Y = Y

    End Sub

    End Structure

    <StructLayout(LayoutKind.Sequential)> _

    Public Class KBDLLHOOKSTRUCT

    Public vkCode As Integer

    Public scanCode As Integer

    Public flags As Integer

    Public time As Integer

    Public dwExtraInfo As IntPtr

    End Class

    Enum HookType

    WH_JOURNALRECORD = 0

    WH_JOURNALPLAYBACK = 1

    WH_KEYBOARD = 2

    WH_GETMESSAGE = 3

    WH_CALLWNDPROC = 4

    WH_CBT = 5

    WH_SYSMSGFILTER = 6

    WH_MOUSE = 7

    WH_HARDWARE = 8

    WH_DEBUG = 9

    WH_SHELL = 10

    WH_FOREGROUNDIDLE = 11

    WH_CALLWNDPROCRET = 12

    WH_KEYBOARD_LL = 13

    WH_MOUSE_LL = 14

    End Enum

    Enum WindowMessages

    WM_ACTIVATE = &H6

    WM_ACTIVATEAPP = &H1C

    WM_AFXFIRST = &H360

    WM_AFXLAST = &H37F

    WM_APP = &H8000

    WM_ASKCBFORMATNAME = &H30C

    WM_CANCELJOURNAL = &H4B

    WM_CANCELMODE = &H1F

    WM_CAPTURECHANGED = &H215

    WM_CHANGECBCHAIN = &H30D

    WM_CHAR = &H102

    WM_CHARTOITEM = &H2F

    WM_CHILDACTIVATE = &H22

    WM_CLEAR = &H303

    WM_CLOSE = &H10

    WM_COMMAND = &H111

    WM_COMPACTING = &H41

    WM_COMPAREITEM = &H39

    WM_CONTEXTMENU = &H7B

    WM_COPY = &H301

    WM_COPYDATA = &H4A

    WM_CREATE = &H1

    WM_CTLCOLORBTN = &H135

    WM_CTLCOLORDLG = &H136

    WM_CTLCOLOREDIT = &H133

    WM_CTLCOLORLISTBOX = &H134

    WM_CTLCOLORMSGBOX = &H132

    WM_CTLCOLORSCROLLBAR = &H137

    WM_CTLCOLORSTATIC = &H138

    WM_CUT = &H300

    WM_DEADCHAR = &H103

    WM_DELETEITEM = &H2D

    WM_DESTROY = &H2

    WM_DESTROYCLIPBOARD = &H307

    WM_DEVICECHANGE = &H219

    WM_DEVMODECHANGE = &H1B

    WM_DISPLAYCHANGE = &H7E

    WM_DRAWCLIPBOARD = &H308

    WM_DRAWITEM = &H2B

    WM_DROPFILES = &H233

    WM_ENABLE = &HA

    WM_ENDSESSION = &H16

    WM_ENTERIDLE = &H121

    WM_ENTERMENULOOP = &H211

    WM_ENTERSIZEMOVE = &H231

    WM_ERASEBKGND = &H14

    WM_EXITMENULOOP = &H212

    WM_EXITSIZEMOVE = &H232

    WM_FONTCHANGE = &H1D

    WM_GETDLGCODE = &H87

    WM_GETFONT = &H31

    WM_GETHOTKEY = &H33

    WM_GETICON = &H7F

    WM_GETMINMAXINFO = &H24

    WM_GETOBJECT = &H3D

    WM_GETSYSMENU = &H313

    WM_GETTEXT = &HD

    WM_GETTEXTLENGTH = &HE

    WM_HANDHELDFIRST = &H358

    WM_HANDHELDLAST = &H35F

    WM_HELP = &H53

    WM_HOTKEY = &H312

    WM_HSCROLL = &H114

    WM_HSCROLLCLIPBOARD = &H30E

    WM_ICONERASEBKGND = &H27

    WM_IME_CHAR = &H286

    WM_IME_COMPOSITION = &H10F

    WM_IME_COMPOSITIONFULL = &H284

    WM_IME_CONTROL = &H283

    WM_IME_ENDCOMPOSITION = &H10E

    WM_IME_KEYDOWN = &H290

    WM_IME_KEYLAST = &H10F

    WM_IME_KEYUP = &H291

    WM_IME_NOTIFY = &H282

    WM_IME_REQUEST = &H288

    WM_IME_SELECT = &H285

    WM_IME_SETCONTEXT = &H281

    WM_IME_STARTCOMPOSITION = &H10D

    WM_INITDIALOG = &H110

    WM_INITMENU = &H116

    WM_INITMENUPOPUP = &H117

    WM_INPUTLANGCHANGE = &H51

    WM_INPUTLANGCHANGEREQUEST = &H50

    WM_KEYDOWN = &H100

    WM_KEYFIRST = &H100

    WM_KEYLAST = &H108

    WM_KEYUP = &H101

    WM_KILLFOCUS = &H8

    WM_LBUTTONDBLCLK = &H203

    WM_LBUTTONDOWN = &H201

    WM_LBUTTONUP = &H202

    WM_MBUTTONDBLCLK = &H209

    WM_MBUTTONDOWN = &H207

    WM_MBUTTONUP = &H208

    WM_MDIACTIVATE = &H222

    WM_MDICASCADE = &H227

    WM_MDICREATE = &H220

    WM_MDIDESTROY = &H221

    WM_MDIGETACTIVE = &H229

    WM_MDIICONARRANGE = &H228

    WM_MDIMAXIMIZE = &H225

    WM_MDINEXT = &H224

    WM_MDIREFRESHMENU = &H234

    WM_MDIRESTORE = &H223

    WM_MDISETMENU = &H230

    WM_MDITILE = &H226

    WM_MEASUREITEM = &H2C

    WM_MENUCHAR = &H120

    WM_MENUCOMMAND = &H126

    WM_MENUDRAG = &H123

    WM_MENUGETOBJECT = &H124

    WM_MENURBUTTONUP = &H122

    WM_MENUSELECT = &H11F

    WM_MOUSEACTIVATE = &H21

    WM_MOUSEFIRST = &H200

    WM_MOUSEHOVER = &H2A1

    WM_MOUSELAST = &H20A

    WM_MOUSELEAVE = &H2A3

    WM_MOUSEMOVE = &H200

    WM_MOUSEWHEEL = &H20A

    WM_MOVE = &H3

    WM_MOVING = &H216

    WM_NCACTIVATE = &H86

    WM_NCCALCSIZE = &H83

    WM_NCCREATE = &H81

    WM_NCDESTROY = &H82

    WM_NCHITTEST = &H84

    WM_NCLBUTTONDBLCLK = &HA3

    WM_NCLBUTTONDOWN = &HA1

    WM_NCLBUTTONUP = &HA2

    WM_NCMBUTTONDBLCLK = &HA9

    WM_NCMBUTTONDOWN = &HA7

    WM_NCMBUTTONUP = &HA8

    WM_NCMOUSEHOVER = &H2A0

    WM_NCMOUSELEAVE = &H2A2

    WM_NCMOUSEMOVE = &HA0

    WM_NCPAINT = &H85

    WM_NCRBUTTONDBLCLK = &HA6

    WM_NCRBUTTONDOWN = &HA4

    WM_NCRBUTTONUP = &HA5

    WM_NEXTDLGCTL = &H28

    WM_NEXTMENU = &H213

    WM_NOTIFY = &H4E

    WM_NOTIFYFORMAT = &H55

    WM_NULL = &H0

    WM_PAINT = &HF

    WM_PAINTCLIPBOARD = &H309

    WM_PAINTICON = &H26

    WM_PALETTECHANGED = &H311

    WM_PALETTEISCHANGING = &H310

    WM_PARENTNOTIFY = &H210

    WM_PASTE = &H302

    WM_PENWINFIRST = &H380

    WM_PENWINLAST = &H38F

    WM_POWER = &H48

    WM_PRINT = &H317

    WM_PRINTCLIENT = &H318

    WM_QUERYDRAGICON = &H37

    WM_QUERYENDSESSION = &H11

    WM_QUERYNEWPALETTE = &H30F

    WM_QUERYOPEN = &H13

    WM_QUEUESYNC = &H23

    WM_QUIT = &H12

    WM_RBUTTONDBLCLK = &H206

    WM_RBUTTONDOWN = &H204

    WM_RBUTTONUP = &H205

    WM_RENDERALLFORMATS = &H306

    WM_RENDERFORMAT = &H305

    WM_SETCURSOR = &H20

    WM_SETFOCUS = &H7

    WM_SETFONT = &H30

    WM_SETHOTKEY = &H32

    WM_SETICON = &H80

    WM_SETREDRAW = &HB

    WM_SETTEXT = &HC

    WM_SETTINGCHANGE = &H1A

    WM_SHOWWINDOW = &H18

    WM_SIZE = &H5

    WM_SIZECLIPBOARD = &H30B

    WM_SIZING = &H214

    WM_SPOOLERSTATUS = &H2A

    WM_STYLECHANGED = &H7D

    WM_STYLECHANGING = &H7C

    WM_SYNCPAINT = &H88

    WM_SYSCHAR = &H106

    WM_SYSCOLORCHANGE = &H15

    WM_SYSCOMMAND = &H112

    WM_SYSDEADCHAR = &H107

    WM_SYSKEYDOWN = &H104

    WM_SYSKEYUP = &H105

    WM_SYSTIMER = &H118 ' undocumented, see http://support.microsoft.com/ id=108938

    WM_TCARD = &H52

    WM_TIMECHANGE = &H1E

    WM_TIMER = &H113

    WM_UNDO = &H304

    WM_UNINITMENUPOPUP = &H125

    WM_USER = &H400

    WM_USERCHANGED = &H54

    WM_VKEYTOITEM = &H2E

    WM_VSCROLL = &H115

    WM_VSCROLLCLIPBOARD = &H30A

    WM_WINDOWPOSCHANGED = &H47

    WM_WINDOWPOSCHANGING = &H46

    WM_WININICHANGE = &H1A

    WM_XBUTTONDBLCLK = &H20D

    WM_XBUTTONDOWN = &H20B

    WM_XBUTTONUP = &H20C

    End Enum

    This is the tester app, it's a simple form with 2 buttons:

    Imports System.Runtime.InteropServices

    Imports System.Reflection

    Public Class Form1

    Private delLLKB As New LowLevelKeyboardProc(AddressOf LowLevelKbCallback)

    Private delLLMS As New LowLevelMouseProc(AddressOf LowLevelMsCallback)

    Private kbHookPointer As IntPtr

    Private msHookPointer As IntPtr

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

    Me.Hook()

    End Sub

    Private Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click

    Me.UnHook()

    End Sub

    ' Register global keyboard and mouse hooks

    Private Sub Hook()

    If kbHookPointer <> Nothing Or msHookPointer <> Nothing Then

    Me.UnHook()

    End If

    Dim hMod As IntPtr = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()(0))

    ' For thread level hooks, use HookType.WH_KEYBOARD and WH_MOUSE,

    ' then set hMod to 0 and dwThreadId to AppDomain.GetCurrentThreadId()

    kbHookPointer = SetWindowsHookEx(HookType.WH_KEYBOARD_LL, delLLKB, hMod, IntPtr.Zero)

    msHookPointer = SetWindowsHookEx(HookType.WH_MOUSE_LL, delLLMS, hMod, IntPtr.Zero)

    If kbHookPointer <> Nothing Then

    MessageBox.Show("KB hook Success!")

    Else

    MessageBox.Show("KB hook Failed..")

    End If

    If msHookPointer <> Nothing Then

    MessageBox.Show("Mouse hook Success!")

    Else

    MessageBox.Show("Mouse hook Failed..")

    End If

    End Sub

    ' Unregister global keyboard and mouse hooks

    Private Sub UnHook()

    If kbHookPointer <> Nothing Then

    If UnhookWindowsHookEx(kbHookPointer) Then

    MessageBox.Show("KB Unhook Success!")

    Else

    MessageBox.Show("KB Unhook failed")

    End If

    End If

    If msHookPointer <> Nothing Then

    If UnhookWindowsHookEx(msHookPointer) Then

    MessageBox.Show("Mouse Unhook Success!")

    Else

    MessageBox.Show("Mouse Unhook failed")

    End If

    End If

    End Sub

    ' Callback for Low-Level Mouse Events

    Private Function LowLevelMsCallback(ByVal nCode As Integer, ByVal wParam As WindowMessages, ByVal lParam As MSLLHOOKSTRUCT) As Integer

    Dim lParamPtr As IntPtr = GCHandle.ToIntPtr(GCHandle.Alloc(lParam))

    If nCode < 0 Then

    Return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParamPtr)

    Else

    'TODO: Put code here for handling global mouse events, wParam can be one of the following:

    ' WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_RBUTTONDOWN, or WM_RBUTTONUP

    'lParam contains additional information

    'Make sure to invoke CallNextHookEx

    Return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParamPtr)

    End If

    End Function

    ' Callback for Low-Level Keyboard Events

    Private Function LowLevelKbCallback(ByVal nCode As Integer, ByVal wParam As WindowMessages, ByVal lParam As KBDLLHOOKSTRUCT) As Integer

    Dim lParamPtr As IntPtr = GCHandle.ToIntPtr(GCHandle.Alloc(lParam))

    If nCode < 0 Then

    Return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParamPtr)

    Else

    'TODO: Put code here for handling global low-level keyboard events, wParam can be one of the following:

    'WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, or WM_SYSKEYUP

    'lParam contains additional information

    'Make sure to invoke CallNextHookEx

    Return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParamPtr)

    End If

    End Function

    End Class

    Hope this helps.



  • BrettDerry

    I found this nice and clean C# code to do it. http://www.codeproject.com/csharp/globalhook.asp. Thought I'd share it with you guys. Now I have to figure out a way to get VB.NET to use this C# class. Anybody wanna save me some time looking for the answer online ;) N. Farr.
  • A background process to detect user activity / How to get VB.NET to use a C# class?