BigBastis Blog

C#: Alle Sichtbaren Prozesse / Fenster auflisten

Introduction

user

Sebastian Gross

Sebastian Gross arbeitet in Bielefeld als Softwareentwickler für .NET und Java im Bereich Web.Als Fan der .NET-Plattform lässt er sich kein Userguppen Treffen und Community Event im Raum OWL entgehen.Dabei hat er eine besondere Vorliebe für das ASP.NET MVC Framework und für das Test Driven Development (TDD) entwickelt.


LATEST POSTS

Handling too long scrollspy menus 10th June, 2015

Java: Create ZIP archive 23rd March, 2015

.NET

C#: Alle Sichtbaren Prozesse / Fenster auflisten

Posted on .

Immer öfter benötige ich beim erstellen einer Software Zugriff auf fremde Fenster, doch leider bietet hier die Process-Klasse des .NET Frameworks nur wenig Hilfe. Denn mit ihr kann mann nicht alle Fenster ansprechen!

Problem:

Nehmen wir mal wir möchten alle zur Zeit geöffneten Fenster in einer Liste darstellen, dabei haben wir 3 Windows Explorer Fenster, 2 Mal FireFox und 3 anderen Programme geöffnet.

Wenn wir nun auf die Mittel des .NET Frameworks zurückgreifen, müssten wir das so angehen:

            foreach (Process p in Process.GetProcesses())
            {
                if (p.MainWindowHandle != null){
                    MessageBox.Show(p.MainWindowTitle + " - " + p.MainWindowHandle.ToString());
                }
            }

Der Code ist sehr simpel und leicht verständlich. Wir prüfen, ob ein Fenster sichtbar ist, in dem wir schauen, ob ein Handle dafür hinterlegt ist. Leider kann man das meines Wissens nicht anders lösen.

Aber wie es euch vielleicht schon aufgefallen ist benutzen wir hier die Funktion p.MainWindowHandle() – das Problem hierbei ist das kleine Wörtchen „Main“, das in dem Funktionsnamen steckt, denn diese Funktion liefert uns NUR den Titel und das Fensterhandle des Hauptfensters des Programms, oder in anderen Worten, des Fensters eines Programms, das zuletzt aktiv gewesen ist.

Das bedeutet, dass wenn wir 3 Firefox Fenster offen haben wir nur das Handle und den Titel des zuletzt aktiven Fenster bekommen. Schlimmer ist es noch mit den Systemfenstern, da diese immer unter dem Prozess „Explorer“ laufen, der unter anderem auch für die Darstellung der Taskleiste verantwortlich ist.

Das bedeutet, dass wir im Normalfall nicht die Windows Explorer Fenster ansprechen können sondern stattdessen das Handle der Taskleiste zurückbekommen, da diese meißt aktiver ist als die Windows Fenster und meistens auch immer im Vordergrund bleibt. (Siehe letzter Artikel)

Lösung:

Doch wie bekommen wir nun alle Fenster? Hier bietet uns das Framework leider keine Hilfe, weswegen wir auf die Win32 API zurückgreifen müssen. Hier finden wir Methoden, die uns viel über die Fenster verraten:

        [DllImport("user32.dll")] //Die Position und Größe eines Fensters bestimmen
        public static extern long GetWindowRect(IntPtr hwnd, ref RECT lpRect);

        [DllImport("user32.dll")] //Prüfen, ob ein Fenster Sichtbar ist
        public static extern bool IsWindowVisible(IntPtr hWnd);

        [DllImport("user32.Dll")] //Alle offenen Fenster abrufen
        public static extern int EnumWindows(ProcessListDemo.Windows.WinCallBack x, int y);

        [DllImport("User32.Dll")] //Titel eines Fensters auslesen
        public static extern void GetWindowText(int h, StringBuilder s, int nMaxCount);

        [DllImport("User32.Dll")] //Die Klasse des Fensters besimmen
        public static extern void GetClassName(int h, StringBuilder s, int nMaxCount);

