我想知道我的NotifyIcon
在系统托盘中的位置(在对其进行任何点击之前)。
在this other question @ Hans Passant 发表评论说否,这是不可能的,但我认为几乎所有的事情都可以由操作系统在内部完成也可由开发人员复制,如果我不正确那么为什么SO可以让NotifyIcon
位置显示弹出窗口?
在上面的同一个问题中,有一个C#示例说明了如何定位系统托盘矩形,我想知道这可能是一个开始。
这项任务能否实现?
如果是,那怎么样?
如果没有,为什么操作系统可以?我们以什么方式限制无法重现同样的事情?
答案 0 :(得分:6)
您需要这些声明:
public const Int32 WM_MYMESSAGE = 0x8000; //WM_APP
public const Int32 NOTIFYICON_VERSION_4 = 0x4;
//messages
public const Int32 WM_CONTEXTMENU = 0x7B;
public const Int32 NIN_BALLOONHIDE = 0x403;
public const Int32 NIN_BALLOONSHOW = 0x402;
public const Int32 NIN_BALLOONTIMEOUT = 0x404;
public const Int32 NIN_BALLOONUSERCLICK = 0x405;
public const Int32 NIN_KEYSELECT = 0x403;
public const Int32 NIN_SELECT = 0x400;
public const Int32 NIN_POPUPOPEN = 0x406;
public const Int32 NIN_POPUPCLOSE = 0x407;
public const Int32 NIIF_USER = 0x4;
public const Int32 NIIF_NONE = 0x0;
public const Int32 NIIF_INFO = 0x1;
public const Int32 NIIF_WARNING = 0x2;
public const Int32 NIIF_ERROR = 0x3;
public const Int32 NIIF_LARGE_ICON = 0x20;
public enum NotifyFlags {
NIF_MESSAGE = 0x01,
NIF_ICON = 0x02,
NIF_TIP = 0x04,
NIF_INFO = 0x10,
NIF_STATE = 0x08,
NIF_GUID = 0x20,
NIF_SHOWTIP = 0x80
}
public enum NotifyCommand { NIM_ADD = 0x0, NIM_DELETE = 0x2, NIM_MODIFY = 0x1, NIM_SETVERSION = 0x4}
[StructLayout(LayoutKind.Sequential)]
public struct NOTIFYICONDATA
{
public Int32 cbSize;
public IntPtr hWnd;
public Int32 uID;
public NotifyFlags uFlags;
public Int32 uCallbackMessage;
public IntPtr hIcon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public String szTip;
public Int32 dwState;
public Int32 dwStateMask;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public String szInfo;
public Int32 uVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public String szInfoTitle;
public Int32 dwInfoFlags;
public Guid guidItem; //> IE 6
public IntPtr hBalloonIcon;
}
[DllImport("shell32.dll")]
public static extern System.Int32 Shell_NotifyIcon(NotifyCommand cmd, ref NOTIFYICONDATA data);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public Int32 left;
public Int32 top;
public Int32 right;
public Int32 bottom;
}
[StructLayout(LayoutKind.Sequential)]
public struct NOTIFYICONIDENTIFIER
{
public Int32 cbSize;
public IntPtr hWnd;
public Int32 uID;
public Guid guidItem;
}
//Works with Shell32.dll (version 6.1 or later)
[DllImport("shell32.dll", SetLastError = true)]
public static extern int Shell_NotifyIconGetRect([In]ref NOTIFYICONIDENTIFIER identifier, [Out]out RECT iconLocation);
添加图标:
//you only need this guid to identify your icon
private Guid guid;
//call only once to set the icon and create the guid.
private void AddIcon()
{
guid = Guid.NewGuid();
NOTIFYICONDATA data = new NOTIFYICONDATA();
data.cbSize = Marshal.SizeOf(data);
data.hWnd = this.Handle;
data.guidItem = guid;
data.uCallbackMessage = WM_MYMESSAGE; //This is the message sent to our app
data.hIcon = Properties.Resources.myIcon;
data.szTip = "Your text";
data.uFlags = NotifyFlags.NIF_ICON | NotifyFlags.NIF_GUID | NotifyFlags.NIF_MESSAGE | NotifyFlags.NIF_TIP |
NotifyFlags.NIF_SHOWTIP;
Shell_NotifyIcon(NotifyCommand.NIM_ADD, ref data);
data.uVersion = NOTIFYICON_VERSION_4;
Shell_NotifyIcon(NotifyCommand.NIM_SETVERSION, ref data);
}
在屏幕坐标中获取图标的位置:
private void GetRectIcon()
{
RECT rect = new RECT();
NOTIFYICONIDENTIFIER notifyIcon = new NOTIFYICONIDENTIFIER();
notifyIcon.cbSize = Marshal.SizeOf(notifyIcon);
//only guid is needed
notifyIcon.guidItem = guid;
int hresult = Shell_NotifyIconGetRect(ref notifyIcon, out rect);
//rect now has the position and size of icon
}
删除通知图标:
private void DeleteIcon()
{
NOTIFYICONDATA data = new NOTIFYICONDATA();
data.cbSize = Marshal.SizeOf(data);
data.uFlags = NotifyFlags.NIF_GUID;
data.guidItem = guid;
Shell_NotifyIcon(NotifyCommand.NIM_DELETE, ref data);
}
添加气球
private void AddBalloon()
{
NOTIFYICONDATA data;
data = new NOTIFYICONDATA();
data.cbSize = Marshal.SizeOf(data);
data.guidItem = guid;
//Set custom icon for balloon or NIIF_NONE for no icon. You can use all the other
//NIIF_... for system icons
data.dwInfoFlags = NIIF_USER;
data.hBalloonIcon = Properties.Resources.myNewIcon;
//text in balloon
data.szInfo = "My text in balloon";
//balloon title
data.szInfoTitle = "Balloon title";
//set the flags to be modified
data.uFlags = NotifyFlags.NIF_INFO | NotifyFlags.NIF_SHOWTIP | NotifyFlags.NIF_GUID;
Shell_NotifyIcon(NotifyCommand.NIM_MODIFY, ref data);
}
抓住消息
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MYMESSAGE)
{
//(Int32)m.LParam & 0x0000FFFF get the low 2 bytes of LParam, we dont need the high ones.
//(Int32)m.WParam & 0x0000FFFF is the X coordinate and
//((Int32)m.WParam & 0xFFFF0000) >> 16 the Y
switch ((Int32)m.LParam & 0x0000FFFF)
{
case NIN_BALLOONHIDE:
break;
case NIN_BALLOONSHOW:
break;
case NIN_BALLOONTIMEOUT:
break;
case NIN_BALLOONUSERCLICK:
//user clicked on balloon
break;
case NIN_SELECT:
//user left click on icon
break;
case WM_CONTEXTMENU:
//user right click on icon
break;
//get what mouse messages you want
//case WM_LBUTTONDOWN:
//....
default:
break;
}
}
base.WndProc(ref m);
}
非托管代码的强大功能
答案 1 :(得分:4)
在γηράσκωδ'αείπολλάδιδ的答案的帮助下,我找到了一个解决方案,让你使用标准NotifyIcon
控制来确定图标的位置。它需要使用Reflection,因为它必须访问私有字段,因此取决于NotifyIcon类的当前实现。我使用.NET Framework 2.0和4.0进行了测试。
using System;
using System.Reflection;
using System.Windows.Forms;
using System.Runtime.InteropServices;
sealed class NotifyIconHelper
{
public static Rectangle GetIconRect(NotifyIcon icon)
{
RECT rect = new RECT();
NOTIFYICONIDENTIFIER notifyIcon = new NOTIFYICONIDENTIFIER();
notifyIcon.cbSize = Marshal.SizeOf(notifyIcon);
//use hWnd and id of NotifyIcon instead of guid is needed
notifyIcon.hWnd = GetHandle(icon);
notifyIcon.uID = GetId(icon);
int hresult = Shell_NotifyIconGetRect(ref notifyIcon, out rect);
//rect now has the position and size of icon
return new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public Int32 left;
public Int32 top;
public Int32 right;
public Int32 bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct NOTIFYICONIDENTIFIER
{
public Int32 cbSize;
public IntPtr hWnd;
public Int32 uID;
public Guid guidItem;
}
[DllImport("shell32.dll", SetLastError = true)]
private static extern int Shell_NotifyIconGetRect([In]ref NOTIFYICONIDENTIFIER identifier, [Out]out RECT iconLocation);
private static FieldInfo windowField = typeof(NotifyIcon).GetField("window", Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance);
private static IntPtr GetHandle(NotifyIcon icon)
{
if (windowField == null) throw new InvalidOperationException("[Useful error message]");
NativeWindow window = windowField.GetValue(icon) as NativeWindow;
if (window == null) throw new InvalidOperationException("[Useful error message]"); // should not happen?
return window.Handle;
}
private static FieldInfo idField = typeof(NotifyIcon).GetField("id", Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance);
private static int GetId(NotifyIcon icon)
{
if (idField == null) throw new InvalidOperationException("[Useful error message]");
return (int)idField.GetValue(icon);
}
}