שתף באמצעות


Easy way to wait for keypress from user (VB in windows)

Question

Thursday, July 30, 2009 6:03 PM

Hi all,

(Sorry, this should have gone to the VB General forum... - can a moderator move it there or do I need to repost?)

I want to have a one second pause after some code has run.  During that pause, if a certain key is pressed, I want to do some action.  Otherwise, I want the code to continue as normal.

IOW, what would I use to replace the comments in the following code?

myTestCode()
' Wait one second and see if a given key was pressed.
' If specific key is pressed, then show messagebox (or whatever) asking user if we should cancel (whatever)
NextMethodToRun()

Yes, I know there are a number of ways, which is why I am looking for something that is fast and simple to use.  I do not want a console window, some other form, etc. to appear.  Just want to check if a key was pressed.  The key (if pressed) should be pulled off the keyboard buffer

Thanks,

FletcherJ

All replies (29)

Monday, August 3, 2009 9:25 PM ✅Answered | 1 vote

how about this or similar:

Class ErrorClass

     public Sub Main

          MyStep1()

          if CheckDeveloperKey() = true then
               DebugStep1()
               DebugStep2()
          Else
               StandardStep1()
               StandardStep2()
          End If

     End Sub

     Private Function CheckDeveloperKey as Boolean
          
          if CHKDEVKEY.ShowDialog() = DialogResult.Yes then
               Return True
          Else
               Return False
          End If

     End Function

End Class

Class Frm_CheckDevKey Inherits Form

     public Sub New()

          InitializeComponet
          
          'i think we can do this and it will hide the .showdialog form, but if not we just set the form size to 1,1 anyways
          Me.Visible = False

          me.formborderstyle = None
          me.showintaskbar = false
          me.size = new size(1,1)
          me.location = new point(0,0)

          'because i dont know how to return a dialogresult without a button, im doing this:

          Dim B1 as new button
          dim B2 as new button

          b1.dialogresult = dialogresult.Yes
          b2.dialogresult = dialogresult.no

          me.controls.add(b1)
          me.controls.add(b2)

          Dim Timer1 as new Timer
          me.controls.add(Timer1)
          Timer1.Interval = 1100
          Addhandler Timer1.tick, Addressof TimerTickPressesYESButton
          Timer1.Start
 
          Dim Timer2 as new Timer

          Timer2.Interval = 1000
          me.controls.add(Timer2)
          Addhandler Timer2.tick, Addressof TimerTick2PressesNOButton
          Timer2.Start
     End Sub

   
Private Sub TimerTickPressesYESButton
DirectCast(Me.controls("b1"),Button).performClick
End Sub

Private Sub TimerTickPressesNOButton
DirectCast(Me.controls("b2"),Button).performClick
End Sub

Private Sub FormButtonClick("ButtonClickEventArgs Here") Handles me.keypress
if e.key = keys.Enter
DirectCast(me.controls(Timer2),Timer).Stop
End Sub

End Class

Any and All code is provided only as a guide. - learning visual basic


Thursday, August 6, 2009 5:53 AM ✅Answered | 1 vote

FletcherJ

