0 Comments

Problema

Scrivo questo articolo in quanto mi è stato chiesto di gestire per un’applicazione windows un blocco della stessa se l’utente non la stà usando da 15 minuti. Praticamente una gestione di Session timeout per un’applicazione windows.


Session Timeout

Per un’applicazione WEB la gestione di un timeout della sessione rappresenta l'evento che si verifica quando un utente non esegue alcuna azione su un sito Web durante un intervallo (definito dal server Web). L'evento, sul lato server, modifica lo stato della sessione utente in "non valido" (ovvero "non più utilizzato"). Tutto questo è gestito in modo trasparente per il developer.


Windows App

Per un’applicazione windows possiamo raggiungere lo scopo con un pò più di fatica senza dover per forza scrivere codice in tutti i punti dell’applicazione dove l’utente interagisce con essa (menu, textbox, listbox ecc ecc)

API Windows

L’idea è quella di sfruttare le API di windows che intercettano i messaggi inviati dall’utente attraverso tastiera e mouse e riconoscere che questi messaggi sono inviati alla mia applicazione.

Dopo un pò di ricerche e tentativi trovo che la SetWindowsHookExdella libreria user32 è quella che soddisfa i miei requisiti.

Implementazione

A questo punto implemento il tutto su un progetto di prova per non toccare ancora il mostro.

Nel progetto inserisco un formmain con una serie di menu (simulo l’applicativo)

inserisco 3 form richiamabili dal menu con oggetti di vario genere buttati dentro a caso con lo scopo di simulare operazioni dell’utente.

Inserisco un form di nome frmSetWindowsHookEx con la funzione di apparire in modalità modale solo quando è raggiunto il timeout.

Un modulo per la gestione api.

Nel form principale dell’applicazione inserisco queste righe di codice nel load:

frmSetWindowsHookEx.InizializzaLocked

Sempre form principale dell’applicazione inserisco queste righe di codice nel unload:

RemoveKeyboardHook

Nel form di nome frmSetWindowsHookEx inserisco questo codice.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
Option Explicit

Private activitydate As Date

Public Sub resetTimer()

'richiamata da ogni attività dell’utente sull’applicazione

    activitydate = Now

End Sub

Private Sub cmdAbilita_Click()

   SetKeyboardHook

End Sub

Private Sub Command1_Click()


'avviso all’utente dell’inattività e che è negessario ri

    RemoveKeyboardHook

'al posto della end si può far apparire il form del login dell’applicazione se c’è
    End

End Sub

Public Sub InizializzaLocked()
    SetKeyboardHook
    activitydate = Now
    Timer1.Enabled = True
End Sub

Private Sub Timer1_Timer()
    If DateDiff("n", activitydate, Now()) >= MINLOCKED Then
        Label1.Caption = Label1.Caption & " " & MINLOCKED & " MINUTI"
        Me.Show vbModal
    End If

End Sub

e nel modulo della gestione dell’api.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
Option Explicit
 
Global Const MINLOCKED = 1
 
Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" ( _
    ByVal idHook As Long, _
    ByVal lpfn As Long, _
    ByVal hmod As Long, _
    ByVal dwThreadId As Long _
) As Long


Private Type POINTAPI
        x As Long
        y As Long
End Type

Private Declare Function UnhookWindowsHookEx Lib "user32" ( _
    ByVal hHook As Long _
) As Long
 
Private Declare Function CallNextHookEx Lib "user32" ( _
    ByVal hHook As Long, _
    ByVal nCode As Long, _
    ByVal wParam As Long, _
    lParam As Any _
) As Long

Public Const WH_KEYBOARD = 2
Public Const WH_MOUSE = 7

Private Const HC_ACTION = 0
 
 
Public Type KBDLLHOOKSTRUCT
    vkCode As Long
    scanCode As Long
    flags As Long
    time As Long
    dwExtraInfo As Long
End Type
 
 Private Const WH_GETMESSAGE = 3
 
Private hHookKeyboard As Long
Private hHookMouse As Long
Public IsHooked As Boolean
 
Public Sub SetKeyboardHook()
    
    If IsHooked Then
    Else
        hHookMouse = SetWindowsHookEx(WH_MOUSE, AddressOf LowLevelKeyboardProc, App.hInstance, App.ThreadID)
        hHookKeyboard = SetWindowsHookEx(WH_KEYBOARD, AddressOf LowLevelKeyboardProc, App.hInstance, App.ThreadID)
        IsHooked = True
    End If

End Sub
 
Public Sub RemoveKeyboardHook()
    If IsHooked Then
        UnhookWindowsHookEx hHookKeyboard
        UnhookWindowsHookEx hHookMouse
        IsHooked = False
    End If
End Sub
 
Public Function LowLevelKeyboardProc(ByVal uCode As Long, ByVal wParam As Long, lParam As KBDLLHOOKSTRUCT) As Long
    If uCode >= 0 Then
        Select Case uCode
            Case HC_ACTION
               frmSetWindowsHookEx.resetTimer
        End Select
        LowLevelKeyboardProc = CallNextHookEx(hHookMouse, uCode, wParam, lParam)
    Else
        LowLevelKeyboardProc = CallNextHookEx(hHookMouse, uCode, wParam, lParam)
    End If
        
End Function


Comodo! Non devo scrivere codice ripetitivo in numerosi punti dell’applicazione e non devo preoccuparmi di inserire codice per la gestione del timeout anche nelle nuove funzionalità che ci potranno essere nel futuro di questa applicazione.