Montag, 28. Mai 2012

Globale Hotkeys erstellen

In diesem Post soll es darum gehen, wie globale Hotkeys für ein C# Programm registriert werden können. Einmal registriert, wirken diese Hotkeys systemweit - heißt: Auch wenn das Programm nur im Hintergrund läuft, wird das Drücken der Hotkeys trotzdem dem Programm gemeldet. So kann z.B. eine laufende inaktive Anwendung per Tastendruck in den Vordergrund geholt werden. Einen Hotkey definieren wir über die API - Funktion RegisterHotKey(). Diese erwartet 4 Parameter:

  • Den Handle des Windows, welches die Nachricht erhalten soll.
  • Die ID des Hotkeys, wichtig wenn mehrere Hotkeys pro Anwendung registriert werden sollen. Wird NULL übergeben, wird der Hotkey einfach mit der Anwendung assoziiert.
  • Die Steuertasten, welche zur Aktivierung des Hotkeys gedrückt werden müssen (z.B. Alt, Strg ...)
  • Die Taste, welche welche zur Aktivierung des Hotkeys gedrückt werden muss (z.B. "X")
Analog dazu gibt es die Funktion UnregisterHotKey(), der das Fenster Handle sowie die ID des Hotkeys übergeben werden muss, mit der der Hotkey gelöscht werden kann (bzw. sollte bei Programmende). Beim Drücken eines Hotkeys wird der Awendung die Nachricht WM_HOTKEY übermittelt, um diese abzufangen ist die Funktion WndProc() zu überschreiben. Folgender Beispielcode legt zwei Hotkeys an (Shift + Strg + X und Shift + Strg + Y), ich denke daran sollte das Prinzip klar werden:
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 WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        [DllImport("user32.dll")]
        private static extern bool RegisterHotKey(IntPtr hWnd, int id, 
int fsModifiers, int vk);
        [DllImport("user32.dll")]
        private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

        const int MOD_CONTROL = 0x0002;
        const int MOD_SHIFT = 0x0004;
        const int WM_HOTKEY = 0x0312;

        private void Form1_Load(object sender, EventArgs e)
        {
            RegisterHotKey(this.Handle, 1, MOD_CONTROL + MOD_SHIFT, (int)Keys.X);
            RegisterHotKey(this.Handle, 2, MOD_CONTROL + MOD_SHIFT, (int)Keys.Y);
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            UnregisterHotKey(this.Handle, 1);
        }

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WM_HOTKEY && (int)m.WParam == 1)
                MessageBox.Show("Hotkey X erhalten.");
            if (m.Msg == WM_HOTKEY && (int)m.WParam == 2)
                MessageBox.Show("Hotkey Y erhalten.");
            base.WndProc(ref m);
        }

    }
}
Weitere Informationen (z.B. der Wert anderer Steuertasten) ist bei MSDN nachlesbar.

Kommentare:

  1. Das hab ich schon immer gesucht. Leider funktioniert es bei mir nicht irgendwas amch ich noch falsch es kommt keine fehler meldung aber auch keine msgbox mit hotkey erkannt.

    AntwortenLöschen
    Antworten
    1. Bei mir war es so, dass ich die Und-Bedingung in der WndProc weg machen musste.

      Also einfach nur

      if(m.Msg == WM_HOTKEY)
      MessageBox.Show("Hotkey X erhalten.");

      Allerdings habe ich auch nur EINEN Hotkey regestriert ... nicht 2

      Löschen
  2. Hallo erstmal, ich hätte eine Frage:
    Wie kann ich in C# so einen Modus erzeugen, wie bei Snipping Tool, bei ich keine Möglichkeit mehr habe ( außer durch ausschalten oder drücken der Windows-Taste ), um das Programm in den Hintergrund zu bringen? ( durchs Wegklicken oder durch das Starten eines anderen Programmes )
    LG

    AntwortenLöschen
  3. Ist richtig geil. Ist nur leider für WInForms, geht das auch irgentwie mit WPF?

    Wäre gut wenn du da noch was machen könntest...

    AntwortenLöschen
    Antworten
    1. Dieser Link hilft dir sicher weiter
      http://www.1771.in/c-global-hotkeys-in-wpf-working-from-every-window.html

      Löschen
  4. Hallo,

    an den 1. Poster: Hast du mal den Tipp aus Artikel http://csharp-tricks.blogspot.de/2012/03/wndproc-uberschreiben.html ausprobiert?
    Also einfach die WndProc() überschreiben und die Ereignisse ausgeben lassen?
    Dann solltest du sehen, wie weit das Programm kommt, eigentlich sollte es auf diese Weise funktionieren.

    An den 2.: Was genau meinst du, ich verstehe deinen Kommentar leider nicht. Ein Programm "verstecken" kannst du z.B., in dem du die Eigenschaft Visible des Formulars auf false stellst. Sonst und auch speziell um das Prorgamm wieder in den Vordergrund zu bringen hilft dir hoffentlich dieser Post: http://csharp-tricks.blogspot.de/2012/05/anwendung-in-den-vordergrund-bringen.html

    An den 3. Poster: Tut mir leid, mit WPF kenne ich mich nicht aus ...

    AntwortenLöschen
  5. Wieso ist

    "const int WM_HOTKEY = 0x0312;"

    das so?
    Ich verstehe das nicht :(

    AntwortenLöschen
  6. Du meinst, wie man auf die Zahl kommt? Das ist einfach so definiert, irgendwie muss man in diesem Fall bestimmte Ereignisse ja beschreiben. Bei P/Invoke Funktionen werden dazu meistens Zahlen in Hexadezimaldarstellung verwendet, und diese benennt man dann der Einfachheit halber mit Konstanten.

    AntwortenLöschen
  7. Ist es auch möglich, HotKeys wie z.B. F6 od. F7 zu registrieren?
    Denn ich bekomme das einfach nicht hin :(

    LG

    AntwortenLöschen
    Antworten
    1. Hi,
      ja ist es, diese Zeile registriert den Hotkey F6:
      RegisterHotKey(this.Handle, 3, 0, (int)Keys.F6);

      Löschen
  8. Dieser Kommentar wurde vom Autor entfernt.

    AntwortenLöschen
  9. Können dadurch auch globale Mausklicks registriert werden?

    AntwortenLöschen
    Antworten
    1. Ja.
      Schau dir dazu mal genauer die Events an, die in der WndProc abgefangen werden, wie z.B. hier beschrieben: http://csharp-tricks.blogspot.de/2012/03/wndproc-uberschreiben.html
      Ich denke, die Ereignisse MouseMove und SetCursor sollten dir weiterhelfen.

      Löschen
  10. Dieser Kommentar wurde vom Autor entfernt.

    AntwortenLöschen
  11. Danke, hat geholfen ☺

    Man sollte, auch wenn es relativ ist, kurz die Kombinationen mit angeben 😀

    Strg + Shift + X, Strg + Shift + Y 😇

    Eine Sache noch: einige Teile des Codes wird hier nicht richtig oder gar nicht angezeigt, ich musste in den Quelltest den Rest suchen, da dieser hier nicht angezeigt wurde. Abschnitt "RegisterHotKey(this.Handle, 1,", dort fehlte Code welcher aber im Quelltest der Seite angezeigt wurde.

    AntwortenLöschen
  12. Hallo, wie kann ich danach prüfen (z.B. mit einer if Anweisung) welcher Key gesendet wird? Mein Ziel ist es, mehere Keys zu registrieren und danach gleichzeitig zu prüfen welche Taste quasi betätigt wird.

    AntwortenLöschen