Die wichtigsten hier sind „EnumWindows“ und „IsWindowVisible“. Die erste liefert uns eine Liste mit allen Fensterhandles die zur Zeit aktiv sind und mit der Zweiten können wir bestimmen, ob ein Fenster sichtbar ist oder nicht. Die anderen Funktionen lassen wir erstmal ungeachtet.

Wie eben schon erwähnt benutzen wir die EnumWindows-Funktion um uns alle Fensterhandles zu besorgen. Diese Funktion arbeitet hierbei über einen Delegaten Aufruf.

        ///
        /// Beinhaltet alle zur Zeit geöffneten und Minimierten Fenster
        ///
        public LinkedList lstWindows { get; set; }

        ///
        /// Delegate Funktion für EnumWindows (Siehe Declarations)
        /// Gibt die Werte an EnumWindowCallBack weiter
        ///
        public delegate bool WinCallBack(int hwnd, int lParam);

        private void getWindows()
        {
            //Liste mit Fenstern befüllen
            ProcessListDemo.Declarations.EnumWindows(new WinCallBack(EnumWindowCallBack), 0);

        }

        ///
        /// Diese Funktion wird durch die Delegate Funktion WinCallBack aufgerufen
        /// und iteriert durch alle zur Zeit geöffneten Fenster
        private bool EnumWindowCallBack(int hwnd, int lParam)
        {
            IntPtr windowHandle = (IntPtr)hwnd;

            StringBuilder sb = new StringBuilder(1024);
            StringBuilder sbc = new StringBuilder(256);

            ProcessListDemo.Declarations.GetClassName(hwnd, sbc, sbc.Capacity);
            ProcessListDemo.Declarations.GetWindowText((int)windowHandle, sb, sb.Capacity);

            //Nur Prozesse mit einer Beschreibung, also einem Fenster bearbeiten
            if (sb.Length > 0)
            {
                ProcessListDemo.Declarations.RECT r = new ProcessListDemo.Declarations.RECT(); //Fensterposition & Größe bestimmen:
                ProcessListDemo.Declarations.GetWindowRect(windowHandle,ref r);

                Window w = new Window(  sb + "",
                                        windowHandle,
                                        sbc + "",
                                        ProcessListDemo.Declarations.IsWindowVisible(windowHandle),
                                        new ProcessListDemo.Declarations.Point(r.Left, r.Top),
                                        new ProcessListDemo.Declarations.Point(r.Right - r.Left, r.Bottom - r.Top),
                                        Window.WinType.Normal);
                this.lstWindows.AddLast(w);
            }
            return true;
        }

In einfachen Worten erklärt passiert hier folgendes: Wir rufen die EnumWindows Funktion auf und übergeben dieser als Parameter die Referenz auf die Funktion EnumWindowCallBack, dazu benötigen wir die Delegate Funktion WinCallBack. Wichtig hierbei ist, dass alle Funktionen die gleichen Parameterdefinitionen haben. Der zweite Parameter ist bei mir hier aber ungenutzt, dieser ist optional vorhanden, um zB. weitere Referenzen zu übergeben wie zB. eine Listbox worin die Ergebnisse gespeichert werden sollen. Da wir hier aber die Ergebnisse direkt in die Klassen-variable schreiben benötigen wir diese nicht.

Nachdem die EnumWindows Funktion nun aufgerufen wurde macht sie nichts anderes, als die ihr übergebene Funktion (EnumWindowCallBack) aufzurufen. Diese wird dabei für jeden Prozess einmal aufgerufen und bekommt als Parameter das Handle des Fensters.

Im Prinzip nicht kompliziert. Ich habe hier, um das ganze besser zu visualisieren zwei Klassen namens „Window“ und „Windows“ erstellt. Diese übernehmen die ganze Arbeit für euch und erstellen eine Komplette Übersicht aller Fenster inklusive der Angaben, wie Größe, Position, Handle und Titel. Diese könnt ihr gern in euren eigenen Projekten nutzen.

Bild 1: Die Eigenschaften der Window-Klasse