Here is a simple keylogger form this thread:(http://social.msdn.microsoft.com/Forums/en-US/Vsexpressvb/thread/515ca67c-3201-4be1-a08e-72e686cb44cd)

All thats needed on the form is:(you could modified its display mode as console)

Textbox1 = Multiline and vertical scroll bar.

Timer1 set to interval 10 and Enabled

Public Class Keylog1
    Dim result As Integer
    Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer

    Private Sub Keylog1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Timer1.Start()
    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        For i = 1 To 255
            result = 0
            result = GetAsyncKeyState(i)
            If result = -32767 Then
                TextBox1.Text = TextBox1.Text + Chr(i)
            End If
        Next i
    End Sub
End Class

Best wishes
Xingwei Hu

Please remember to mark the replies as answers if they help and unmark them if they provide no help.
Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.


Monday, August 10, 2009 3:32 PM ✅Answered

Xingwei,

This is much better than the forms kludge I used and much more in line with what I wanted.  It doesn't catch the key I like to use as my "break-in" key, but that's not a major problem.  Here is a revised version of your code that doesn't require a timer, etc.

Public Class MyGetKey

    Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer

    Public Shared Function getKey(ByVal seconds As Decimal) As Integer

        Dim intEndTime As Date = Date.Now.AddSeconds(seconds)
        Dim intResult As Integer = 0

        ' Now wait for the desired number of seconds
        Do While Date.Now <= intEndTime
            ' Have they pressed the "End" key?
            If GetAsyncKeyState(35) = -32767 Then
                intResult = 35
                Exit Do
            End If
        Loop

        Return intResult

    End Function
    

End Class

Thanks for pointing me in this direction. 

FletcherJ


Thursday, July 30, 2009 8:36 PM | 1 vote

i tried with a button, textbox, and a timer..
timer interval was set to 1500, to give me extra time to press the Enter key in the textbox.

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        TextBox1.Text = "timer1.enabled"
        Timer1.Enabled = True
    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Dim num As Integer = 0
        Do Until num = 3
            If num = 2 Then
                Timer1.Enabled = False
                TextBox1.Text = " Timer1.Enabled =False "
            End If
            num = num + 1
        Loop
    End Sub

    Private Sub TextBox1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles TextBox1.KeyDown
        If Timer1.Enabled = True Then
            If e.KeyCode = Keys.Enter Then
                Me.Text = "ok"
            End If
        Else
            Me.Text = "not ok"
        End If
    End Sub

End Class

i think it is fairly simple, that i was even able to put the code together.. ;o)
trujade.i like**:** VB General  google  fast cars  username password


Thursday, July 30, 2009 8:51 PM

Trujade,

This would work if I was using a form, but I am not.  I am using a class that has a series of methods that execute, one after another.  After each one, I wait a second for the user to press a key.  If they don't, I just keep going.  Otherwise, I ask the user what they want.

I could do this with threading, but that is more complicated than I need.  All I want is a method that watches the keyboard for one second and either returns null (if no key pressed) or the key (ascii value or as a char) as soon as a key is pressed.  I check the return value to figure out what to do next.

But thanks for the suggestion,

FletcherJ


Thursday, July 30, 2009 10:23 PM

if you are using a class where did you implement the class? Console or Windows Form Application
kaymafI hope this helps, if that is what you want, just mark it as answer so that we can move on


Monday, August 3, 2009 4:17 PM

Kaymaf,

I have a singleton class that is referenced when an error occures.  I display a message to the user letting them know about the error and asking them what they want to do.  After they click on the button, I then wait to see if a special key has been pressed.  If it has, then I will go into a special debug mode (the mode depends on if I am running in debut mode or not) so that I can figure out what happened.

As the dialog may be a messagebox, after the user has responded to the messagebox, I will want to wait to see if the specific key has been pressed. 

All I really need is a simple way to monitor the keyboard for one second, checking for some activity and, if there is any, see what key has been pressed.  I had sort of thougth that there would be an existing function in VB that would do so. 

Thanks,

FletcherJ


Monday, August 3, 2009 6:08 PM | 1 vote

1) the easiest way for your class to gets a key from the keyboard is to have access to a KeyPress event, so have it to inherits from something like Control that will give you this ability

  1. to pose your thread while staying responsive to the keyboard you can use a loop similar to this

(This give you a +-5 seconds waiting time, but if the key is pressed, the application will continue immediately)

        Dim Key As String ' This is set by the KeyPress event
        Dim counter As Integer = 0
        Do
            counter += 1
            Threading.Thread.Sleep(100)
            Application.DoEvents()
            If Key = "5" Then ' Or whatever you want
                Exit Do
            End If
        Loop Until counter = 50

Monday, August 3, 2009 6:19 PM

Crazypennie,

This certainly will work and I have used a approach like this in some other OOP languages.  But doesn't VB.NET allow me to define a keypress event and add a handler to any class? 

What I was really looking for was something like "if keypress( 1000 ) = 27 then "  which would wait 1000 ms to see if an esc key was pressed.

But your response is the closest to an answer I have had so far, thank!

FletcherJ


Monday, August 3, 2009 7:08 PM | 1 vote

You can just do it like that

