Showing posts with label P/Invoke. Show all posts
Showing posts with label P/Invoke. Show all posts

Sunday, May 24, 2009

When KeyPressed / KeyDown just isn't enough. An adventure in GetKeyboardState.

Source Code: http://www.box.net/shared/y7v5jskfd1

A question on StackOverflow this weekend, piqued my interest. It was about some guy that was creating a Tetris game in C# (using Winforms) and was having issues with keyboard input. Basically, he was hooking into the Forms KeyDown event, and if the key was either left down or right, he'd move the tetris piece accordingly. The problem he was running into was if the user would hold down more than one key at a time (which is very common when playing Tetris or any other game for that matter), only one key would register.

Having messed with XNA in the past (I still hope to release a game to the XBOX Community games in this life time :-P ), I had a hunch. In XNA, things work a little different. A little background on how games work in general. All games consist of a "Game Loop". Basically, the entire application runs in one gigantic loop. You have a timer that constantly runs in the background, and every time the timer "ticks" two things happen. Update() is called, and Draw() is called. During the update routine, you update your game logic, ie: move player's position, move enemies position etc.. Then, during the Draw routine, you draw everything to screen.

It is during this Update routine, where you "poll" the keyboard (or gamepad when working on the XBOX 360) to find out what's going on. Here's a small sample:

56 var state = Keyboard.GetState();

57 bool downPressed = state.IsKeyDown(Keys.Down);


As you can see, you "ask" the keyboard if the Down key is pressed at the current time. The cool thing about this method is that you can "ask" for multiple keys in one trip. There's nothing stopping you from doing this:

56 var state = Keyboard.GetState();

57 bool downPressed = state.IsKeyDown(Keys.Down);

58 bool upPressed = state.IsKeyDown(Keys.Up);



In Windows Forms though, if you listen for the KeyDown event, the KeyEventArgs will give you the Keycode, but only of ONE key. So, if more than one key is pressed, what do you do?

After some searching, I found that there's a "GetKeyboardState" function that's part of the Win32 API. We should be able to P/Invoke this function, and poll the keyboard for more than one key. According to the Microsoft documentation, you pass in the int value of the key, and you get back a short.

The return value specifies the status of the specified virtual key, as follows:

  • If the high-order bit is 1, the key is down; otherwise, it is up.
  • If the low-order bit is 1, the key is toggled. A key, such as the CAPS LOCK key, is toggled if it is turned on. The key is off and untoggled if the low-order bit is 0. A toggle key's indicator light (if any) on the keyboard will be on when the key is toggled, and off when the key is untoggled.
So, I found this helpful little class online that wraps this API call nicely.

First, a simple struct to hold the Key's state:



public struct KeyStateInfo
{
private Keys key;
private bool isPressed;
private bool isToggled;

public KeyStateInfo(Keys key, bool ispressed, bool istoggled)
{
this.key = key;
isPressed = ispressed;
isToggled = istoggled;
}

public static KeyStateInfo Default
{
get
{
return new KeyStateInfo(Keys.None, false, false);
}
}

public Keys Key
{
get { return key; }
}

public bool IsPressed
{
get { return isPressed; }
}

public bool IsToggled
{
get { return isToggled; }
}
}

Then, here's the actual class that wraps the P/Invoke:



using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

public class KeyboardInfo
{
private KeyboardInfo() { }
[DllImport("user32")]
private static extern short GetKeyState(int vKey);

public static KeyStateInfo GetKeyState(Keys key)
{
short keyState = GetKeyState((int)key);
int low = Low(keyState), high = High(keyState);
bool toggled = low == 1;
bool pressed = high == 1;
return new KeyStateInfo(key, pressed, toggled);
}
private static int High(int keyState)
{
return keyState > 0 ? keyState >> 0x10
: (keyState >> 0x10) & 0x1;
}
private static int Low(int keyState)
{
return keyState & 0xffff;
}
}


Simple enough. To prove that this works now with more than one key, I wrote a simple windows app that moves a ball around the form based on the user's pressing of the arrow keys. You'll notice, that if you press two arrows at once, it will move the ball diagonally, proving that it accepts more than one key at a time. Here's the code:

First, I created a simple Ball class:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace WindowsFormsApplication1
{
public class Ball
{
private Brush brush;

public float X { get; set; }
public float Y { get; set; }
public float DX { get; set; }
public float DY { get; set; }
public Color Color { get; set; }
public float Size { get; set; }

public void Draw(Graphics g)
{
if (this.brush == null)
{
this.brush = new SolidBrush(this.Color);
}
g.FillEllipse(this.brush, X, Y, Size, Size);
}

public void MoveRight()
{
this.X += DX;
}

public void MoveLeft()
{
this.X -= this.DX;
}

public void MoveUp()
{
this.Y -= this.DY;
}

public void MoveDown()
{
this.Y += this.DY;
}
}

}


