C# 是什么导致我的NotifyIcon在Alt+F4之后隐藏?
我有一个WPF应用程序,它使用winforms Notify图标在托盘上显示上下文菜单。当我执行以下步骤时,图标消失 右键单击任务栏中的通知图标 选择显示模式对话框的关联菜单项 关闭该对话框 按Alt+F4 下面是我看到这个bug的一个小例子 XAML: 代码隐藏:C# 是什么导致我的NotifyIcon在Alt+F4之后隐藏?,c#,wpf,notifications,system-tray,C#,Wpf,Notifications,System Tray,我有一个WPF应用程序,它使用winforms Notify图标在托盘上显示上下文菜单。当我执行以下步骤时,图标消失 右键单击任务栏中的通知图标 选择显示模式对话框的关联菜单项 关闭该对话框 按Alt+F4 下面是我看到这个bug的一个小例子 XAML: 代码隐藏: namespace killtrayicon { using System.Windows; /// <summary> /// Interaction logic for MainWindow
namespace killtrayicon
{
using System.Windows;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private System.Windows.Forms.NotifyIcon notifyIcon = new System.Windows.Forms.NotifyIcon();
public MainWindow()
{
InitializeComponent();
notifyIcon.Icon = Properties.Resources.icon;
notifyIcon.Visible = true;
notifyIcon.Text = "test";
notifyIcon.ContextMenu = new System.Windows.Forms.ContextMenu();
notifyIcon.ContextMenu.MenuItems.Add("click", (s, e) =>
{
MessageBox.Show("menu");
});
}
private void Button_Click(object sender, RoutedEventArgs e)
{
notifyIcon.Icon = Properties.Resources.icon;
}
}
}
单击“我的主窗口”中的按钮将重置图标,并再次显示通知图标。因此,通知图标本身没有被删除。检查NotifyIcon实例表明,在重置图标之前,它仍然可见,并且icon属性指向“我的资源”中的有效ICO
我怀疑上下文菜单是问题所在,因为如果我通过单击托盘图标显示模式对话框,则不会出现此问题
如何使NotifyIcon不响应Alt+F4
编辑:此问题是的副本,但该问题没有复制问题死链接的示例代码,提交给Microsoft的问题链接也是死链接,并且没有实际解决方案的可接受答案。我发现了解决方案。NotifyIcon创建一个隐藏的NativeWindow作为由Shell_NotifyIcon创建的图标生成的窗口消息的收件人。该窗口使用默认的窗口进程,该进程与其他窗口一样处理Alt+F4。通过将其转换为WM_CLOSE。您需要使用Win32 API在该NativeWindow中对HWND进行子类化,截取WM_CLOSE并忽略它 首先从comctl32.dll添加一些Win32方法:
public static class Comctl32
{
public const string DLL = "comctl32.dll";
public const uint WM_CLOSE = 0x0010;
public const uint WM_NCDESTROY = 0x0082;
public delegate IntPtr SubclassWndProc(IntPtr hWnd, uint uMsg, UIntPtr wParam, UIntPtr lParam, UIntPtr uIdSubclass, UIntPtr dwRefData);
[DllImport(DLL, CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool SetWindowSubclass(
[param: In]
IntPtr hWnd,
[param: In]
SubclassWndProc pfnSubclass,
[param: In]
UIntPtr uIdSubclass,
[param: In]
UIntPtr dwRefData);
[DllImport(DLL, CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool RemoveWindowSubclass(
[param: In]
IntPtr hWnd,
[param: In]
SubclassWndProc pfnSubclass,
[param: In]
UIntPtr uIdSubclass);
[DllImport(DLL, CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr DefSubclassProc(
[param: In]
IntPtr hWnd,
[param: In, MarshalAs(UnmanagedType.U4)]
uint uMsg,
[param: In]
UIntPtr WPARAM,
[param: In]
UIntPtr LPARAM);
}
然后将代码添加到NotifyIcon,以对隐藏的托盘图标窗口进行子类化。您需要使用反射来访问窗口,因为它不是公共的:
private Native.Comctl32.SubclassWndProc subclassWndProc;
...
// Get the HWND from the notify icon
Type notifyIconType = typeof(System.Windows.Forms.NotifyIcon);
BindingFlags hidden = BindingFlags.NonPublic | BindingFlags.Instance;
var window = notifyIconType.GetField("window", hidden).GetValue(this.notifyIcon) as System.Windows.Forms.NativeWindow;
// Inject our window proc to intercept window messages
this.subclassWndProc = this.TrayWndProc;
Native.Comctl32.SetWindowSubclass(window.Handle, this.subclassWndProc, UIntPtr.Zero, UIntPtr.Zero);
然后截取WM_CLOSE以忽略Alt+F4。我们还确保取消WM_NCDESTROY上的子类:
哇,对一个没有死链接的措辞更好的问题投了反对票?
private Native.Comctl32.SubclassWndProc subclassWndProc;
...
// Get the HWND from the notify icon
Type notifyIconType = typeof(System.Windows.Forms.NotifyIcon);
BindingFlags hidden = BindingFlags.NonPublic | BindingFlags.Instance;
var window = notifyIconType.GetField("window", hidden).GetValue(this.notifyIcon) as System.Windows.Forms.NativeWindow;
// Inject our window proc to intercept window messages
this.subclassWndProc = this.TrayWndProc;
Native.Comctl32.SetWindowSubclass(window.Handle, this.subclassWndProc, UIntPtr.Zero, UIntPtr.Zero);
private IntPtr TrayWndProc(IntPtr hWnd, uint uMsg, UIntPtr wParam, UIntPtr lParam, UIntPtr uIdSubclass, UIntPtr dwRefData)
{
switch (uMsg)
{
// Ignore the close message to avoid Alt+F4 killing the tray icon
case Native.Comctl32.WM_CLOSE:
return IntPtr.Zero;
// Clean up subclassing
case Native.Comctl32.WM_NCDESTROY:
Native.Comctl32.RemoveWindowSubclass(hWnd, this.subclassWndProc, UIntPtr.Zero))
break;
}
// Invoke the default window proc
return Native.Comctl32.DefSubclassProc(hWnd, uMsg, wParam, lParam);
}