Wenn ihr euch mal die Ergebnisse der EnumWindows Funktion anschaut werdet ihr feststellen, dass es ziemlich viele Fenster sind die da so im Hintergrund laufen, aber unsichtbar sind! Um diese Fenster zu filtern benutzen wir die Funktion IsWindowVisible und prüfen ob das Fenster Sichtbar ist oder nicht.

Bild 2: Das Demoprogramm in Aktion, zeigt alle Fensterprozesse

In dem Demoprogramm zeige ich euch wie ihr die zwei Klassen „Window“ und „Windows“ sehr einfach dazu benutzen könne, um eine Komplette Übersicht über alle Fenster des Systems zu bekommen.

        private void cmdList_Click(object sender, EventArgs e)
        {
            //Alte Liste Leeren
            lstProcess.Items.Clear();

            //Alle vorhandenen Fenster bestimmen
            Windows windows = new Windows();

            //Alle Fenster durchgehen und in die Lite einsetzen
            foreach (Window w in windows.lstWindows)
            {
                ListViewItem lvi = new ListViewItem(w.winTitle);
                lvi.SubItems.AddRange(new string[] { w.winClass,
                    w.winHandle.ToString(), w.winVisible.ToString() });     

                lstProcess.Items.Add(lvi);
            }
        }

Wie man sieht ist diese Angelegenheit dank der „Windows“-Klassen nun sehr einfach geworden. Zum besseren Verständnis solltet ihr euch das Demoprojekt herunterladen.

Download des Demoprojekts: Download [VS 2008 C#]

Wie hat euch dieser Artikel gefallen? Habt ihr Verbesserungsvorschläge, Kritik oder Lob? Bitte ein Kommentar schreiben!

profile

Sebastian Gross

http://www.bigbasti.com

Sebastian Gross arbeitet in Bielefeld als Softwareentwickler für .NET und Java im Bereich Web.Als Fan der .NET-Plattform lässt er sich kein Userguppen Treffen und Community Event im Raum OWL entgehen.Dabei hat er eine besondere Vorliebe für das ASP.NET MVC Framework und für das Test Driven Development (TDD) entwickelt.

Comments
user

Author bigbasti87

Posted at 12:24 28. Februar 2010.

Bloggd: C#: Alle Sichtbaren Prozesse / Fenster auflisten – http://blog.bigbasti.com/c-alle-sichtbar… #csharp #windows #process

user

Author Sebastian87

Posted at 11:15 9. Juni 2010.

Hallo Basti,
ich durchsuche gerade das netz zu den Themen EnumWindows und dem Kontrollieren fremder Anwendungen aus eigenen Programmen heraus. Da du dich damit schon eine Weile beschäftigt hast, hätte ich mal eine Frage zur Realisierbarkeit.
Ist es möglich ein Windows Fenster mit allen Elementen, die es enthält durch eigene Software nachzubauen? Sprich ich lasse mir von meinem Programm sagen wo welches Element welcher Klasse auf dem untersuchten Window platziert ist, verpacke diese Informationen in eine schöne XML und sende diese woanders hin, wo eine weitere Software anhand dieser XML Informationen (man könnte das ganze ja auch schön als TCP/IP Stream machen) das Window repliziert.
Es geht hier vor allem um Standard Windows Komponenten: Editfelder, Buttons, Labels usw.

Ist das deiner Meinung nach möglich?

Mit freundlichen Grüßen
Sebastian

user

Author admin

Posted at 14:37 9. Juni 2010.

Hallo Sebastian,
nichts ist unmöglich 😉 es hängt nur davon ab wieviel Aufwand du treiben willst. Dein Vorhaben sollte aber garnicht mal so komplex werden, habe mit soeteas ähnlichem auch schon mal herum gespielt. Schau dir mal diese Links an, damit solltest du weiter kommen: http://goo.gl/MGkg http://goo.gl/hMOm – Mal schauen, vielleicht nehme ich mir auch mal die Zeit und mach da ein Beispiel zu 😉
MfG, Basti

Kommentar verfassen

View Comments (3) ...
Navigation