C# WinForm呼叫Shell_NotifyIcon
阿新 • • 發佈:2020-11-20
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