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
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