c#的任务栏托盘图标控件NotifyIcon只有MouseMove事件,MouseMove事件刷新很快,很不好用,而且我们有时需要鼠标进入和离开的事件,但是不知道c#怎么回事,没有提供,那么就只能自己来处理了。
解决鼠标进入和离开的思路是:
1.通过MouseMove事件确定当前鼠标已经进入托盘图标的范围
2.进入后启动检测timer
3.定时检测托盘图标的位置和当前鼠标的位置,判断鼠标是否在托盘图标的范围内
主要难点:获取当前托盘图标的位置
获取托盘图标位置的思路:
1.查找到托盘图标所在的窗口
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
private IntPtr FindTrayToolbarWindow() { IntPtr hWnd = FindWindow("Shell_TrayWnd", null); if (hWnd != IntPtr.Zero) { hWnd = FindWindowEx(hWnd, IntPtr.Zero, "TrayNotifyWnd", null); if (hWnd != IntPtr.Zero) { hWnd = FindWindowEx(hWnd, IntPtr.Zero, "SysPager", null); if (hWnd != IntPtr.Zero) { hWnd = FindWindowEx(hWnd, IntPtr.Zero, "ToolbarWindow32", null); } } } return hWnd; }
2.遍历窗口内的托盘图标
3.获取当前托盘图标的句柄,通过句柄得到这个托盘图标所关联的进程id
4.通过进程id比较获取到当前程序的托盘图标
5.拖过api获取当前托盘图标相对于它所在窗口的位置
6.获取窗口在整个屏幕中的位置,在计算出托盘图标相对于屏幕的位置
2-6代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
private bool FindNotifyIcon(IntPtr hTrayWnd, ref Rect rectNotify) { UInt32 trayPid = 0; Rect rectTray = new Rect(); GetWindowRect(hTrayWnd, out rectTray); int count = (int) SendMessage(hTrayWnd, TB_BUTTONCOUNT, 0, IntPtr.Zero); //给托盘窗口发消息,得到托盘里图标 bool isFind = false; if (count > 0) { GetWindowThreadProcessId(hTrayWnd, out trayPid); //取得托盘窗口对应的进程id //获取托盘图标的位置 IntPtr hProcess = OpenProcess(ProcessAccess.VMOperation | ProcessAccess.VMRead | ProcessAccess.VMWrite, false, trayPid); //打开进程,取得进程句柄 IntPtr address = VirtualAllocEx(hProcess, //在目标进程中申请一块内存,放TBBUTTON信息 IntPtr.Zero, 1024, AllocationType.Commit, MemoryProtection.ReadWrite); TBBUTTON btnData = new TBBUTTON(); TRAYDATA trayData = new TRAYDATA(); // Console.WriteLine("Count:"+count); var handel = Process.GetCurrentProcess().Id; // Console.WriteLine("curHandel:" + handel); for (uint j = 0; j < count; j++) { // Console.WriteLine("j:"+j); var i = j; SendMessage(hTrayWnd, TB_GETBUTTON, i, address); //取得TBBUTTON结构到本地 int iTmp = 0; var isTrue = ReadProcessMemory(hProcess, address, out btnData, Marshal.SizeOf(btnData), out iTmp); if (isTrue == false) continue; //这一步至关重要,不能省略 //主要解决64位系统电脑运行的是x86的程序 if (btnData.dwData == IntPtr.Zero) { btnData.dwData = btnData.iString; } ReadProcessMemory(hProcess, //从目标进程address处存放的是TBBUTTON btnData.dwData, //取dwData字段指向的TRAYDATA结构 out trayData, Marshal.SizeOf(trayData), out iTmp); UInt32 dwProcessId = 0; GetWindowThreadProcessId(trayData.hwnd, //通过TRAYDATA里的hwnd字段取得本图标的进程id out dwProcessId); //获取当前进程id // StringBuilder sb = new StringBuilder(256); // GetModuleFileNameEx(OpenProcess(ProcessAccess.AllAccess, false, dwProcessId), IntPtr.Zero, sb, 256); // Console.WriteLine(sb.ToString()); if (dwProcessId == (UInt32) handel) { Rect rect = new Rect(); IntPtr lngRect = VirtualAllocEx(hProcess, //在目标进程中申请一块内存,放TBBUTTON信息 IntPtr.Zero, Marshal.SizeOf(typeof (Rect)), AllocationType.Commit, MemoryProtection.ReadWrite); i = j; SendMessage(hTrayWnd, TB_GETITEMRECT, i, lngRect); isTrue = ReadProcessMemory(hProcess, lngRect, out rect, Marshal.SizeOf(rect), out iTmp); //释放内存 VirtualFreeEx(hProcess, lngRect, Marshal.SizeOf(rect), FreeType.Decommit); VirtualFreeEx(hProcess, lngRect, 0, FreeType.Release); int left = rectTray.Left + rect.Left; int top = rectTray.Top + rect.Top; int botton = rectTray.Top + rect.Bottom; int right = rectTray.Left + rect.Right; rectNotify = new Rect(); rectNotify.Left = left; rectNotify.Right = right; rectNotify.Top = top; rectNotify.Bottom = botton; isFind = true; break; } } VirtualFreeEx(hProcess, address, 0x4096, FreeType.Decommit); VirtualFreeEx(hProcess, address, 0, FreeType.Release); CloseHandle(hProcess); } return isFind; }
7.如果没有找到,那么需要用相同的方法在托盘溢出区域内查找
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)