C# WinForm调用Shell_NotifyIcon教程
1 public class InnerClass: Form
2 {
3 private Shell_NotifyIconEx servicesClass = null; // 接受主CLASS 的实例句柄
4 internal InnerClass(Shell_NotifyIconEx _servicesClass)
5 {
6 servicesClass = _servicesClass;
7 }
8
9 private const int WM_LBUTTONDOWN = 0x0201; // 左键
10 private const int WM_RBUTTONDOWN = 0x204; // 右键
11 private const int WM_MBUTTONDOWN = 0x207; // 中键
12
13 [DllImport("user32.dll", EntryPoint = "TrackPopupMenu")]
14 private static extern int TrackPopupMenu( // c# 和vb.net 好象没有了随地popup 了,只要请它老人家出马了
15 IntPtr hMenu,
16 int wFlags,
17 int x,
18 int y,
19 int nReserved,
20 IntPtr hwnd,
21 ref RECT lprc
22 );
23
24 [StructLayout(LayoutKind.Sequential)]
25 private struct RECT
26 { // 上面那位用的结构,表示前弹出菜单可用的一个范围大小(一般是全屏幕都让它用,留着搞游戏或视频对话之类的朋友指定菜单可用的范围)
27 internal int Left;
28 internal int Top;
29 internal int Right;
30 internal int Bottom;
31 }
32
33 protected override void WndProc(ref Message msg)
34 {
35 if (msg.Msg == servicesClass.WM_NOTIFY_TRAY)
36 { // 如果消息相符
37 if ((int)msg.WParam == servicesClass.uID)
38 { // 并且消息的WParam 相符
39 MouseButtons mb =MouseButtons.None;
40 if ((int)msg.LParam == WM_LBUTTONDOWN)
41 { //如果点击的是左键
42 mb =MouseButtons.Left;
43 }
44 else if ((int)msg.LParam == WM_MBUTTONDOWN)
45 { //中键
46 mb =MouseButtons.Middle;
47 }
48 else if ((int)msg.LParam == WM_RBUTTONDOWN)
49 { //右键
50 if (servicesClass.contextMenuHwnd != IntPtr.Zero)
51 { //如果有定义过菜单关联
52 RECT r = new RECT();
53 r.Left = Screen.PrimaryScreen.WorkingArea.Left;
54 r.Right =Screen.PrimaryScreen.WorkingArea.Right;
55 r.Top =Screen.PrimaryScreen.WorkingArea.Top;
56 r.Bottom =Screen.PrimaryScreen.WorkingArea.Right;
57
58 TrackPopupMenu(
59 servicesClass.contextMenuHwnd,
60 2,
61 Cursor.Position.X,
62 Cursor.Position.Y,
63 0,
64 servicesClass.formHwnd,
65 ref r
66 );
67 }
68 else
69 { //如果没有定义过菜单关联
70 mb =MouseButtons.Right;
71 }
72 }
73
74 if (mb !=MouseButtons.None && servicesClass._delegateOfCallBack != null)
75 {
76 servicesClass._delegateOfCallBack(mb); // 执行回调
77 return;
78 }
79 }
80 }
81
82 base.WndProc(ref msg);
83 }
84 }
1 public class Shell_NotifyIconEx
2 {
3 /// <summary>
4 /// ArLi, last fix: 2003.9.12, reference: ArLi.CommonPrj Lib @ http://zpcity.com/arli/
5 /// </summary>
6 public static readonly System.Version myVersion = new System.Version(1, 2); //版本声明
7
8 private readonly InnerClass formTmp = null; // 这个很重要,不能放在构造里,因为它必须和此实例同等生存期才不会被中止消息循环
9 private readonly IntPtr formTmpHwnd = IntPtr.Zero; // 这是上一行的句柄
10 private readonly bool VersionOk = false; // 这是一个由VersionPass 返回的属性,它允许开发者检测当前机子的Shell32.dll(可能在win95 或未知平台上版本) 合适此组,不符则用.net 自己的notifyicon
11 private bool forgetDelNotifyBox = false; // 这是一个私有标志,它允许开发者在程序退出时忘记调用DelNotifyBox 来清除图标时会自动在析构里清掉它。
12
13 internal IntPtr formHwnd = IntPtr.Zero; // 这是调用此组件的主窗口句柄(当前实例有效,可多个icon 不冲突)
14 internal IntPtr contextMenuHwnd = IntPtr.Zero; // 这是菜单的句柄(当前实例有效,可多个icon 不冲突)
15
16 internal delegate void delegateOfCallBack(System.Windows.Forms.MouseButtons mb);
17 internal delegateOfCallBack _delegateOfCallBack = null;
18
19 public Shell_NotifyIconEx() // 构造
20 {
21 WM_NOTIFY_TRAY += 1; // 消息ID +1,避免多个ICON 消息处理冲突
22 uID += 1; // 同上
23 formTmp = new InnerClass(this); // 新实例一个消息循环
24 formTmpHwnd = formTmp.Handle; // 新实例句柄
25 VersionOk = this.GetShell32VersionInfo() >= 5; // 版本是否合适,此组件由于重点在气泡提示,它要求Shell32.dll 5.0(ie 5.0) 以上
26 }
27
28 ~Shell_NotifyIconEx()
29 { // 析构
30 if (forgetDelNotifyBox) this.DelNotifyBox(); //如果开发者忘记则清理icon
31 }
32
33 #region API_Consts
34 internal readonly int WM_NOTIFY_TRAY = 0x0400 + 2001; //readonly 表示只在构造可付值
35 internal readonly int uID = 5000;
36
37 // 常数定义,有VC 的可以参见 shellapi.h
38 private const int NIIF_NONE = 0x00;
39 private const int NIIF_INFO = 0x01;
40 private const int NIIF_WARNING = 0x02;
41 private const int NIIF_ERROR = 0x03;
42
43 private const int NIF_MESSAGE = 0x01;
44 private const int NIF_ICON = 0x02;
45 private const int NIF_TIP = 0x04;
46 private const int NIF_STATE = 0x08;
47 private const int NIF_INFO = 0x10;
48
49 private const int NIM_ADD = 0x00;
50 private const int NIM_MODIFY = 0x01;
51 private const int NIM_DELETE = 0x02;
52 private const int NIM_SETFOCUS = 0x03;
53 private const int NIM_SETVERSION = 0x04;
54
55 private const int NIS_HIDDEN = 0x01;
56 private const int NIS_SHAREDICON = 0x02;
57
58 private const int NOTIFYICON_OLDVERSION = 0x00;
59 private const int NOTIFYICON_VERSION = 0x03;
60
61 [DllImport("shell32.dll", EntryPoint = "Shell_NotifyIcon")]
62 private static extern bool Shell_NotifyIcon( // 这位是主角
63 int dwMessage,
64 ref NOTIFYICONDATA lpData
65 );
66
67 /// <summary>
68 /// 此API 的作用是当 this.focus() 无效时可以考虑使用,效果很好
69 /// </summary>
70 /// <param name="hwnd">this.Handle, 当前窗体句柄</param>
71 [DllImport("user32.dll", EntryPoint = "SetForegroundWindow")]
72 public static extern int SetForegroundWindow(
73 IntPtr hwnd
74 );
75
76 [StructLayout(LayoutKind.Sequential)]
77 private struct NOTIFYICONDATA
78 { // 主角用的结构
79 internal int cbSize;
80 internal IntPtr hwnd;
81 internal int uID;
82 internal int uFlags;
83 internal int uCallbackMessage;
84 internal IntPtr hIcon;
85 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x80)]
86 internal string szTip;
87 internal int dwState; // 这里往下几个是 5.0 的精华
88 internal int dwStateMask;
89 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0xFF)]
90 internal string szInfo;
91 internal int uTimeoutAndVersion;
92 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x40)]
93 internal string szInfoTitle;
94 internal int dwInfoFlags;
95 }
96 #endregion
97
98 /// <summary>
99 /// 建一个结构
100 /// </summary>
101 private NOTIFYICONDATA GetNOTIFYICONDATA(IntPtr iconHwnd, string sTip, string boxTitle, string boxText)
102 {
103 NOTIFYICONDATA nData = new NOTIFYICONDATA();
104
105 nData.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(nData); // 结构的大小
106 nData.hwnd = formTmpHwnd; // 处理消息循环的窗体句柄,可以移成主窗体
107 nData.uID = uID; // 消息的 WParam,回调时用
108 nData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_INFO; // 标志,表示由消息、图标、提示、信息组成
109 nData.uCallbackMessage = WM_NOTIFY_TRAY; // 消息ID,回调用
110 nData.hIcon = iconHwnd; // 图标的句柄,有兴趣的话可以定时改变它变成动画ICON
111 nData.uTimeoutAndVersion = 10 * 1000 | NOTIFYICON_VERSION; // 提示的超时值(几秒后自动消失)和版本
112 nData.dwInfoFlags = NIIF_INFO; // 类型标志,有INFO、WARNING、ERROR,更改此值将影响气泡提示框的图标类型
113
114 nData.szTip = sTip; // 图标的提示信息
115 nData.szInfoTitle = boxTitle; // 气泡提示框的标题
116 nData.szInfo = boxText; // 气泡提示框的提示内容
117
118 return nData; // 这个嘛。。。
119 }
120
121 private int GetShell32VersionInfo()
122 { // 返回shell32 的版本
123 FileInfo fi = new FileInfo(Path.Combine(System.Environment.SystemDirectory, "shell32.dll")); //将来的平台shell32 放哪目前不得而知,碰到再改
124 if (fi.Exists)
125 {
126 FileVersionInfo theVersion = FileVersionInfo.GetVersionInfo(fi.FullName);
127 int i = theVersion.FileVersion.IndexOf('.');
128 if (i > 0)
129 {
130 try
131 {
132 return int.Parse(theVersion.FileVersion.Substring(0, i));
133 }
134 catch { }
135 }
136 }
137 return 0;
138 }
139
140 /// <summary>
141 /// 加一个新图标
142 /// </summary>
143 /// <param name="iconHwnd">图标句柄</param>
144 /// <param name="sTip">提示, 5.0 最大: 128 char</param>
145 /// <param name="boxTitle">气泡标题, 最大: 64 char</param>
146 /// <param name="boxText">气泡内容, 最大: 256 char</param>
147 /// <returns>成功、失败或错误(-1)</returns>
148 public int AddNotifyBox(IntPtr iconHwnd, string sTip, string boxTitle, string boxText)
149 {
150 if (!this.VersionOk) return -1;
151
152 NOTIFYICONDATA nData = GetNOTIFYICONDATA(iconHwnd, sTip, boxTitle, boxText);
153 if (Shell_NotifyIcon(NIM_ADD, ref nData))
154 {
155 this.forgetDelNotifyBox = true;
156 return 1;
157 }
158 else
159 {
160 return 0;
161 }
162 }
163
164 /// <summary>
165 /// 和add 差不多,不重复了
166 /// </summary>
167 public int DelNotifyBox()
168 {
169 if (!this.VersionOk) return -1;
170
171 NOTIFYICONDATA nData = GetNOTIFYICONDATA(IntPtr.Zero, null, null, null);
172 if (Shell_NotifyIcon(NIM_DELETE, ref nData))
173 {
174 this.forgetDelNotifyBox = false;
175 return 1;
176 }
177 else
178 {
179 return 0;
180 }
181 }
182
183 public int ModiNotifyBox(IntPtr iconHwnd, string sTip, string boxTitle, string boxText)
184 {
185 if (!this.VersionOk) return -1;
186
187 NOTIFYICONDATA nData = GetNOTIFYICONDATA(iconHwnd, sTip, boxTitle, boxText);
188 return Shell_NotifyIcon(NIM_MODIFY, ref nData) ? 1 : 0;
189 }
190
191 #region Optional Module //这里是可选方法
192 /// <summary>
193 /// 连接一个已存在的 contextMenu
194 /// </summary>
195 /// <param name="_formHwnd">窗体句柄,用来处理菜单的消息</param>
196 /// <param name="_contextMenuHwnd">菜单的句柄</param>
197 public void ConnectMyMenu(IntPtr _formHwnd, IntPtr _contextMenuHwnd)
198 {
199 formHwnd = _formHwnd;
200 contextMenuHwnd = _contextMenuHwnd;
201 }
202
203 /// <summary>
204 /// 立即清理掉图标、委托和formtmp 资源(好象没什么资源,考虑到可能二次开发挂接就开了这个东东)
205 /// </summary>
206 public void Dispose()
207 {
208 _delegateOfCallBack = null;
209 this.formTmp.Dispose();
210 }
211
212 /// <summary>
213 /// 版本适合
214 /// </summary>
215 public bool VersionPass
216 {
217 get
218 {
219 return this.VersionOk;
220 }
221 }
222 #endregion
223 }
用法示例:
1 private void button2_Click (object sender, System.EventArgs e) {
2 Shell_NotifyIconEx ().AddNotifyBox (this.Icon.Handle, this.Text, "这是标题", "单击这里开始,我将带你畅游API 世界");
3 }
private void GetPoc1 (MouseButtons mb) { // 回调处理
if (mb == MouseButtons.Left) {
MessageBox.Show ("来自菜单1");
}
}
privateShell_NotifyIconEx o1 = newShell_NotifyIconEx (); //这个放外面是用在 o.DelNotifyBox
private void button1_Click (object sender, System.EventArgs e) {
o1.AddNotifyBox (this.Icon.Handle, this.Text, "菜单1", "单击这里开始,我将带你畅游API 世界");
o1.ConnectMyMenu (this.Handle, this.contextMenu1.Handle); // 挂上菜单,可选
o1._delegateOfCallBack = newShell_NotifyIconEx.delegateOfCallBack (GetPoc1); //定义回调
}
private void GetPoc1(MouseButtons mb) { // 回调处理
if (mb == MouseButtons.Left) {
MessageBox.Show("来自菜单1");
}
}
private Shell_NotifyIconEx o1 = new Shell_NotifyIconEx(); //这个放外面是用在 o.DelNotifyBox
private void button1_Click(object sender, System.EventArgs e) {
o1.AddNotifyBox(this.Icon.Handle,this.Text,"菜单1","单击这里开始,我将带你畅游API 世界");
o1.ConnectMyMenu(this.Handle,this.contextMenu1.Handle); // 挂上菜单,可选
o1._delegateOfCallBack = new Shell_NotifyIconEx.delegateOfCallBack(GetPoc1); //定义回调
}
private void GetPoc2(MouseButtons mb) {
if (mb == MouseButtons.Left) {
MessageBox.Show("来自菜单2");
}
}
private Shell_NotifyIconEx o2 = new Shell_NotifyIconEx(); //第二个nofityicon 和上面一样
private void button2_Click(object sender, System.EventArgs e) {
o2.AddNotifyBox(this.Icon.Handle,this.Text,"菜单2","单击这里开始,我将带你畅游API 世界");
o2.ConnectMyMenu(this.Handle,this.contextMenu2.Handle);
o2._delegateOfCallBack = new Shell_NotifyIconEx.delegateOfCallBack(GetPoc2);
}
本文来自:http://blog.sina.com.cn/s/blog\_6c0affba0100pi0e.html