This will be do the same as above and will return Pressed key if the key was pressed and return "" if no key was pressed

    Dim Key As String = "" ' This is set by the KeyPress event
    Private Function KeyP(ByVal TimeOut As Integer) As String

        Dim counter As Integer = 0
        Do
            counter += 1
            Threading.Thread.Sleep(100)
            Application.DoEvents()
            If Key <> "" Then ' Or whatever you want
                Return Key
            End If
        Loop Until counter > TimeOut / 100
        Return ""

    End Function

now, you can use

 If KeyP(1000)="B" then

Monday, August 3, 2009 7:12 PM

Instead of using loop, you should use timer.

Its quite possible to achieve exactly like you want if you use your own form to display message than standard MsgBox.

and Flow of your code should be Like

Sub work()
  'do work
  ShowCustomFormAsMessageBox()
End Sub

Sub CustomFrom_formLoad()
  aglobalBooleanInForm=True
  Timer1.Start()
  'Timer1 should have interval set to 1000ms
End Sub

Timer1_Tick()
  Timer1.Stop
  globalBooleanInForm=False
  continueWork()
End Sub

keyPressEventOfCustomForm()
 if GlobalBoolean=true then
   check key using passed eventargs
   Show Debug Window
   Stop Timer1 etc..
 End IF
End Sub

Sub ContinueWork()
 'work to do if key wasnt pressed
end Sub


Thanks
-@omkarnath

May the force be with you


Monday, August 3, 2009 7:21 PM

Omie,

I appreciate your code, but the goal of the question is to find a way to just see if a key on the keyboard has been pressed, without having to use a form, console window, or control.

Apparently, it is much more difficult than I thought. 

From what I have found, there is no way to just check the keyboard for a key unless some type of data entry element exists and is visible to the user. 

Essentially, console.in.peek would do the job, if it worked without showing a console screen.  But I can't even get that to work as desired.

