博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一个新的NotifyIcon

Posted on 2008-01-22 14:33  faib  阅读(1886)  评论(0编辑  收藏  举报
    2003的NotifyIcon没有气泡提示功能,所以扩展了一个新的,能达到2005的NotifyIcon的同样功能,并且提供了两个新的功能。
    静态方法 FindNotifyIcon 在系统托盘里查找提示文本相同的托盘句柄,以便以向它发送消息。
    事件 DoWndProc 托盘WndProc时触发。
    下面是程序清单:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace FaibClass.Windows.Forms
{
    
public sealed class NotifyIcon : Component
    
{
        
private bool added;
        
private ContextMenu contextMenu;
        
private bool doubleClick;
        
private ToolTipIcon balloonTipIcon;
        
private string balloonTipText;
        
private string balloonTipTitle;
        
private IntPtr handle = IntPtr.Zero;

        
private Icon icon;
        
private int id;
        
private static int nextId = 0;
        
private string text;
        
private bool visible = true;
        
private NotifyIconNativeWindow window;
        
private static int WM_TASKBARCREATED = RegisterWindowMessage("TaskbarCreated");
        
private const int WM_USER = 0x400;
        
private const int WM_TRAYMOUSEMESSAGE = 2048;
        
private const int WM_MOUSEMOVE = 0x200;
        
private const int WM_LBUTTONDOWN = 0x201;
        
private const int WM_LBUTTONUP = 0x202;
        
private const int WM_LBUTTONDBLCLK = 0x203;
        
private const int WM_RBUTTONDOWN = 0x204;
        
private const int WM_RBUTTONUP = 0x205;
        
private const int WM_RBUTTONDBLCLK = 0x206;
        
private const int WM_MBUTTONDOWN = 0x207;
        
private const int WM_MBUTTONUP = 0x208;
        
private const int WM_MBUTTONDBLCLK = 0x209;
        
private const int NIN_BALLOONSHOW = 0x402;
        
private const int NIN_BALLOONHIDE = 0x403;
        
private const int NIN_BALLOONTIMEOUT = 0x404;
        
private const int NIN_BALLOONUSERCLICK = 0x405;

        
private const int READ_CONTROL = 0x20000;
        
private const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
        
private const int STANDARD_RIGHTS_READ = READ_CONTROL;
        
private const int STANDARD_RIGHTS_EXECUTE = READ_CONTROL;
        
private const int STANDARD_RIGHTS_ALL = 0x1F0000;
        
private const int STANDARD_RIGHTS_WRITE = READ_CONTROL;
        
private const int SYNCHRONIZE = 0x100000;
        
private const int PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF;
        
private const int PROCESS_TERMINATE = 0x1;

        
private const int PROCESS_VM_OPERATION = 0x8;
        
private const int PROCESS_VM_READ = 0x10;
        
private const int PROCESS_VM_WRITE = 0x20;
        
private const int MEM_RESERVE = 0x2000;
        
private const int MEM_COMMIT = 0x1000;
        
private const int MEM_RELEASE = 0x8000;
        
private const int PAGE_READWRITE = 0x4;

        
private const int TB_BUTTONCOUNT = (WM_USER + 24);
        
private const int TB_HIDEBUTTON = (WM_USER + 4);
        
private const int TB_GETBUTTON = (WM_USER + 23);
        
private const int TB_GETBITMAP = (WM_USER + 44);
        
private const int TB_DELETEBUTTON = (WM_USER + 22);
        
private const int TB_ADDBUTTONS = (WM_USER + 20);
        
private const int TB_INSERTBUTTON = (WM_USER + 21);
        
private const int TB_ISBUTTONHIDDEN = (WM_USER + 12);
        
private const int ILD_NORMAL = 0x0;

        
private const int TPM_NONOTIFY = 0x80;

        
枚举

        
结构

        
Win32 API 引用

        
public event EventHandler Click;
        
public event EventHandler DoubleClick;
        
public event MouseEventHandler MouseDown;
        
public event MouseEventHandler MouseMove;
        
public event MouseEventHandler MouseUp;
        
public event EventHandler BalloonTipClicked;
        
public event EventHandler BalloonTipClosed;
        
public event EventHandler BalloonTipShown;
        
public event WndProcEventHandler DoWndProc;

        
public NotifyIcon()
        
{
            icon 
= null;
            text 
= "";
            id 
= 0;
            added 
= false;
            window 
= null;
            contextMenu 
= null;
            doubleClick 
= false;
            id 
= ++nextId;
            window 
= new NotifyIconNativeWindow(this);
            
//UpdateIcon(visible);
        }


        
public NotifyIcon(IContainer container) : this()
        
{
            container.Add(
this);
        }


        
属性

        
protected override void Dispose(bool disposing)
        
{
            
if (disposing)
            
{
                
if (window != null)
                
{
                    icon 
= null;
                    Text 
= "";
                    UpdateIcon(
false);
                    window.DestroyHandle();
                    window 
= null;
                    contextMenu 
= null;
                }

            }

            
else if ((window != null&& (window.Handle != IntPtr.Zero))
            
{
                PostMessage(
new HandleRef(window, window.Handle), 1600);
                window.ReleaseHandle();
            }

            
base.Dispose(disposing);
        }


        
private void OnBalloonTipClicked()
        
{
            
if (BalloonTipClicked != null)
                BalloonTipClicked(
this, EventArgs.Empty);
        }


        
private void OnBalloonTipClosed()
        
{
            
if (BalloonTipClosed != null)
                BalloonTipClosed(
this, EventArgs.Empty);
        }


        
private void OnBalloonTipShown()
        
{
            
if (BalloonTipShown != null)
                BalloonTipShown(
this, EventArgs.Empty);
        }


        
private void OnClick(EventArgs e)
        
{
            
if (Click != null)
                Click(
this, e);
        }


        
private void OnDoubleClick(EventArgs e)
        
{
            
if (DoubleClick != null)
                DoubleClick(
this, e);
        }


        
private void OnMouseDown(MouseEventArgs e)
        
{
            
if (MouseDown != null)
                MouseDown(
this, e);
        }


        
private void OnMouseMove(MouseEventArgs e)
        
{
            
if (MouseMove != null)
                MouseMove(
this, e);
        }


        
private void OnMouseUp(MouseEventArgs e)
        
{
            
if (MouseUp != null)
                MouseUp(
this, e);
        }


        
private bool OnDoWndProc(ref Message m)
        
{
            
if (DoWndProc != null)
                
return DoWndProc(thisref m);
            
return true;
        }


        
private void ShowContextMenu()
        
{
            
if (contextMenu != null)
            
{
                POINT pt 
= new POINT();
                GetCursorPos(
ref pt);
                SetForegroundWindow(
new HandleRef(window, window.Handle));
                contextMenu.GetType().InvokeMember(
"OnPopup",
                    BindingFlags.NonPublic 
| BindingFlags.InvokeMethod | BindingFlags.Instance,
                    
null, contextMenu, new Object[] {new EventArgs()});
                TrackPopupMenuEx(
new HandleRef(contextMenu, contextMenu.Handle), TPM_NONOTIFY, pt.x, pt.y, new HandleRef(window, window.Handle), null);
                PostMessage(
new HandleRef(window, window.Handle), 0, IntPtr.Zero, IntPtr.Zero);
            }

        }


        
private void UpdateIcon(bool showIconInTray)
        
{
            
lock (this)
            
{
                
if (!base.DesignMode)
                
{
                    window.LockReference(showIconInTray);
                    NotifyIconData pnid 
= new NotifyIconData();
                    pnid.uCallbackMessage 
= 0x800;
                    pnid.uFlags 
= NotifyFlags.Message;
                    
if (showIconInTray && (window.Handle == IntPtr.Zero))
                    
{
                        window.CreateHandle(
new CreateParams());
                    }

                    pnid.cbSize 
=Marshal.SizeOf(pnid);
                    pnid.hWnd 
= window.Handle;
                    pnid.uID 
= id;
                    pnid.hIcon 
= IntPtr.Zero;
                    pnid.szTip 
= null;
                    
if (icon != null)
                    
{
                        pnid.uFlags 
|= NotifyFlags.Icon;
                        pnid.hIcon 
= icon.Handle;
                    }

                    pnid.uFlags 
|= NotifyFlags.Tip;
                    pnid.szTip 
= text;
                    
if (showIconInTray && (icon != null))
                    
{
                        
if (!added)
                        
{
                            Shell_NotifyIcon(NotifyCommand.Add, 
ref pnid);
                            added 
= true;
                        }

                        
else
                        
{
                            Shell_NotifyIcon(NotifyCommand.Modify, 
ref pnid);
                        }

                    }

                    
else if (added)
                    
{
                        Shell_NotifyIcon(NotifyCommand.Delete, 
ref pnid);
                        added 
= false;
                    }

                }

            }

        }


        
public static IntPtr FindNotifyIcon(string TipTitle)
        
{
            
if(TipTitle.Length == 0)return IntPtr.Zero;
            IntPtr pid 
= IntPtr.Zero;
            IntPtr ipHandle 
= IntPtr.Zero; //图标句柄
            IntPtr lTextAdr = IntPtr.Zero; //文本内存地址
            IntPtr ipTemp = FindWindow("Shell_TrayWnd"null);
            
//找到托盘
            ipTemp = FindWindowEx(ipTemp, IntPtr.Zero, "TrayNotifyWnd"null);
            ipTemp 
= FindWindowEx(ipTemp, IntPtr.Zero, "SysPager"null);
            IntPtr ipTray 
= FindWindowEx(ipTemp, IntPtr.Zero, "ToolbarWindow32"null);
            
            GetWindowThreadProcessId(ipTray, 
ref pid);
            
if(pid.Equals(0))return ipHandle;

            IntPtr hProcess 
= OpenProcess(PROCESS_ALL_ACCESS | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, IntPtr.Zero, pid);
            IntPtr lAddress 
= VirtualAllocEx(hProcess, 04096, MEM_COMMIT, PAGE_READWRITE);

            
//得到图标个数
            int lButton = SendMessage(ipTray, TB_BUTTONCOUNT, 00);
            
for(int i = 0; i < lButton; i++)
            
{
                SendMessage(ipTray, TB_GETBUTTON, i, lAddress);
                
//读文本地址
                ReadProcessMemory(hProcess, (IntPtr)(lAddress.ToInt32() + 16), ref lTextAdr, 40);
                
if(!lTextAdr.Equals(-1))
                
{
                    
byte[] buff = new byte[1024];
                    
//读文本
                    ReadProcessMemory(hProcess, lTextAdr, buff, 10240);
                    
string title = System.Text.ASCIIEncoding.Unicode.GetString(buff);
                    
// 从字符0处截断
                    int nullindex = title.IndexOf("\0");
                    
if(nullindex > 0)
                    
{
                        title 
= title.Substring(0, nullindex);
                    }

                    
//ReadProcessMemory(hProcess, lAddress, ref ipButtonID, 4, 0);
                    
//判断是不是要找的图标
                    if(title.Equals(TipTitle))
                    
{
                        IntPtr ipHandleAdr 
= IntPtr.Zero;
                        
//读句柄地址
                        ReadProcessMemory(hProcess, (IntPtr)(lAddress.ToInt32() + 12), ref ipHandleAdr, 40);
                        ReadProcessMemory(hProcess, ipHandleAdr, 
ref ipHandle, 40);
                        
break;
                    }

                }

            }

            VirtualFreeEx(hProcess, lAddress, 
4096, MEM_RELEASE);
            CloseHandle(hProcess);
            
return ipHandle;
        }


        
public void ShowBalloonTip(int timeout)
        
{
            ShowBalloonTip(balloonTipTitle, balloonTipText, balloonTipIcon, timeout);
        }


        
public void ShowBalloonTip(string tipTitle, string tipText, ToolTipIcon tipIcon, int timeout)
        
{
            
if (timeout < 0)
            
{
                
throw new ArgumentOutOfRangeException("timeout""超时时间不错误");
            }

            
if (tipText == null || tipText.Length == 0)
            
{
                
throw new ArgumentException("显示的信息不能为空");
            }

            
if (added && !base.DesignMode)
            
{
                NotifyIconData pnid 
= new NotifyIconData();
                
if (window.Handle == IntPtr.Zero)
                
{
                    
//创建窗体
                    window.CreateHandle(new CreateParams());
                }

                pnid.cbSize 
=Marshal.SizeOf(pnid);
                pnid.hWnd 
= window.Handle;
                pnid.uID 
= id;
                pnid.uFlags 
= NotifyFlags.Info;
                pnid.uTimeoutOrVersion 
= timeout;
                pnid.szInfoTitle 
= tipTitle;
                pnid.szInfo 
= tipText;
                pnid.dwInfoFlags 
= tipIcon;
                Shell_NotifyIcon(NotifyCommand.Modify, 
ref pnid);
            }

            
//延时后更新图标
            System.Threading.Thread.Sleep(50);
            UpdateIcon(visible);
        }


        
private void WmMouseDown(ref Message m, MouseButtons button, int clicks)
        
{
            
if (clicks == 2//双击
            {
                OnDoubleClick(EventArgs.Empty);
                doubleClick 
= true;
            }

            OnMouseDown(
new MouseEventArgs(button, clicks, 000));
        }


        
private void WmMouseMove(ref Message m)
        
{
            OnMouseMove(
new MouseEventArgs(Control.MouseButtons, 0000));
        }


        
private void WmMouseUp(ref Message m, MouseButtons button)
        
{
            OnMouseUp(
new MouseEventArgs(button, 0000));
            
if (!doubleClick)
            
{
                OnClick(EventArgs.Empty);
            }

            doubleClick 
= false;
        }


        
private void WmTaskbarCreated(ref Message m)
        
{
            added 
= false;
            UpdateIcon(visible);
        }


        
private void WndProc(ref Message msg)
        
{
            
if(!OnDoWndProc(ref msg))return//中断消息
            Console.WriteLine(msg.Msg);
            
switch (msg.Msg)
            
{
                
case 0x2b://WM_DRAWITEM
                    break;
                
case 0x2c://WM_MEASUREITEM
                    break;
                
case 0x111://WM_COMMAND
                    if (IntPtr.Zero == msg.LParam)
                    
{
                        Type typeCmd 
= typeof(Form).Assembly.GetType("System.Windows.Forms.Command");
                        MethodInfo methodDispId 
= typeCmd.GetMethod("DispatchID", BindingFlags.Static | BindingFlags.Public);
                        
int code = ((int)msg.WParam) & 0xffff;
                        
bool res = (bool)methodDispId.Invoke(nullnew object[]{code});
                        
break;
                    }

                    window.DefWndProc(
ref msg);
                    
return;
                
case 0x800://WM_TRAYMOUSEMESSAGE:
                    switch ((int)msg.LParam)
                    
{
                        
case WM_MOUSEMOVE:
                            
this.WmMouseMove(ref msg);
                            
return;

                        
case WM_LBUTTONDOWN:
                            
this.WmMouseDown(ref msg, MouseButtons.Left, 1);
                            
return;

                        
case WM_LBUTTONUP:
                            
this.WmMouseUp(ref msg, MouseButtons.Left);
                            
return;

                        
case WM_LBUTTONDBLCLK:
                            
this.WmMouseDown(ref msg, MouseButtons.Left, 2);
                            
return;

                        
case WM_RBUTTONDOWN:
                            
this.WmMouseDown(ref msg, MouseButtons.Right, 1);
                            
return;

                        
case WM_RBUTTONUP:
                            
if (this.contextMenu != null)
                            
{
                                
this.ShowContextMenu();
                            }

                            
this.WmMouseUp(ref msg, MouseButtons.Right);
                            
return;

                        
case WM_RBUTTONDBLCLK:
                            
this.WmMouseDown(ref msg, MouseButtons.Right, 2);
                            
return;

                        
case WM_MBUTTONDOWN:
                            
this.WmMouseDown(ref msg, MouseButtons.Middle, 1);
                            
return;

                        
case WM_MBUTTONUP:
                            
this.WmMouseUp(ref msg, MouseButtons.Middle);
                            
return;

                        
case WM_MBUTTONDBLCLK:
                            
this.WmMouseDown(ref msg, MouseButtons.Middle, 2);
                            
return;

                        
case NIN_BALLOONSHOW:
                            
this.OnBalloonTipShown();
                            
return;

                        
case NIN_BALLOONHIDE:
                            
this.OnBalloonTipClosed();
                            
return;

                        
case NIN_BALLOONTIMEOUT:
                            
this.OnBalloonTipClosed();
                            
return;

                        
case NIN_BALLOONUSERCLICK:
                            
this.OnBalloonTipClicked();
                            
return;

                        }

                    
return;
                
default:
                    
if (msg.Msg == WM_TASKBARCREATED)
                    
{
                        WmTaskbarCreated(
ref msg);
                    }

                    window.DefWndProc(
ref msg);
                    
break;
            }

        }


        
private class NotifyIconNativeWindow : NativeWindow
        
{
            
internal NotifyIcon reference;
            
private GCHandle rootRef;

            
internal NotifyIconNativeWindow(NotifyIcon control)
            
{
                reference 
= control;
            }


            
~ NotifyIconNativeWindow()
            
{
                
if (base.Handle != IntPtr.Zero)
                
{
                    PostMessage(
new HandleRef(thisbase.Handle), 1600);
                }

            }


            
public void LockReference(bool locked)
            
{
                
if (locked)
                
{
                    
if (!rootRef.IsAllocated)
                    
{
                        rootRef 
= GCHandle.Alloc(reference, GCHandleType.Normal);
                    }

                }

                
else if (rootRef.IsAllocated)
                
{
                    rootRef.Free();
                }

            }


            
protected override void OnThreadException(Exception e)
            
{
                Application.OnThreadException(e);
            }


            
protected override void WndProc(ref Message m)
            
{
                reference.WndProc(
ref m);
            }

        }

    }

    
    [Flags]
    
public enum ToolTipIcon
    
{
        None 
= 0,
        Info 
= 1,
        Warning 
= 2,
        Error 
= 3
    }


    
public delegate bool WndProcEventHandler (object sender, ref Message m);

}

比如使用它可以使程序只运行一个实例:
[STAThread]  
static void Main(params string[] pars)   
{  
//确保只有一个实例运行  
Process current = Process.GetCurrentProcess();  
Process[] ps 
= Process.GetProcessesByName(current.ProcessName);  
foreach(Process process in ps)  
{  
if(!process.Id.Equals(current.Id))  
{  
IntPtr nti 
= FbSoft.Assistant.Controls.NotifyIcon.FindNotifyIcon("NotifyIcon标题");  
if(nti != IntPtr.Zero)  
{  
//向托盘发送自定消息 WM_USER + 0x33;  
Win32.SendMessage(nti, 0x400 + 0x3300);  
}
  
Application.Exit();  
return;  
}
  
}
 frmMain里触发事件:
private bool ntiMain_DoWndProc(object sender, ref System.Windows.Forms.Message m)  
{  
if(m.Msg == 0x400 + 0x33)  
{  
ntiMain_DoubleClick(
nullnull);  
return false;  
}
  
return true;  
}
  

private void ntiMain_DoubleClick(object sender, System.EventArgs e)  
{  
if(Visible)return;  
Thread.Sleep(
100);  
isLoad 
= true;  
if(!ShowInTaskbar)  
{  
ShowInTaskbar 
= true;  
}
  
isLoad 
= false;  
Win32.SetForegroundWindow(Handle);  
Win32.SetActiveWindow(Handle);  
WindowState 
= FormWindowState.Maximized;  
Show();  
}