C# - Globális gyorsbillentyű létrehozása

forráskód letöltése
A Windows-ban definiálhatunk olyan gyorsbillentyűket, melyek az egész operációs rendszerre nézve globálisak, vagyis az adott billentyű kombináció lenyomása nem abban a programban kerül érvényesítésre, amelyik éppen az aktív, hanem abban, amelyik ezt létrehozta.
Ennek segítségével készíthetünk olyan alkalmazást, mely mondjuk a Ctrl + F11 lenyomásakor lefuttat valamilyen kódot, még akkor is, ha éppen egy másik alkalmazás az aktív és a mi programunk akár nem is látszik.
A megvalósítás lényege a RegisterHotKey Windows függvényben rejlik. E függvényen keresztül kérhetjük meg a Windows-t, hogy egy általunk megadott billentyűkombináció lenyomásakor értesítse az alkalmazásunkat egy üzenet formájában. Figyelve ezt az üzenetet, azonnal értesülhet alkalmazásunk erről a tényről és tetszőleges kódot futtathat le.
A függvény eléréséhez létrehozunk egy Win32 nevű osztályt és ebben külsőként deklaráljuk ezt a függvényt, mely a User32.dll-ben található.
  public class Win32
  {
    [DllImport("user32.dll")]
    public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
Szükségünk lesz még egy másik függvényre, mely UnRegisterHotKey névre hallgat. Ennek segítségével tudjuk megszüntetni a RegisterHotKey hatását.
    [DllImport("user32.dll")]
    public static extern bool UnRegisterHotKey(IntPtr hWnd, int id);
  }
Mivel a billentyűkombináció lenyomásakor a Windows egy üzenetet küld az általunk megadott ablaknak, így szükségünk lesz egy osztályra, mely képes a Windows üzenetek feldolgozására. Létrehozzuk tehát a GlobalHotKey osztályt, melyet a NativeWindow-ból származtatunk. Ebben megtalálható a WndProc nevű függvény, melyet felülírva már is képesek vagyunk a Windows üzenetek figyelésre. Ha itt azt tapasztaljuk, hogy a WM_HOTKEY üzenet érkezett, akkor az általunk figyeltetett billentyű kombináció lenyomásra került. Ha több kombinációt is figyeltetni szeretnénk, akkor itt szét kellene ágaztatnunk a programunkat annak függvényében, hogy aktuálisan melyik lett lenyomva. Ez esetben a kapott Message struktúra adna segítséget. Ennek LParam property-jének alsó 16 bitje a lenyomott gomb kódját adja, míg a felső 16 bit arról árulkodik, hogy lett-e módosító gomb (alt, shift, stb.) is lenyomva.
  public class GlobalHotKey: NativeWindow, IDisposable 
  {
    protected override void WndProc(ref Message m) 
    {
      switch (m.Msg)
      {
        case Win32.WM_HOTKEY:
          OnHotkey();
          break;
        default:
          base.WndProc(ref m);
          break;
      }
    }
Ahhoz persze, hogy WM_HOTKEY üzenetet kaphassunk, először is meg kell ezt rendelnünk a Windows-tól a RegisterHotKey hívásával. Hogy melyik billentyű kombinációt figyeltessük, azt az osztályunk konstruktorának paramétereiben kell megadnunk. Első paraméterként egy billentyű kódját, majd a továbbiakban a módosító billentyűk figyelését kell megadni. Ez utóbbiak logikai értékek, így ha a Ctrl + Alt kombinációra van szükségünk, akkor ezek a paraméterek legyenek igazak, a többi hamis. A billentyű megadása a Win32 osztályunkban lévő KeyCodes felsorolt típus elemei közül lehetséges.
    public GlobalHotKey(Win32.KeyCodes Key, bool Ctrl, bool Shift, bool Alt, bool Win) 
    {
A kapott értékeket tároljuk.
      key=(uint)Key;
A módosító billentyűkből képzünk egy számot, mely egyértelműen leírja, hogy mely gombokra kell figyelni és melyekre nem.
      mod=0;
      if (Ctrl) mod += Win32.MOD_CONTROL;
      if (Shift) mod += Win32.MOD_SHIFT;
      if (Alt) mod += Win32.MOD_ALT;
      if (Win) mod += Win32.MOD_WIN;
Win 2000 felett jeleznünk kell, hogy az üzenetkezelő ablakunk semmi mással nem szeretne foglalkozni, mint az üzenetek kezelésével, vagyis nem jelenik meg valós ablak a képernyőn. Ezt a HWND_MESSAGE konstans felhasználásával tehetjük meg.
      CreateParams cp = new CreateParams();
      if (Environment.OSVersion.Version.Major >= 5)
      {
        cp.Parent = (IntPtr)Win32.HWND_MESSAGE;
      }
      CreateHandle(cp);
    }
Amikor a CreateHandle lefut, akkor már rendelkezésünkre áll ablakkezelő osztályunk azonosítója. Ekkor már meghívhatjuk a RegisterHotKey függvényt, melynek első paraméterében ezt az azonosítót kell megadnunk. A Windows ennek az ablaknak fogja küldeni a WM_HOTKEY üzenetet. Második paraméter egy egyedi azonosító, melyet mi választhatunk meg 0x0000 és 0xBFFF között, illetve ha DLL-t készítünk, akkor 0xC000 és 0xFFFF között. Jelen példánknál mivel csak egy példányban használjuk fel a GlobalHotKey osztályunkat, így itt elegendő egy, konstans értéket megadnunk. Ha viszont többször is futtatnánk a RegisterHotKey-t, akkor már figyelnünk kellene arra, hogy a megadott azonosító egyedi legyen minden esetben.
A harmadik paraméterbe kerül a módosító billentyűk kódja, míg a negyedikbe a figyelendő billentyű kódja.
    public override void CreateHandle(CreateParams cp) 
    {
      base.CreateHandle(cp);
      Win32.RegisterHotKey(Handle, ID, mod, key);
    }
Amikor megszűnik az ablakunk, akkor kell meghívnunk az UnRegisterHotKey függvényt, értesítve a Windows-t, hogy a továbbiakban nem kérjük az adott gomb lenyomásának figyelését.
    public override void DestroyHandle() 
    {
      Win32. (Handle, ID);
      base.DestroyHandle();
    }           
Nézzük most a felhasználását az osztályunknak. A Form1 konstruktorában létrehozzuk a GlobalHotKey osztályunk egy példányát és megadjuk neki a Ctrl + F11 billentyűkombináció figyelését. A Hotkey eseményéhez hozzárendeljük a Hotkey nevű függvényünket, mely akkor jön létre, ha a Ctrl + F11 lenyomásra kerül, bármely alkalmazás is legyen az aktív.
    public Form1()
    {
      InitializeComponent();
      GlobalHotKey ghk = new GlobalHotKey(Win32.KeyCodes.VK_F11, true, false, false, false);
      ghk.Hotkey += new VoidEventHandler(Hotkey);
    }
A Hotkey függvény futásakor tehát biztosak lehetünk abban, hogy a felhasználó lenyomta a Ctrl + F11-et, ekkor tetszőleges kódot futtathatunk.
    private void Hotkey()
    {
      this.BackColor=Color.Red;
    }