PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : WPF, Windows und Window Handles


Elemental
2015-09-09, 08:31:14
Hallo,
ich habe nach einer Möglichkeit gesucht, meine WPF Anwendung schliessen zu lassen, wenn eine WM_CLOSE an den Prozess geschickt wird. Unter Windows Forms ist das ja recht einfach mit dem IMessageFilter Interface und Application.AddMessageFilter().
Unter WPF gibt es nichts vergleichbares, also habe ich mich mit einem Hook an das MainWindow meiner WPF Anwendung gehängt. Allerdings kam dort nie die WM_Close Message an, die an den Prozess geschickt wurde. Auf irgend einer Internet-Seite hab ich dann gelesen, dass eine WPF-Anwendung immer mehrere Windows hat, auch wenn man im Projekt in Visual Studio nur ein Window angelegt hat und dass Process.MainWindowHandle nicht unbedingt das WPF-Window der Anwendung zurück gibt.

Also habe ich eine kleine TestAnwendung gebaut, die mittels EnumThreadWindows alle window handles eines Prozesses ermittelt:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text;
using System.Windows;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace ConsoleApplication1
{
class Program
{
#region P/Invoke

public const int WM_CLOSE = 0x0010;

[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);

[DllImport("user32.dll")]
static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);

static IEnumerable<IntPtr> EnumerateProcessWindowHandles(Process p)
{
var handles = new List<IntPtr>();

foreach (ProcessThread thread in p.Threads)
{
EnumThreadWindows(thread.Id, (hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero);
}

return handles;
}

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

#endregion

static void Main(string[] args)
{
//look for all WpfApplication1 processes and send them WM_CLOSE
Process[] processes = Process.GetProcesses();
foreach (Process p in processes)
{
if (p.ProcessName.StartsWith("WpfApplication1"))
{
IEnumerable<IntPtr> windowHandles = EnumerateProcessWindowHandles(p);
foreach (var handle in windowHandles)
{
StringBuilder sbWindowText = new StringBuilder(256);
GetWindowText(handle, sbWindowText, 256);
string windowText = sbWindowText.ToString();
Console.WriteLine("window handle: " + handle.ToString() + " window text: " + windowText);
Debug.WriteLine("window handle: " + handle.ToString() + " window text: " + windowText);

PostMessage(handle, WM_CLOSE, new IntPtr(0), new IntPtr(0));
}
}
}
}
}
}


Für meine frisch angelegte WpfApplication1 mit nur einem WPFWindow (Title="MyMainWindow") bekomme ich 9 window handles und folgende Ausgabe im Output-Fenster von Visual Studio:
window handle: 1181524 window text: CiceroUIWndFrame
window handle: 723434 window text: CiceroUIWndFrame
window handle: 2622664 window text: MyMainWindow
window handle: 1509214 window text: MediaContextNotificationWindow
window handle: 657918 window text:
window handle: 657882 window text: SystemResourceNotifyWindow
window handle: 1378054 window text:
window handle: 1902494 window text: MSCTFIME UI
window handle: 854528 window text: Default IME


Weiss jemand, wieso ich so viele window handles bekomme, vor allem auch welche mit solch komischen Texten wie "CiceroUIWndFrame" und "MediaContextNotificationWindow"?
Mache ich irgend etwas falsch bei EnumThreadWindows, so dass auch window handles von anderen Prozessen mit erfasst werden? Ich kann aber keinen Fehler im Code erkennen. :confused:

mfG
Elemental

Monger
2015-09-09, 18:39:04
Nur um mal das Problem besser zu verstehen: was genau willst du denn erreichen? Wann erwartest du denn ein WM_CLOSE, und was willst du dann daraufhin tun?

Für mich klingt das erstmal so, als wolltest du einfach nur die Anwendung in einer definierten Art und Weise beenden. Dafür gibts ganz oben an der App.xaml passende Events, die üblicherweise dann aktiv werden wenn alle Fenster geschlossen werden. Das gilt meines Wissens auch dann, wenn etwas anderes versucht die Anwendung zu schließen.

Elemental
2015-09-09, 20:20:59
Ich möchte meine WPFAnwendung von einer anderen Anwendung beenden lassen. Konkret geht es um eine Anwendung im System Tray, die bei der Deinstallation beendet werden soll.
Normalerweise würde ich einfach an Process.MainWindowHandle eine WM_CLOSE message schicken, aber bei der Tray Application ist Process.MainWindowHandle = 0.
Deshalb der ganze Aufwand mit EnumThreadWindows.
Das funktioniert auch so, aber mich wundert es halt, dass ich so viele Window Handles bekomme für den Prozess, der nur ein Window hat.

mfG
Elemental

Gast
2015-09-29, 19:43:19
Also erstens sieht man auf Anhieb mindestens 3 "Fenster" ("Windows") und zweitens sollte man bei der Windows-Programmierung natürlich wissen, wofür ein "window handle" eigentlich steht; nämlich für die einzige Möglichkeit, eine Windows-Verwaltete MessageQueue (und damit globale Nachrichten) zu erhalten. Das hat aber nichts mit sichtbaren Fenstern zu tun, solche sind nur Teil des zugrundeliegenden Konzeptes...