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);
        }
    }
}

Kommentare:

  1. Top Artikel! Top Blog!
    Hab mir schon des öfteren kleine Denkanstöße von hier geholt!

    Weiter so!

    AntwortenLöschen
  2. Auch von mir ein großes Lob, ich schaue ebenfalls immer wieder mal gerne hier im Blog vorbei, Super Sache!

    Ich habe noch eine Frage an dich Oliver,
    ist es irgenwie einem C# Programm möglich herauszubekommen, welches (andere) Programm (meinetwegen der Explorer oder Photoshop oder Media Player) gerade den Fokus besitzt und wie der Fenstertitel dieses Programmes ist (oder name der Exe datei ginge auch)?

    D.h. ich hätte mein c# programm im Hintergrund laufen und arbeite meinetwegen gerade mit Photoshop. Dann soll mein Programm im Hintergrund wissen, dass Photoshop gerade das aktive Fenster in Windows ist und dass es "Photoshop" heißt. Würde ich nun den Arbeitsplatz (Computer) öffnen, sollte mein Programm dies auch mitbekommen und wissen, dass nun der Windows Explorer das aktive Fenster von Windows ist und dass es "Explorer" heißt.

    Ich möchte nämlich

    AntwortenLöschen
  3. Hi,
    aus deinem letzten leider abgebrochenen Satz kann ich mir ebenfalls nicht zusammenreimen was du möchtest, ich kann dir aber beantworten wie du das aktive Fenster abfragst: Dies geht über die Funktion GetForegroundWindow (siehe http://www.pinvoke.net/default.aspx/user32/GetForegroundWindow.html).
    Hoffe das hilft, liebe Grüße

    AntwortenLöschen
  4. Ohje, hat mein Internet wohl versagt ;-)
    Ich möchte ein Programm schreiben, dass die Finderbar eines Macs (sozusagen die "Taskleiste" des Macs) simuliert. Dafür muss das Programm wissen, welches Fenster eben gerade aktiv ist (damit es dementsprechende Aktionen wie "Speichern unter" etc. bei Photoshop und meinetwegen "Neuer Ordner" bei Windows Explorer (um bei den Beispielen zu bleiben) anzeigen kann. Ich müsste dafür außerdem noch wissen, ob man sozusagen "umgekehrte" globale Hooks erstellen kann, d.h. wenn mein Programm ein Key-Up Event empfängt, soll nicht mein Programm diese Tasteneingaben verarbeiten, sondern das eigentliche aktive Programm (wie z.B. Photoshop).

    vielen Dank für eure Hilfe! :-)

    AntwortenLöschen
  5. Dieses Problem kann auch mithilfe eines kleinen Tricks mit .Net-eigenen Bordmitteln lösen. Folgender Code deklariert das Fenster als TopMost-Fenster ("Immer im Vordergrund"-Fenster), stellt sich, dass dies angewendet wird (Application.DoEvents()) und setzt es anschließend wieder zurück:

    this.TopMost = true;
    Application.DoEvents();
    this.TopMost = false;

    Auch wenn TopMost wieder auf "false" gesetzt wurde, das Fenster bleibt trotzdem im Vordergrund. Damit spart man sich den Dll-Import.

    Grüße

    AntwortenLöschen
  6. Dieses Problem kann auch mithilfe eines kleinen Tricks mit .Net-eigenen Bordmitteln gelöst werden. Folgender Code deklariert das Fenster als TopMost-Fenster ("Immer im Vordergrund"-Fenster), stellt sicher, dass dies angewendet wird (Application.DoEvents()) und setzt es anschließend wieder zurück:

    this.TopMost = true;
    Application.DoEvents();
    this.TopMost = false;

    Obwohl TopMost wieder auf "false" gesetzt wurde, das Fenster verbleibt trotzdem im Vordergrund. Damit spart man sich den Dll-Import.

    Grüße

    AntwortenLöschen
  7. Ergänzung:

    Dazu wäre noch folgender Code sinnvoll:
    this.WindowState = FormWindowState.Normal;

    AntwortenLöschen