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.

No comments: