msgbartop
A site for my programming pet projects
msgbarbottom

20 Jun 09 Instant Rimshot

It’s been a while since I’ve written anything here, I’ve been working on my master project like crazy, have a 6 month old baby and so haven’t had much time for programming pet projects. Tonight I spent a half hour making perhaps the most useless application I’ll ever make. It’s based on the single-serving website http://instantrimshot.com which only has one huge red button, if you press it a rimshot sound is played. I thought it might be fun to have that available as a keyboard shortcut in windows, and then I started wondering how it could be done in C#.

The only way to get a global keyboard working in .NET is by calling into the Win32 API. To register a global hotkey there is the aptly named function RegisterHotKey. That requires you to have a window handle however, and I wanted to make the application as simple as possible, so I used a lower level API call,
SetWindowsHookEx which lets you get notified of all keyboard activity on the system. I then check what keys are pressed and when the combination Ctrl+Shift+I is pressed the sound is played. There is only one way to exit the app, and that is by pressing the keyboard combination Ctrl+Shift+Alt+I, that cleans up the hook before exiting. So, the code is shown below, you can download the source or just download the program itself. Enjoy!

using System;
using System.IO;
using System.Media;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;

class InstantRimshot {

    const int WH_KEYBOARD_LL = 13;
    const int WM_KEYDOWN = 0x100;

    delegate int Hook(int code, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll")]
    static extern IntPtr SetWindowsHookEx(int code,
                                          Hook func,
                                          IntPtr hInstance,
                                          int threadID);

    [DllImport("user32.dll")]
    static extern int UnhookWindowsHookEx(IntPtr hookHandle);

    [DllImport("user32.dll")]
    static extern int CallNextHookEx(IntPtr hhook,
                                     int code,
                                     IntPtr wParam,
                                     IntPtr lParam);

    static bool IsPressed(Keys check) {
        return ((Control.ModifierKeys & check) == check);
    }

    static void Main() {

        Stream sound = Assembly.GetExecutingAssembly()
            .GetManifestResourceStream("InstantRimshot.rimshot.wav");
        SoundPlayer player = new SoundPlayer(sound);
        player.Load();
        IntPtr hookHandle = IntPtr.Zero;
        hookHandle = SetWindowsHookEx(WH_KEYBOARD_LL,
            delegate(int code, IntPtr wParam, IntPtr lParam) {
                if (code >= 0 && wParam == (IntPtr) WM_KEYDOWN) {
                    Keys vkCode = (Keys)Marshal.ReadInt32(lParam);
                    if (Keys.I == vkCode
                    && IsPressed(Keys.Control)
                    && IsPressed(Keys.Shift)) {
                        if (IsPressed(Keys.Alt)) {
                            UnhookWindowsHookEx(hookHandle);
                            Application.Exit();
                            return CallNextHookEx(hookHandle, code, wParam, lParam);
                        } else {
                            player.Play();
                        }
                    }
                }
                return CallNextHookEx(hookHandle, code, wParam, lParam);
            }, IntPtr.Zero, 0);
        Application.Run();
    }

}

Leave a Comment