And while your code works, the form needs to have focus for it to work.  So I would have to somehow move it off the screen first (so the user couldn't see it) and then have the timer check it for a value. 

Thanks,

Fletcher


Monday, August 3, 2009 7:26 PM

CrazyPennie,

Ok, I have a class based on UserControl.  But I can't get it to have focus enough so that the keypress event will fire.  I try to set focus to the control, added a textbox and set focus to it, etc. but just can't get the keypress event to fire.  So your loop always returns a blank, no matter what keys I press.

Any ideas?

Thanks,

FletcherJ


Monday, August 3, 2009 7:27 PM

The goal of the question is to find a way to just see if a key on the keyboard has been pressed, without having to use a form, console window, or control.

if you are using a class where did you implement the class? Console or Windows Form Application
-kaymaf

I'm just wondering... your application works ? How ??

Thanks
-@omkarnath

May the force be with you


Monday, August 3, 2009 7:49 PM

Fletcher,

I need to know what kind of application you are using,

I First assume that you were using a Window form application, started in the Main method and that you were not showing any windows,

is this right?


Monday, August 3, 2009 7:49 PM

Omie,

This is a windows based application.  It has forms. 

One example:

There is a global error handler that catches unhandled exceptions.  This can fire whereever there is a problem.  I catch the error and have a series of steps that are performed.  I may want to be able to (as a developer) break into any of the steps.  So I want the ability to check the keyboard to see if a special key is pressed.  If so, instead of doing anything further, the application will go into a special diagnostics mode that only a developer would find usefull.  Otherwise, the user sees a standard interface.  The decision needs to be made before the interface is shown as, in some cases, I don't want the interface shown.

There are other places where I could use this, but this gives you the basic idea of why I can't assume that there is a form available, etc.

So, back to my question.  How do I, without displaying anything to the user, get the program to just look to the keyboard for one second, checking for a special key.  Then, after the second, return either a 0 value or the value of the key pressed?

Thanks,

FletcherJ


Monday, August 3, 2009 7:49 PM | 1 vote

...  I display a message to the user letting them know about the error and asking them what they want to do.  After they click on the button, I then wait to see if a special key has been pressed. ...

How are you displaying that message?  Where is that button?  Are those on a form?  If so, THAT form should be able to use the keypress handler somewhere, even if that form was constructed in code and not visually.

If you don't want the form hanging around there while the user has the key option, you can hide it instead of closing it.  Then close it when you get the key to process, or right before you move on to the next item in your list of error processing.  (eg, hide the form on the button click, close the form it as part of the timer tick.)


Monday, August 3, 2009 7:53 PM

Omie

If he has no console and no window, He has to sleep his thread, Otherwise, the thread will reach the end of the main method and the application will terminate


Monday, August 3, 2009 7:55 PM

CRB042,

Suppose I am using a Messagebox.show to display the message? 

This is the problem with giving examples.  Yes, I can use a form.  But that approach has it's limitations for some purposes.

All I want is the ability to see if a key has been pressed in the keyboard - without a form or other visible interface being visible to the user.  Is this possible?  If so, if the special key is pressed, can I then remove it from the keyboard?

Thanks,

FletcherJ


Monday, August 3, 2009 8:10 PM

Some error occurred
You keep it secret for 1 sec and in this 1 sec you want to check if  special key was pressed.
if yes then show Special Debug Window
Else So simple msgbox and continue

is it so ?

( if it is so, you know when will you get error and when program waits for listening to that special keystroke ? )

Thanks
-@omkarnath

May the force be with you


Monday, August 3, 2009 8:33 PM

Fletcher,

If you have no interface at all, WinProc will not receive any messages from Windows, So, No keys can be read

Usualy, to create an application with no user interface, you still create a window that you set "Visible=false"

Without that, as I said, you will not get any message for windows


Monday, August 3, 2009 8:35 PM

Suppose I am using a Messagebox.show to display the message? 

Suppose you are.  Change it to instancing a form with the keyboard trap code built in to it.  The user won't know the difference.  You can even have the form use a property of type DialogResult to pass a return value that looks like it came from a messagebox.


Tuesday, August 4, 2009 6:56 PM

Hi all,

Ok, I finally gave up and used what I consider to be a kludge.  At some point, I will see what I can find out about writing a routine in C++ or some such that will allow me to look at the keyboard buffer for keys.  But for now, I just want to get this done.

So I created a form, with a textbox and timer on it.  I have a second object that has a shared method that does the following:

instantiates the form (with a location of -1000,-1000 so the user doesn't see it), passes it a object variable that can be filled when the form closes, then uses the showDialog method so that the form keeps focus.

If a key is presssed or the timer runs out of time, they close the form.  In the forms FormClosing method, it updates the object variable passed earlier and closes.

The shared method then gets control again and simply returns the value in the object variable that was passed to the form.

Not elegant or clean, but it does the job and I can now get on with mine. 

Thanks again for all your help and suggestions. 

Thanks,

FletcherJ


Tuesday, August 4, 2009 9:25 PM | 1 vote

fletcher, i'm not 100% sure of this, but if a user has 4 monitors (Configured in a square, with the Top Left Monitor being the Main Monitor) it may show your form on the bottom right.

Any and All code is provided only as a guide. - learning visual basic


Tuesday, August 4, 2009 11:14 PM

yes, best to set the forms visible state to false, or use Me.Hide() on the load event, or something similar, so the user just can't at all see it.


Wednesday, August 5, 2009 3:48 PM

jwalker343,

I will test this out, but I was under the impression that a negative number was left (or up) and a positive number was down/right.  On the other hand, given your scenario, if the bottom right monitor was the primary, then the upper left could represent a negative location and, therefore, show the form.

I guess I could (if VB allows) get the upper left location of all active monitors and subtract 1000 from that.... But I am not sure if this is really worth the effort as such a layout would be quite confusing and I don't believe anyone here would have such a layout.

But, it's worth testing. 

Thanks for the heads up,

Fletcher


Wednesday, August 5, 2009 3:50 PM

Give it an insane number like 99999999, then on startup of the form use Me.Hide() this will ensure nobody ever sees it unless they are stupidly stupid.


Wednesday, August 5, 2009 3:51 PM

Michael,

I will test this as well, but I was under the impression that if a form (or control) was invisible, then it couldn't get focus.  I need it to be able to have focus so that it can trap the keypress.

But, as with jwalkers comment, it's worth testing - if for no other reason than that I don't, at some future time, make a forms visible property false and assume that it can't get focus.

Take care,

FletcherJ


Wednesday, August 5, 2009 4:13 PM | 1 vote

A control doesn't need fovus for keypresses, the main way to get a working xample is to search for VB Keylogger, it wont be detected by a virus scanner, because it's essentially not really, and, it will help you with your issue.