1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > c# 任务栏托盘图标鼠标进入MouseEnter和鼠标离开MouseLeave实现

c# 任务栏托盘图标鼠标进入MouseEnter和鼠标离开MouseLeave实现

时间:2022-07-01 22:58:13

相关推荐

c# 任务栏托盘图标鼠标进入MouseEnter和鼠标离开MouseLeave实现

c#的任务栏托盘图标控件NotifyIcon只有MouseMove事件,MouseMove事件刷新很快,很不好用,而且我们有时需要鼠标进入和离开的事件,但是不知道c#怎么回事,没有提供,那么就只能自己来处理了。

解决鼠标进入和离开的思路是:

1.通过MouseMove事件确定当前鼠标已经进入托盘图标的范围

2.进入后启动检测timer

3.定时检测托盘图标的位置和当前鼠标的位置,判断鼠标是否在托盘图标的范围内

主要难点:获取当前托盘图标的位置

获取托盘图标位置的思路:

1.查找到托盘图标所在的窗口

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

View Code

2.遍历窗口内的托盘图标

3.获取当前托盘图标的句柄,通过句柄得到这个托盘图标所关联的进程id

4.通过进程id比较获取到当前程序的托盘图标

5.拖过api获取当前托盘图标相对于它所在窗口的位置

6.获取窗口在整个屏幕中的位置,在计算出托盘图标相对于屏幕的位置

2-6代码:

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,mit,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处存放的是TBBUTTONbtnData.dwData, //取dwData字段指向的TRAYDATA结构out trayData,Marshal.SizeOf(trayData),out iTmp);UInt32 dwProcessId = 0;GetWindowThreadProcessId(trayData.hwnd, //通过TRAYDATA里的hwnd字段取得本图标的进程idout 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)),mit,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;}

View Code

7.如果没有找到,那么需要用相同的方法在托盘溢出区域内查找

private IntPtr FindTrayToolbarOverFlowWindow(){IntPtr hWnd = FindWindow("NotifyIconOverflowWindow", null);if (hWnd != IntPtr.Zero){hWnd = FindWindowEx(hWnd, IntPtr.Zero, "ToolbarWindow32", null);}return hWnd;}

View Code

在查找中的难点:

1.对于32位操作系统和64位操作系统,系统内部处理方式不一样,所以许多时候当去取TBBUTTON结构到本地的时候得到的地址为0,这里查询了一些资料,网上一些资料TBBUTTON的结构体如下:

[StructLayout(LayoutKind.Sequential, Pack = 1)]public struct TBBUTTON{public int iBitmap;public int idCommand;public byte fsState;public byte fsStyle;public byte bReserved0;public byte bReserved1;public IntPtr dwData;public IntPtr iString;}

View Code

这个在32位下面没有问题,但是在64位系统下就出现了问题,后面参考网上一些资料,原来问题出在中间4个byte中,由于32位系统中4个byte刚好32位,但是在64位中这里就不对,所以就修改为如下:

[StructLayout(LayoutKind.Sequential, Pack = 1)]public struct TBBUTTON{public int iBitmap;public int idCommand;public IntPtr fsStateStylePadding;public IntPtr dwData;public IntPtr iString;}

View Code

修改过后在64位系统中运行通过了,在这样一位问题解决了,但是当我将解决方案迁移到程序当中的时候,却出了问题,一直不能得到地址,查找了很多原因,原来是在我程序编译的时候,生成的平台是X86,那么就造成了64位系统中使用32位平台时出现问题:

到这里我就猜想是不是public IntPtr fsStateStylePadding;这一句出了问题,当时x86平台的时候,这个只占用了32位,但是实际64位系统这个位置应该要占用64位,造成地址不对,出错了。

那么接下来我就证实了下这个问题,在我获取地址的时候在public IntPtr dwData;字段中没有获取到,但是在public IntPtr iString;字段中获取到了,那么证明我的猜想是对的(真正是否正确还需要指正),

那么解决方案就来了,为了更好的兼容性,结构体不变,当获取dwData地址没有获取到的时候,我们查找iString字段就好了,方法在这里:

//这一步至关重要,不能省略//主要解决64位系统电脑运行的是x86的程序if (btnData.dwData == IntPtr.Zero){btnData.dwData = btnData.iString;}

View Code

把这个主要的解决了,后面就是查找当前托盘图标相对于父窗体的位置了,使用了很多方法:

GetWindowRect

ScreenToClient

GetClientRect

这些方法都没有成功,最后发现网上有这么一种方法。

SendMessage(hTrayWnd, TB_GETITEMRECT, i, lngRect);isTrue = ReadProcessMemory(hProcess, lngRect, out rect, Marshal.SizeOf(rect), out iTmp);

View Code

在这里真正感受到c++的强大。

在解决这个问题的过程中,参考了很多方案,通过整合才解决了这个问题,如下:

/zjlovety@126/blog/static/2241862442763542917/

/wzsy/article/details/47980317

/hanf/archive//08/09/2131641.html