This class basically holds the coordinates of the ball, and the "velocity" (DX, and DY), or the speed at which the ball will move each time the key is pressed. It also holds the color and size.

Then, here's the main Form code:



using System;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private Ball ball;
private Timer timer;
public Form1()
{
InitializeComponent();
this.ball = new Ball
{
X = 10f,
Y = 10f,
DX = 2f,
DY = 2f,
Color = Color.Red,
Size = 10f
};
this.timer = new Timer();
timer.Interval = 20;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}

void timer_Tick(object sender, EventArgs e)
{
var left = KeyboardInfo.GetKeyState(Keys.Left);
var right = KeyboardInfo.GetKeyState(Keys.Right);
var up = KeyboardInfo.GetKeyState(Keys.Up);
var down = KeyboardInfo.GetKeyState(Keys.Down);

if (left.IsPressed)
{
ball.MoveLeft();
this.Invalidate();
}

if (right.IsPressed)
{
ball.MoveRight();
this.Invalidate();
}

if (up.IsPressed)
{
ball.MoveUp();
this.Invalidate();
}

if (down.IsPressed)
{
ball.MoveDown();
this.Invalidate();
}


}


protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (this.ball != null)
{
this.ball.Draw(e.Graphics);
}
}
}


}

First, I create a red ball that's 10 pixels in size. Then, I create a Timer which will "tick" every 20 milliseconds. This is simple to simulate a game loop. It won't actually "Draw" every tick, it will only ever redraw if one of the arrow keys is pressed. The event handler for the Timer is where the meat of this lies:


void timer_Tick(object sender, EventArgs e)
{
var left = KeyboardInfo.GetKeyState(Keys.Left);
var right = KeyboardInfo.GetKeyState(Keys.Right);
var up = KeyboardInfo.GetKeyState(Keys.Up);
var down = KeyboardInfo.GetKeyState(Keys.Down);

if (left.IsPressed)
{
ball.MoveLeft();
this.Invalidate();
}

if (right.IsPressed)
{
ball.MoveRight();
this.Invalidate();
}

if (up.IsPressed)
{
ball.MoveUp();
this.Invalidate();
}

if (down.IsPressed)
{
ball.MoveDown();
this.Invalidate();
}
}

First, we "poll" the keyboard for the arrows keys. We poll for all four of them, therefore if more than one is pressed, we'll be able to react to all of them. Then, if either key is held down, we move the ball in that direction and call Invalidate() to allow the form to repaint itself.

