Tuesday, February 8, 2011

Is there .Net replacement for GetAsyncKeyState?

In VB6, I used a call to the Windows API, GetAsyncKeyState, to determine if the user has hit the ESC key to allow them to exit out of a long running loop.

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

Is there an equivalent in pure .NET that does require a direct call to the API?

  • You can find the P/Invoke declaration for GetAsyncKeyState from http://pinvoke.net/default.aspx/user32/GetAsyncKeyState.html

    Here's the C# signature for example:

    [DllImport("user32.dll")]
    static extern short GetAsyncKeyState(int vKey);
    
  • Depending on your desired use there are a couple of options, including invoking the same method as described above). From a console app:

    bool exitLoop = false;
    for(int i=0;i<bigNumber && !exitLoop;i++)
    {
        // Do Stuff.
        if(Console.KeyAvailable)
        {
         // Read the key and display it (false to hide it)
         ConsoleKeyInfo key = Console.ReadKey(true);
         if(ConsoleKey.Escape == key.Key)
         {
          exitLoop=false;
         }
        }
    }
    

    If you are working on a windows form, every form has a number of key related events you can listen to and handle as necessary (Simplified most of the logic):

    public partial class Form1 : Form
    {
     private bool exitLoop;
     public Form1()
     {
      InitializeComponent();
      this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyUp);
     }
     public void doSomething()
     {
      // reset our exit flag:
      this.exitLoop = false;
      System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(delegate(object notUsed)
       {
        while (!exitLoop)
        {
         // Do something
        }
       }));
     }
     private void Form1_KeyUp(object sender, KeyEventArgs e)
     {
      if (Keys.Escape == e.KeyCode)
      {
       e.Handled = true;
       this.exitLoop = true;
      }
     }
    
    }
    

    Note that this is very simplified - it doesn't handle any of the usual threading issues or anything like that. As was pointed out in the comments, the original go-round didn't address that problem, I added a quick little ThreadPool call to thread the background work. Also note, that the problem with listening for the key events is that other controls may actually handle them, so you need to make sure that you register for the event on the correct control(s). If a windows form application is the direction you are heading, you can also attempt to inject yourself into the message loop itself...

    public override bool PreProcessMessage(ref Message msg)
    {
      // Handle the message or pass it to the default handler...
      base.PreProcessMessage(msg);
    }
    
    Shane : I don't think this is going to work because the KeyUp event is not going to fire while "doSomething" loop is running.
    From Brian B.

0 comments:

Post a Comment