Dienstag, 29. Mai 2012

Anwendung in den Vordergrund bringen

In diesem Post möchte ich zeigen, wie man eine C# Anwendung in den Vordergrund holen kann. Die vorgestellte Methode soll, egal ob das Prorgamm momentan nur nicht den Fokus hat oder ganz minimiert ist, der Applikation den Eingabefokus geben und sie als oberstes Fenster auf dem Bildschirm anzeigen.
Standardmäßig in C# enthalten ist auf Formularebene das Attribut TopMost bzw. die Funktion BringToFront(), diese rücken das Formular aber nur unter mehreren Formularen der gleichen Anwendung nach vorne. Also müssen wir etwas tiefer in die Trickkiste greifen und bedienen uns der Win32 API.

Die für obige Zwecke benötigte Funktion heißt SetForegroundWindow() und erwartet den Handle des nach vorne zu holenden Fensters. Sie verschafft der Anwendung den Eingabefokus, ist die Anwendung allerdings minimiert, wird diese nicht angezeigt. Der Grund hierfür ist, dass bei minimierten Anwendungen eben kein Fenster vorhanden ist, welches angezeigt werden könnte.

Um dieses Fenster anzulegen, benutzen wir die Funktion ShowWindowAsync(). Diese erwartet 2 Parameter, als ersten wieder das Fenster Handle und als zweiten einen Int - Wert, der bestimmt, wie das Fenster angezeigt wird. Für einen Überblick über alle Möglichkeiten verweise ich auf MSDN, ich benutze hier nur SW_RESTORE, welches das Fenster in seiner ursprünglichen Größe anzeigt.

Wir fügen nun noch eine Abfrage ein, um zu prüfen, ob das Fenster minimiert ist. Dieses ist über die Funktion IsIconic() möglich.

Folgendes Programm benutzt oben beschriebene Funktionen um das Programm nach Austicken eines Timers in den Vordergrund zu bringen:
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
    {
        [DllImport("user32.dll")]
        private static extern
            bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern
            bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);

        [DllImport("user32.dll")]
        private static extern
            bool IsIconic(IntPtr hWnd);

        private const int SW_RESTORE = 9;

        public Form1()
        {
            InitializeComponent();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (IsIconic(this.Handle))
                ShowWindowAsync(this.Handle, SW_RESTORE);

            SetForegroundWindow(this.Handle);
        }
    }
}

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.