One final thing I'd like to point out, is that with the standard KeyDown event in Windows Forms, you can get "modifiers" (Shift, Ctrl, Alt etc.) as well as the key that was pressed. So if you're writing an app where you want to have some shortcut like say CTRL + A, you don't need to do this. This is only when you want to get info on more than one standard key................Which is why, you'll probably never need this in a real app outside of games (in which case, you're better off with XNA) but it's still something that's good to know.

Monday, March 23, 2009

Calling unmanaged API's from within your C# app using P/Invoke.

While the .NET Framework is HUGE and you generally can find anything you need to do, there are often times when you need to call unmanaged API's from within your code. For example, there are many great functions in the Windows API that simply have no wrapper or equivalent in the Framework. Or, say if you have a fully working and tested library that was written in unmanaged code (like C/C++) why rewrite the entire thing? Why not call into the existing library? That's where the idea of P/Invoke (Platform Invoke) comes into play. I'll demonstrate with a few examples:

The first one I'll show is actually a very simple one, but actually extremely useful. Many times when working in WinForms apps, I find myself needing a Console for debugging. It's much easier to do Console.Writeline's when there's an actual Console, rather than having to look in the tiny Output window in VS. Here's how you can do it. First, create a simple WindowsForm application and add a button on to the form. Then add these few lines of code:

        [DllImport("kernel32.dll")]
        public static extern bool AllocConsole();
 
        [DllImport("kernel32.dll")]
        public static extern bool FreeConsole();

You'll have to add this using statement at the top of your class:

using System.Runtime.InteropServices;

The "DllImport" attribute tells the CLR that this function lives outside the managed world. You then specify the dll where this function lives. You then just need to specify the function name and provide the signature. Here's what the actual AllocConole method looks like in the windows API:

C++
BOOL WINAPI AllocConsole(void);

So as long as you use the same name, it can map the function. You also need to make the method static, and mark it as extern.

Now, you can just call it like any other method. Add this code to the button1 click:

        private void button1_Click(object sender, EventArgs e)
        {
            AllocConsole();
            Console.WriteLine("Writing this to a console from a Windows App.");
        }

Run the program and click the button. You'll see a console window show up, and our message will be written to the console. The console will stick around though, as we never told it to go away. Add another button to your form and put this code in there:

        private void button2_Click(object sender, EventArgs e)
        {
            Console.WriteLine("Press any key to close the console.");
            Console.ReadKey(true);
            FreeConsole();
        }

Run it again, click the first button, then click the second. After you hit a key on your keyboard, the console will disappear. I'm only demonstrating this to give you an intro to P/Invoke, but you'll see, this AllocConsole stuff is actually real useful for debugging Windows Apps.

Now let's move on to another one that's a bit more complicated. In the C/C++ world, very often you'll find that functions take Structs instead of a list of parameters. I'm not a C++ guy myself, so don't ask me why, but that's the way it is. Therefore, in order to P/Invoke such a method, you need to recreate that struct in your C# app. Example:

If you've ever used any chatting program, you'll notice that when the chat window is minimized, if you receive an instant message, the minimized window in your taskbar starts to blink. There doesn't exist a way to do this in .NET unfortunately, therefore we will have to P/Invoke it. The Windows Function for this is called: FlashWindowEx. Now, if you click on that link, you'll notice that the signature for that method is:

BOOL WINAPI FlashWindowEx(

__in PFLASHWINFO pfwi
);
What on earth is _in and what's PFLASHWINFO??? Well, here's where it gets interesting. "_in" means that the parameter being passed in is actually a pointer to the parameter. Secondly, the PFLASHWINFO is a struct. On that page, you'll notice that in the documentation the PFLASHWINFO is a link that takes you to this page, which shows this:

typedef struct {

UINT cbSize;
HWND hwnd;
DWORD dwFlags;
UINT uCount;
DWORD dwTimeout;
} FLASHWINFO, *PFLASHWINFO;
Ok, we're getting somewhere, but looking at that list, (not being a C++ programmer) I can figure out UINT because we have uint in C#, but what's HWND? What's DWORD? How can I create a struct in C# if I don't know those datatypes? That's where you'll need this page. It's a great article on Code Project which has a huge list about halfway down that maps all the C++ data types to C#.

So, looking at that page, we can map everything we need. DWORD is a uint, and HWND is an IntPtr. (IntPtr is a .NET struct that basically represents a pointer. It's used very often when P/Invoking methods. For more info, see this article.)

So, let's create our struct:

    public struct FLASHWINFO
    {
        public uint cbSize;
        public IntPtr hwnd;
        public uint dwFlags;
        public uint uCount;
        public uint dwTimeout;
    }


Ok, we're getting somewhere. We have the struct, but remember, a POINTER to this struct was used to call the Windows API. Well, structs are valuetypes, so by passing a struct using the "ref" keyword, you're essentially doing the same thing. Therefore, we can now add this line to the top of our form:

        [DllImport("User32.dll")]
        public static extern bool FlashWindowEx(ref FLASHWINFO pfwi);

You'll notice that this time I'm calling User32.dll. How did I know that? Well, if you refer back to the original MSDN page that had the FlashWindowEx function declaration, you'll notice at the bottom of the page it says: DLL: User32.dll.

OK, so we set up the method and the P/Invoke, but how do we actually use this now? Let's add a third button to our Form. The basic premise will be when this button will be clicked, the window will minimize. Then, after about a second, i'll start blinking.

So, let's add a Flash method to our form:

        

        private void Flash()
        {
            FLASHWINFO pwfi = new FLASHWINFO();
            //assign the windows handle of our Form
            pwfi.hwnd = this.Handle;
            //size of the struct. Needed for the unmanaged world to allocated correct number of bytes
            pwfi.cbSize = Convert.ToUInt32(Marshal.SizeOf(typeof(FLASHWINFO)));
            //number of times to blink. We want max possible
            pwfi.uCount = uint.MaxValue;
            //the flash status. 2 = taskbar only
            pwfi.dwFlags = 2;
 
            FlashWindowEx(ref pwfi);
        }


First, look at thist page again. That explains what all the values in the struct represent. This way, we can know which values to assign to all the properties in our struct.

            FLASHWINFO pwfi = new FLASHWINFO();
            //assign the windows handle of our Form
            pwfi.hwnd = this.Handle;


So first we create an instance of the struct. Then, in Windows Programming, every Window has something that's called a "Handle". Here's a good explanation:

A handle is a reference for the operating system. It does not have the semantics of a programming reference but what it does do is allow the system resources to know what you are referring to when it is passed in an API call.
So, in .NET every Form has a Handle property that returns an IntPtr.

//size of the struct. Needed for the unmanaged world to allocated correct number of bytes
            pwfi.cbSize = Convert.ToUInt32(Marshal.SizeOf(typeof(FLASHWINFO)));


The next line is needed for the unmanaged world to know how many bytes to allocated for our struct. The Marshal class has alot of helper methods to interact with the unmanaged world.

//number of times to blink. We want max possible
            pwfi.uCount = uint.MaxValue;


The next piece is the uCount. This represents that number of times the icon should blink. We'll just assign it to the max possible. Our intention is to have it blink until the user brings the window back into view.

//the flash status. 2 = taskbar only
            pwfi.dwFlags = 2;


The next property is the dwFlags which according to MSDN is the flash status. If you look at that page you'll see that these are the options:

ValueMeaning
FLASHW_ALL
0x00000003

Flash both the window caption and taskbar button. This is equivalent to setting the FLASHW_CAPTION | FLASHW_TRAY flags.

FLASHW_CAPTION
0x00000001

Flash the window caption.

FLASHW_STOP
0

Stop flashing. The system restores the window to its original state.

FLASHW_TIMER
0x00000004

Flash continuously, until the FLASHW_STOP flag is set.

FLASHW_TIMERNOFG
0x0000000C

Flash continuously until the window comes to the foreground.

FLASHW_TRAY
0x00000002

Flash the taskbar button.

In our case we want just the icon to flash just in the tray. Therefore we set the value to 2.

FlashWindowEx(ref pwfi);

Finally, we just call the method.

At this point, the form will continue to blink. Therefore, we need a way to stop it. Let's create a StopFlashing method:

        private void StopFlashing()
        {
            FLASHWINFO pwfi = new FLASHWINFO();
            pwfi.hwnd = this.Handle;
            pwfi.cbSize = Convert.ToUInt32(Marshal.SizeOf(typeof(FLASHWINFO)));
            pwfi.uCount = uint.MaxValue;
            pwfi.dwFlags = 0;
 
            FlashWindowEx(ref pwfi);
        }

Really the same as before except the dwFlags is set to 0 which means stop. So, now here's the entired Form code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
 
namespace PInvokeTesting
{
    public struct FLASHWINFO
    {
        public uint cbSize;
        public IntPtr hwnd;
        public uint dwFlags;
        public uint uCount;
        public uint dwTimeout;
    }
 
    public partial class Form13 : Form
    {
        [DllImport("kernel32.dll")]
        public static extern bool AllocConsole();
 
        [DllImport("kernel32.dll")]
        public static extern bool FreeConsole();
 
        [DllImport("User32.dll")]
        public static extern bool FlashWindowEx(ref FLASHWINFO pfwi);
 
        public Form13()
        {
            InitializeComponent();
            this.Activated += new System.EventHandler(this.Form13_Activated);
        }
 
        public void Form13_Activated(object sender, EventArgs e)
        {
            this.StopFlashing();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            AllocConsole();
            Console.WriteLine("Writing this to a console from a Windows App.");
        }
 
        private void button2_Click(object sender, EventArgs e)
        {
            Console.WriteLine("Press any key to close the console.");
            Console.ReadKey(true);
            FreeConsole();
        }
 
        private void button3_Click(object sender, EventArgs e)
        {
            this.WindowState = FormWindowState.Minimized;
            this.Flash();
        }
 
        private void Flash()
        {
            FLASHWINFO pwfi = new FLASHWINFO();
            //assign the windows handle of our Form
            pwfi.hwnd = this.Handle;
            //size of the struct. Needed for the unmanaged world to allocated correct number of bytes
            pwfi.cbSize = Convert.ToUInt32(Marshal.SizeOf(typeof(FLASHWINFO)));
            //number of times to blink. We want max possible
            pwfi.uCount = uint.MaxValue;
            //the flash status. 2 = taskbar only
            pwfi.dwFlags = 2;
 
            FlashWindowEx(ref pwfi);
        }
 
        private void StopFlashing()
        {
            FLASHWINFO pwfi = new FLASHWINFO();
            pwfi.hwnd = this.Handle;
            pwfi.cbSize = Convert.ToUInt32(Marshal.SizeOf(typeof(FLASHWINFO)));
            pwfi.uCount = uint.MaxValue;
            pwfi.dwFlags = 0;
 
            FlashWindowEx(ref pwfi);
        }
 
    }
}

Run the program and click the button3. You'll see the window minimize, and start blinking. As soon as you bring it back up, it'll stop flashing.

This is just the tip of the iceberg with P/Invoke, but hopefully this gave you some idea as to what can be done.