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;


枚举#region 枚举
[Flags]
private enum NotifyCommand

{
Add = 0,
Modify = 1,
Delete = 2
}

[Flags]
private enum NotifyFlags

{
Message = 1,
Icon = 2,
Tip = 4,
State = 8,
Info = 16
}
#endregion


结构#region 结构
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
private struct NotifyIconData

{
public int cbSize;
public IntPtr hWnd;
public int uID;
public NotifyFlags uFlags;
public int uCallbackMessage;
public IntPtr hIcon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=0x80)]
public string szTip;
public int dwState;
public int dwStateMask;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=0x100)]
public string szInfo;
public int uTimeoutOrVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=0x40)]
public string szInfoTitle;
public ToolTipIcon dwInfoFlags;
}

[StructLayout(LayoutKind.Sequential)]
private class TPMPARAMS

{
public int cbSize;
public int rcExclude_left;
public int rcExclude_top;
public int rcExclude_right;
public int rcExclude_bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT

{
public int x;
public int y;
}
#endregion


Win32 API 引用#region Win32 API 引用

[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr PostMessage(HandleRef hwnd, int msg, int wparam, int lparam);

[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern bool PostMessage(HandleRef hwnd, int msg, IntPtr wparam, IntPtr lparam);

[DllImport("shell32.Dll")]
private static extern int Shell_NotifyIcon(NotifyCommand cmd, ref NotifyIconData data);

[DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
private static extern int TrackPopupMenuEx(HandleRef hMenu, int uFlags,
int x, int y, HandleRef hWnd, TPMPARAMS tpm);

[DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
private static extern int GetCursorPos(ref POINT point);

[DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
private static extern bool SetForegroundWindow(HandleRef hWnd);

[DllImport("user32.dll", CharSet=CharSet.Auto)]
private static extern int RegisterWindowMessage(string msg);

[DllImport("kernel32", EntryPoint="OpenProcess")]
private static extern IntPtr OpenProcess (
int dwDesiredAccess,
IntPtr bInheritHandle,
IntPtr dwProcessId
);
[DllImport("kernel32", EntryPoint="CloseHandle")]
private static extern int CloseHandle (
IntPtr hObject
);
[DllImport("user32", EntryPoint="GetWindowThreadProcessId")]
private static extern IntPtr GetWindowThreadProcessId (
IntPtr hwnd,
ref IntPtr lpdwProcessId
);
[DllImport("user32", EntryPoint="FindWindow")]
private static extern IntPtr FindWindow (
string lpClassName,
string lpWindowName
);
[DllImport("user32", EntryPoint="FindWindowEx")]
private static extern IntPtr FindWindowEx (
IntPtr hWnd1,
IntPtr hWnd2,
string lpsz1,
string lpsz2
);
[DllImport("user32", EntryPoint="SendMessage")]
private static extern int SendMessage (
IntPtr hwnd,
int wMsg,
int wParam,
int lParam
);
[DllImport("user32", EntryPoint="SendMessage")]
private static extern int SendMessage (
IntPtr hwnd,
int wMsg,
int wParam,
IntPtr lParam
);
[DllImport("kernel32", EntryPoint="ReadProcessMemory")]
private static extern int ReadProcessMemory (
IntPtr hProcess,
IntPtr lpBaseAddress,
ref IntPtr lpBuffer,
int nSize,
int lpNumberOfBytesWritten
);
[DllImport("kernel32", EntryPoint="ReadProcessMemory")]
private static extern int ReadProcessMemory (
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] lpBuffer,
int nSize,
int lpNumberOfBytesWritten
);
[DllImport("kernel32", EntryPoint="WriteProcessMemory")]
private static extern int WriteProcessMemory (
IntPtr hProcess,
ref int lpBaseAddress,
ref int lpBuffer,
int nSize,
ref int lpNumberOfBytesWritten
);
[DllImport("kernel32", EntryPoint="VirtualAllocEx")]
private static extern IntPtr VirtualAllocEx (
IntPtr hProcess,
int lpAddress,
int dwSize,
int flAllocationType,
int flProtect
);
[DllImport("kernel32", EntryPoint="VirtualFreeEx")]
private static extern int VirtualFreeEx (
IntPtr hProcess,
IntPtr lpAddress,
int dwSize,
int dwFreeType
);
#endregion

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);
}


属性#region 属性
public ContextMenu ContextMenu

{
get

{
return contextMenu;
}
set

{
contextMenu = value;
}
}

public IntPtr Handle

{
get

{
return this.window.Handle;
}
}

public Icon Icon

{
get

{
return icon;
}
set

{
if (icon != value)

{
icon = value;
UpdateIcon(visible);
}
}
}

public string Text

{
get

{
return text;
}
set

{
if (value == null)

{
value = "";
}
if ((value != null) && !value.Equals(text))

{
if ((value != null) && (value.Length > 63))

{
throw new Exception("");
}
text = value;
if (added)

{
UpdateIcon(true);
}
}
}
}

[DefaultValue(true)]
public bool Visible

{
get

{
return visible;
}
set

{
if (visible != value)

{
UpdateIcon(value);
visible = value;
}
}
}

public ToolTipIcon BalloonTipIcon

{
get

{
return balloonTipIcon;
}
set

{
if (value != balloonTipIcon)

{
balloonTipIcon = value;
}
}
}

public string BalloonTipText

{
get

{
return balloonTipText;
}
set

{
if (value != balloonTipText)

{
balloonTipText = value;
}
}
}

public string BalloonTipTitle

{
get

{
return balloonTipTitle;
}
set

{
if (value != balloonTipTitle)

{
balloonTipTitle = value;
}
}
}

#endregion

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), 16, 0, 0);
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(this, ref 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, 0, 4096, MEM_COMMIT, PAGE_READWRITE);

//得到图标个数
int lButton = SendMessage(ipTray, TB_BUTTONCOUNT, 0, 0);
for(int i = 0; i < lButton; i++)

{
SendMessage(ipTray, TB_GETBUTTON, i, lAddress);
//读文本地址
ReadProcessMemory(hProcess, (IntPtr)(lAddress.ToInt32() + 16), ref lTextAdr, 4, 0);
if(!lTextAdr.Equals(-1))

{
byte[] buff = new byte[1024];
//读文本
ReadProcessMemory(hProcess, lTextAdr, buff, 1024, 0);
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, 4, 0);
ReadProcessMemory(hProcess, ipHandleAdr, ref ipHandle, 4, 0);
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, 0, 0, 0));
}

private void WmMouseMove(ref Message m)

{
OnMouseMove(new MouseEventArgs(Control.MouseButtons, 0, 0, 0, 0));
}

private void WmMouseUp(ref Message m, MouseButtons button)

{
OnMouseUp(new MouseEventArgs(button, 0, 0, 0, 0));
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(null, new 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(this, base.Handle), 16, 0, 0);
}
}

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 + 0x33, 0, 0);
}
Application.Exit();
return;
}
}
frmMain里触发事件:
private bool ntiMain_DoWndProc(object sender, ref System.Windows.Forms.Message m)


{
if(m.Msg == 0x400 + 0x33)


{
ntiMain_DoubleClick(null, null);
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();
}