等。

以下是调用方法:

private void Load(){this._notifyIcon.MouseDoubleClick += notifyIcon_MouseDoubleClick;_notifyIcon.MouseMove += new MouseEventHandler(notifyIcon_MouseMove);CreateNotifyMouseHelper();}private NotifyIconMouseHelper notifyHelper;private Timer timer = null;private void CreateNotifyMouseHelper(){notifyHelper=new NotifyIconMouseHelper();notifyHelper.MouseEnterNotifyStatusChanged+= MouseEnterNotifyStatusChanged;}private void MouseEnterNotifyStatusChanged(object sender, bool isEnter){if (isEnter){Console.WriteLine("鼠标进入");}else{Console.WriteLine("鼠标离开");}}

View Code

以下是检测的源代码:

using System;using System.Collections.Generic;using System.Data.Entity.Core.Metadata.Edm;using System.Diagnostics;using System.Linq;using System.Runtime.InteropServices;using System.Text;using System.Timers;using System.Windows;namespace NotifyTest{/*托盘图标鼠标进入离开事件*/public delegate void MouseEnterNotifyStatusChangedHandel(object sender, bool isEnter);public class NotifyIconMouseHelper{#region win32类库 [Flags()]public enum ProcessAccess : int{/// <summary>Specifies all possible access flags for the process object.</summary>AllAccess =CreateThread | DuplicateHandle | QueryInformation | SetInformation | Terminate | VMOperation | VMRead |VMWrite | Synchronize,/// <summary>Enables usage of the process handle in the CreateRemoteThread function to create a thread in the process.</summary>CreateThread = 0x2,/// <summary>Enables usage of the process handle as either the source or target process in the DuplicateHandle function to duplicate a handle.</summary>DuplicateHandle = 0x40,/// <summary>Enables usage of the process handle in the GetExitCodeProcess and GetPriorityClass functions to read information from the process object.</summary>QueryInformation = 0x400,/// <summary>Enables usage of the process handle in the SetPriorityClass function to set the priority class of the process.</summary>SetInformation = 0x200,/// <summary>Enables usage of the process handle in the TerminateProcess function to terminate the process.</summary>Terminate = 0x1,/// <summary>Enables usage of the process handle in the VirtualProtectEx and WriteProcessMemory functions to modify the virtual memory of the process.</summary>VMOperation = 0x8,/// <summary>Enables usage of the process handle in the ReadProcessMemory function to' read from the virtual memory of the process.</summary>VMRead = 0x10,/// <summary>Enables usage of the process handle in the WriteProcessMemory function to write to the virtual memory of the process.</summary>VMWrite = 0x20,/// <summary>Enables usage of the process handle in any of the wait functions to wait for the process to terminate.</summary>Synchronize = 0x100000}[StructLayout(LayoutKind.Sequential)]private struct TRAYDATA{public IntPtr hwnd;public UInt32 uID;public UInt32 uCallbackMessage;public UInt32 bReserved0;public UInt32 bReserved1;public IntPtr hIcon;}[StructLayout(LayoutKind.Sequential, Pack = 1)]public struct TBBUTTON{public int iBitmap;public int idCommand;public IntPtr fsStateStylePadding;public IntPtr dwData;public IntPtr iString;}[Flags]public enum AllocationType{Commit = 0x1000,Reserve = 0x2000,Decommit = 0x4000,Release = 0x8000,Reset = 0x80000,Physical = 0x400000,TopDown = 0x100000,WriteWatch = 0x200000,LargePages = 0x20000000}[Flags]public enum MemoryProtection{Execute = 0x10,ExecuteRead = 0x20,ExecuteReadWrite = 0x40,ExecuteWriteCopy = 0x80,NoAccess = 0x01,ReadOnly = 0x02,ReadWrite = 0x04,WriteCopy = 0x08,GuardModifierflag = 0x100,NoCacheModifierflag = 0x200,WriteCombineModifierflag = 0x400}[DllImport("user32.dll", SetLastError = true)]private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);[DllImport("user32.dll", SetLastError = true)]private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass,string lpszWindow);[DllImport("user32.dll", SetLastError = true)]private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);[DllImport("kernel32.dll")]private static extern IntPtr OpenProcess(ProcessAccess dwDesiredAccess,[MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId);[DllImport("user32.dll", CharSet = CharSet.Auto)]private static extern UInt32 SendMessage(IntPtr hWnd, UInt32 Msg, UInt32 wParam, IntPtr lParam);[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]private static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,int dwSize, AllocationType flAllocationType, MemoryProtection flProtect);[DllImport("kernel32.dll", SetLastError = true)]private static extern bool ReadProcessMemory(IntPtr hProcess,IntPtr lpBaseAddress,out TBBUTTON lpBuffer,int dwSize,out int lpNumberOfBytesRead);[DllImport("kernel32.dll", SetLastError = true)]private static extern bool ReadProcessMemory(IntPtr hProcess,IntPtr lpBaseAddress,out Rect lpBuffer,int dwSize,out int lpNumberOfBytesRead);[DllImport("kernel32.dll", SetLastError = true)]private static extern bool ReadProcessMemory(IntPtr hProcess,IntPtr lpBaseAddress,out TRAYDATA lpBuffer,int dwSize,out int lpNumberOfBytesRead);[DllImport("psapi.dll")]private static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName,[In] [MarshalAs(UnmanagedType.U4)] int nSize);[Flags]public enum FreeType{Decommit = 0x4000,Release = 0x8000,}[DllImport("kernel32.dll")]private static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, FreeType dwFreeType);[DllImport("kernel32.dll")][return: MarshalAs(UnmanagedType.Bool)]private static extern bool CloseHandle(IntPtr hObject);[StructLayout(LayoutKind.Sequential)]public struct POINT{public int X;public int Y;public POINT(int x, int y){this.X = x;this.Y = y;}public override string ToString(){return ("X:" + X + ", Y:" + Y);}}[DllImport("user32")]public static extern bool GetClientRect(IntPtr hwnd,out Rect lpRect);[DllImport("user32.dll", CharSet = CharSet.Auto)]public static extern bool GetCursorPos(out POINT pt);[StructLayout(LayoutKind.Sequential)]public struct Rect{public int Left;public int Top;public int Right;public int Bottom;}[DllImport("user32.dll")]private static extern int GetWindowRect(IntPtr hwnd, out Rect lpRect);public const int WM_USER = 0x0400;public const int TB_BUTTONCOUNT = WM_USER + 24;public const int TB_GETBUTTON = WM_USER + 23;public const int TB_GETBUTTONINFOW = WM_USER + 63;public const int TB_GETITEMRECT = WM_USER + 29;#endregion#region 检测托盘图标相对于屏幕位置private bool FindNotifyIcon(ref Rect rect){Rect rectNotify = new Rect();IntPtr hTrayWnd = FindTrayToolbarWindow(); //找到托盘窗口句柄var isTrue = FindNotifyIcon(hTrayWnd, ref rectNotify);if (isTrue == false){hTrayWnd = FindTrayToolbarOverFlowWindow(); //找到托盘窗口句柄isTrue = FindNotifyIcon(hTrayWnd, ref rectNotify);}rect = rectNotify;return isTrue;}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;}private IntPtr FindTrayToolbarOverFlowWindow(){IntPtr hWnd = FindWindow("NotifyIconOverflowWindow", null);if (hWnd != IntPtr.Zero){hWnd = FindWindowEx(hWnd, IntPtr.Zero, "ToolbarWindow32", null);}return hWnd;}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,mit,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处存放的是TBBUTTONbtnData.dwData, //取dwData字段指向的TRAYDATA结构out trayData,Marshal.SizeOf(trayData),out iTmp);UInt32 dwProcessId = 0;GetWindowThreadProcessId(trayData.hwnd, //通过TRAYDATA里的hwnd字段取得本图标的进程idout 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)),mit,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;}#endregionpublic MouseEnterNotifyStatusChangedHandel MouseEnterNotifyStatusChanged;private object moveObject = new object();private bool isOver = false;private Timer timer = null;public void MouseEnter(){lock (moveObject){if (isOver) return;//加载鼠标进入事件MouseEnter(true);CreateCheckTimer();timer.Enabled = true;}}private void CreateCheckTimer(){if (timer != null) return;timer = new Timer();timer.Interval = 120;timer.Elapsed += TimerOnElapsed;}private void TimerOnElapsed(object sender, ElapsedEventArgs arg){//300毫秒检测一次//判断鼠标是否在托盘图标内//如果在,那么就不管//如果不在,就加载鼠标离开事件,同时停止timervar isEnter = CheckMouseIsEnter();if (isEnter) return;timer.Enabled = false;MouseEnter(false);}private void MouseEnter(bool isEnter){isOver = isEnter;if (MouseEnterNotifyStatusChanged == null) return;MouseEnterNotifyStatusChanged(this, isEnter);}public Point Point { get; set; }private bool CheckMouseIsEnter(){//这里怎么检测呢//我很无语啊 //第一步:获取当前鼠标的坐标//第二步:获取托盘图标的坐标// ???? 难难难难难难难难难难try{Rect rectNotify = new Rect();var isTrue = FindNotifyIcon(ref rectNotify);if (isTrue == false) return false;POINT point = new POINT();GetCursorPos(out point);// Console.WriteLine(string.Format(@"//Left={0} Top={1} Right={2} Bottom={3}", rectNotify.Left, rectNotify.Top, rectNotify.Right, rectNotify.Bottom));// Console.WriteLine(point.X + " " + point.Y);//第三步:比较鼠标图标是否在托盘图标的范围内if (point.X >= rectNotify.Left && point.X <= rectNotify.Right &&point.Y >= rectNotify.Top && point.Y <= rectNotify.Bottom){Point = new Point(point.X, point.Y);return true;}else{return false;}}catch (Exception){return false;}}}}

View Code

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。