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

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

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

2.进入后启动检测timer

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

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

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

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

  1. private IntPtr FindTrayToolbarWindow()
  2. {
  3. IntPtr hWnd = FindWindow("Shell_TrayWnd", null);
  4. if (hWnd != IntPtr.Zero)
  5. {
  6. hWnd = FindWindowEx(hWnd, IntPtr.Zero, "TrayNotifyWnd", null);
  7. if (hWnd != IntPtr.Zero)
  8. {
  9.  
  10. hWnd = FindWindowEx(hWnd, IntPtr.Zero, "SysPager", null);
  11. if (hWnd != IntPtr.Zero)
  12. {
  13. hWnd = FindWindowEx(hWnd, IntPtr.Zero, "ToolbarWindow32", null);
  14.  
  15. }
  16. }
  17. }
  18. return hWnd;
  19. }

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

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

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

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

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

2-6代码:

  1. private bool FindNotifyIcon(IntPtr hTrayWnd, ref Rect rectNotify)
  2. {
  3. UInt32 trayPid = ;
  4. Rect rectTray = new Rect();
  5. GetWindowRect(hTrayWnd, out rectTray);
  6. int count = (int) SendMessage(hTrayWnd, TB_BUTTONCOUNT, , IntPtr.Zero); //给托盘窗口发消息,得到托盘里图标
  7.  
  8. bool isFind = false;
  9. if (count > )
  10. {
  11. GetWindowThreadProcessId(hTrayWnd, out trayPid); //取得托盘窗口对应的进程id
  12. //获取托盘图标的位置
  13.  
  14. IntPtr hProcess = OpenProcess(ProcessAccess.VMOperation | ProcessAccess.VMRead | ProcessAccess.VMWrite,
  15. false, trayPid); //打开进程,取得进程句柄
  16.  
  17. IntPtr address = VirtualAllocEx(hProcess, //在目标进程中申请一块内存,放TBBUTTON信息
  18. IntPtr.Zero,
  19. ,
  20. AllocationType.Commit,
  21. MemoryProtection.ReadWrite);
  22.  
  23. TBBUTTON btnData = new TBBUTTON();
  24. TRAYDATA trayData = new TRAYDATA();
  25.  
  26. // Console.WriteLine("Count:"+count);
  27.  
  28. var handel = Process.GetCurrentProcess().Id;
  29. // Console.WriteLine("curHandel:" + handel);
  30. for (uint j = ; j < count; j++)
  31. {
  32. // Console.WriteLine("j:"+j);
  33. var i = j;
  34. SendMessage(hTrayWnd, TB_GETBUTTON, i, address); //取得TBBUTTON结构到本地
  35. int iTmp = ;
  36. var isTrue = ReadProcessMemory(hProcess,
  37. address,
  38. out btnData,
  39. Marshal.SizeOf(btnData),
  40. out iTmp);
  41. if (isTrue == false) continue;
  42. //这一步至关重要,不能省略
  43. //主要解决64位系统电脑运行的是x86的程序
  44. if (btnData.dwData == IntPtr.Zero)
  45. {
  46. btnData.dwData = btnData.iString;
  47. }
  48. ReadProcessMemory(hProcess, //从目标进程address处存放的是TBBUTTON
  49. btnData.dwData, //取dwData字段指向的TRAYDATA结构
  50. out trayData,
  51. Marshal.SizeOf(trayData),
  52. out iTmp);
  53.  
  54. UInt32 dwProcessId = ;
  55. GetWindowThreadProcessId(trayData.hwnd, //通过TRAYDATA里的hwnd字段取得本图标的进程id
  56. out dwProcessId);
  57. //获取当前进程id
  58. // StringBuilder sb = new StringBuilder(256);
  59. // GetModuleFileNameEx(OpenProcess(ProcessAccess.AllAccess, false, dwProcessId), IntPtr.Zero, sb, 256);
  60. // Console.WriteLine(sb.ToString());
  61. if (dwProcessId == (UInt32) handel)
  62. {
  63.  
  64. Rect rect = new Rect();
  65. IntPtr lngRect = VirtualAllocEx(hProcess, //在目标进程中申请一块内存,放TBBUTTON信息
  66. IntPtr.Zero,
  67. Marshal.SizeOf(typeof (Rect)),
  68. AllocationType.Commit,
  69. MemoryProtection.ReadWrite);
  70. i = j;
  71. SendMessage(hTrayWnd, TB_GETITEMRECT, i, lngRect);
  72. isTrue = ReadProcessMemory(hProcess, lngRect, out rect, Marshal.SizeOf(rect), out iTmp);
  73.  
  74. //释放内存
  75. VirtualFreeEx(hProcess, lngRect, Marshal.SizeOf(rect), FreeType.Decommit);
  76. VirtualFreeEx(hProcess, lngRect, , FreeType.Release);
  77.  
  78. int left = rectTray.Left + rect.Left;
  79. int top = rectTray.Top + rect.Top;
  80. int botton = rectTray.Top + rect.Bottom;
  81. int right = rectTray.Left + rect.Right;
  82. rectNotify = new Rect();
  83. rectNotify.Left = left;
  84. rectNotify.Right = right;
  85. rectNotify.Top = top;
  86. rectNotify.Bottom = botton;
  87. isFind = true;
  88. break;
  89. }
  90. }
  91. VirtualFreeEx(hProcess, address, 0x4096, FreeType.Decommit);
  92. VirtualFreeEx(hProcess, address, , FreeType.Release);
  93. CloseHandle(hProcess);
  94. }
  95. return isFind;
  96. }

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

  1. private IntPtr FindTrayToolbarOverFlowWindow()
  2. {
  3. IntPtr hWnd = FindWindow("NotifyIconOverflowWindow", null);
  4. if (hWnd != IntPtr.Zero)
  5. {
  6. hWnd = FindWindowEx(hWnd, IntPtr.Zero, "ToolbarWindow32", null);
  7. }
  8. return hWnd;
  9. }

在查找中的难点:

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

  1. [StructLayout(LayoutKind.Sequential, Pack = )]
  2. public struct TBBUTTON
  3. {
  4. public int iBitmap;
  5. public int idCommand;
  6. public byte fsState;
  7. public byte fsStyle;
  8. public byte bReserved0;
  9. public byte bReserved1;
  10. public IntPtr dwData;
  11. public IntPtr iString;
  12. }

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

  1. [StructLayout(LayoutKind.Sequential, Pack = )]
  2. public struct TBBUTTON
  3. {
  4. public int iBitmap;
  5. public int idCommand;
  6. public IntPtr fsStateStylePadding;
  7. public IntPtr dwData;
  8. public IntPtr iString;
  9. }

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

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

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

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

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

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

GetWindowRect

ScreenToClient

GetClientRect

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

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

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

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

http://blog.163.com/zjlovety@126/blog/static/22418624201142763542917/

http://blog.csdn.net/wzsy/article/details/47980317

http://www.cnblogs.com/hanf/archive/2011/08/09/2131641.html

等。

以下是调用方法:

  1. private void Load()
  2. {
  3. this._notifyIcon.MouseDoubleClick += notifyIcon_MouseDoubleClick;
  4. _notifyIcon.MouseMove += new MouseEventHandler(notifyIcon_MouseMove);
  5. CreateNotifyMouseHelper();
  6. }
  7.  
  8. private NotifyIconMouseHelper notifyHelper;
  9. private Timer timer = null;
  10. private void CreateNotifyMouseHelper()
  11. {
  12. notifyHelper=new NotifyIconMouseHelper();
  13. notifyHelper.MouseEnterNotifyStatusChanged+= MouseEnterNotifyStatusChanged;
  14. }
  15. private void MouseEnterNotifyStatusChanged(object sender, bool isEnter)
  16. {
  17.  
  18. if (isEnter)
  19. {
  20. Console.WriteLine("鼠标进入");
  21. }
  22. else
  23. {
  24. Console.WriteLine("鼠标离开");
  25. }
  26. }

以下是检测的源代码:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data.Entity.Core.Metadata.Edm;
  4. using System.Diagnostics;
  5. using System.Linq;
  6. using System.Runtime.InteropServices;
  7. using System.Text;
  8. using System.Timers;
  9. using System.Windows;
  10.  
  11. namespace NotifyTest
  12. {
  13. /*托盘图标鼠标进入离开事件
  14. */
  15.  
  16. public delegate void MouseEnterNotifyStatusChangedHandel(object sender, bool isEnter);
  17.  
  18. public class NotifyIconMouseHelper
  19. {
  20. #region win32类库
  21.  
  22. [Flags()]
  23. public enum ProcessAccess : int
  24. {
  25. /// <summary>Specifies all possible access flags for the process object.</summary>
  26. AllAccess =
  27. CreateThread | DuplicateHandle | QueryInformation | SetInformation | Terminate | VMOperation | VMRead |
  28. VMWrite | Synchronize,
  29.  
  30. /// <summary>Enables usage of the process handle in the CreateRemoteThread function to create a thread in the process.</summary>
  31. CreateThread = 0x2,
  32.  
  33. /// <summary>Enables usage of the process handle as either the source or target process in the DuplicateHandle function to duplicate a handle.</summary>
  34. DuplicateHandle = 0x40,
  35.  
  36. /// <summary>Enables usage of the process handle in the GetExitCodeProcess and GetPriorityClass functions to read information from the process object.</summary>
  37. QueryInformation = 0x400,
  38.  
  39. /// <summary>Enables usage of the process handle in the SetPriorityClass function to set the priority class of the process.</summary>
  40. SetInformation = 0x200,
  41.  
  42. /// <summary>Enables usage of the process handle in the TerminateProcess function to terminate the process.</summary>
  43. Terminate = 0x1,
  44.  
  45. /// <summary>Enables usage of the process handle in the VirtualProtectEx and WriteProcessMemory functions to modify the virtual memory of the process.</summary>
  46. VMOperation = 0x8,
  47.  
  48. /// <summary>Enables usage of the process handle in the ReadProcessMemory function to' read from the virtual memory of the process.</summary>
  49. VMRead = 0x10,
  50.  
  51. /// <summary>Enables usage of the process handle in the WriteProcessMemory function to write to the virtual memory of the process.</summary>
  52. VMWrite = 0x20,
  53.  
  54. /// <summary>Enables usage of the process handle in any of the wait functions to wait for the process to terminate.</summary>
  55. Synchronize = 0x100000
  56. }
  57.  
  58. [StructLayout(LayoutKind.Sequential)]
  59. private struct TRAYDATA
  60. {
  61. public IntPtr hwnd;
  62. public UInt32 uID;
  63. public UInt32 uCallbackMessage;
  64. public UInt32 bReserved0;
  65. public UInt32 bReserved1;
  66. public IntPtr hIcon;
  67. }
  68.  
  69. [StructLayout(LayoutKind.Sequential, Pack = )]
  70. public struct TBBUTTON
  71. {
  72. public int iBitmap;
  73. public int idCommand;
  74. public IntPtr fsStateStylePadding;
  75. public IntPtr dwData;
  76. public IntPtr iString;
  77. }
  78.  
  79. [Flags]
  80. public enum AllocationType
  81. {
  82. Commit = 0x1000,
  83. Reserve = 0x2000,
  84. Decommit = 0x4000,
  85. Release = 0x8000,
  86. Reset = 0x80000,
  87. Physical = 0x400000,
  88. TopDown = 0x100000,
  89. WriteWatch = 0x200000,
  90. LargePages = 0x20000000
  91. }
  92.  
  93. [Flags]
  94. public enum MemoryProtection
  95. {
  96. Execute = 0x10,
  97. ExecuteRead = 0x20,
  98. ExecuteReadWrite = 0x40,
  99. ExecuteWriteCopy = 0x80,
  100. NoAccess = 0x01,
  101. ReadOnly = 0x02,
  102. ReadWrite = 0x04,
  103. WriteCopy = 0x08,
  104. GuardModifierflag = 0x100,
  105. NoCacheModifierflag = 0x200,
  106. WriteCombineModifierflag = 0x400
  107. }
  108.  
  109. [DllImport("user32.dll", SetLastError = true)]
  110. private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
  111.  
  112. [DllImport("user32.dll", SetLastError = true)]
  113. private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass,
  114. string lpszWindow);
  115.  
  116. [DllImport("user32.dll", SetLastError = true)]
  117. private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
  118.  
  119. [DllImport("kernel32.dll")]
  120. private static extern IntPtr OpenProcess(ProcessAccess dwDesiredAccess,
  121. [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId);
  122.  
  123. [DllImport("user32.dll", CharSet = CharSet.Auto)]
  124. private static extern UInt32 SendMessage(IntPtr hWnd, UInt32 Msg, UInt32 wParam, IntPtr lParam);
  125.  
  126. [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
  127. private static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,
  128. int dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
  129.  
  130. [DllImport("kernel32.dll", SetLastError = true)]
  131. private static extern bool ReadProcessMemory(
  132. IntPtr hProcess,
  133. IntPtr lpBaseAddress,
  134. out TBBUTTON lpBuffer,
  135. int dwSize,
  136. out int lpNumberOfBytesRead
  137. );
  138.  
  139. [DllImport("kernel32.dll", SetLastError = true)]
  140. private static extern bool ReadProcessMemory(
  141. IntPtr hProcess,
  142. IntPtr lpBaseAddress,
  143. out Rect lpBuffer,
  144. int dwSize,
  145. out int lpNumberOfBytesRead
  146. );
  147.  
  148. [DllImport("kernel32.dll", SetLastError = true)]
  149. private static extern bool ReadProcessMemory(
  150. IntPtr hProcess,
  151. IntPtr lpBaseAddress,
  152. out TRAYDATA lpBuffer,
  153. int dwSize,
  154. out int lpNumberOfBytesRead
  155. );
  156.  
  157. [DllImport("psapi.dll")]
  158. private static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName,
  159. [In] [MarshalAs(UnmanagedType.U4)] int nSize);
  160.  
  161. [Flags]
  162. public enum FreeType
  163. {
  164. Decommit = 0x4000,
  165. Release = 0x8000,
  166. }
  167.  
  168. [DllImport("kernel32.dll")]
  169. private static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, FreeType dwFreeType);
  170.  
  171. [DllImport("kernel32.dll")]
  172. [return: MarshalAs(UnmanagedType.Bool)]
  173. private static extern bool CloseHandle(IntPtr hObject);
  174.  
  175. [StructLayout(LayoutKind.Sequential)]
  176. public struct POINT
  177. {
  178. public int X;
  179. public int Y;
  180.  
  181. public POINT(int x, int y)
  182. {
  183. this.X = x;
  184. this.Y = y;
  185. }
  186.  
  187. public override string ToString()
  188. {
  189. return ("X:" + X + ", Y:" + Y);
  190. }
  191. }
  192.  
  193. [DllImport("user32")]
  194. public static extern bool GetClientRect(
  195. IntPtr hwnd,
  196. out Rect lpRect
  197. );
  198.  
  199. [DllImport("user32.dll", CharSet = CharSet.Auto)]
  200. public static extern bool GetCursorPos(out POINT pt);
  201.  
  202. [StructLayout(LayoutKind.Sequential)]
  203. public struct Rect
  204. {
  205. public int Left;
  206. public int Top;
  207. public int Right;
  208. public int Bottom;
  209. }
  210.  
  211. [DllImport("user32.dll")]
  212. private static extern int GetWindowRect(IntPtr hwnd, out Rect lpRect);
  213.  
  214. public const int WM_USER = 0x0400;
  215. public const int TB_BUTTONCOUNT = WM_USER + ;
  216. public const int TB_GETBUTTON = WM_USER + ;
  217. public const int TB_GETBUTTONINFOW = WM_USER + ;
  218. public const int TB_GETITEMRECT = WM_USER + ;
  219.  
  220. #endregion
  221.  
  222. #region 检测托盘图标相对于屏幕位置
  223.  
  224. private bool FindNotifyIcon(ref Rect rect)
  225. {
  226. Rect rectNotify = new Rect();
  227. IntPtr hTrayWnd = FindTrayToolbarWindow(); //找到托盘窗口句柄
  228. var isTrue = FindNotifyIcon(hTrayWnd, ref rectNotify);
  229. if (isTrue == false)
  230. {
  231. hTrayWnd = FindTrayToolbarOverFlowWindow(); //找到托盘窗口句柄
  232. isTrue = FindNotifyIcon(hTrayWnd, ref rectNotify);
  233. }
  234. rect = rectNotify;
  235. return isTrue;
  236. }
  237.  
  238. private IntPtr FindTrayToolbarWindow()
  239. {
  240. IntPtr hWnd = FindWindow("Shell_TrayWnd", null);
  241. if (hWnd != IntPtr.Zero)
  242. {
  243. hWnd = FindWindowEx(hWnd, IntPtr.Zero, "TrayNotifyWnd", null);
  244. if (hWnd != IntPtr.Zero)
  245. {
  246.  
  247. hWnd = FindWindowEx(hWnd, IntPtr.Zero, "SysPager", null);
  248. if (hWnd != IntPtr.Zero)
  249. {
  250. hWnd = FindWindowEx(hWnd, IntPtr.Zero, "ToolbarWindow32", null);
  251.  
  252. }
  253. }
  254. }
  255. return hWnd;
  256. }
  257.  
  258. private IntPtr FindTrayToolbarOverFlowWindow()
  259. {
  260. IntPtr hWnd = FindWindow("NotifyIconOverflowWindow", null);
  261. if (hWnd != IntPtr.Zero)
  262. {
  263. hWnd = FindWindowEx(hWnd, IntPtr.Zero, "ToolbarWindow32", null);
  264. }
  265. return hWnd;
  266. }
  267.  
  268. private bool FindNotifyIcon(IntPtr hTrayWnd, ref Rect rectNotify)
  269. {
  270. UInt32 trayPid = ;
  271. Rect rectTray = new Rect();
  272. GetWindowRect(hTrayWnd, out rectTray);
  273. int count = (int) SendMessage(hTrayWnd, TB_BUTTONCOUNT, , IntPtr.Zero); //给托盘窗口发消息,得到托盘里图标
  274.  
  275. bool isFind = false;
  276. if (count > )
  277. {
  278. GetWindowThreadProcessId(hTrayWnd, out trayPid); //取得托盘窗口对应的进程id
  279. //获取托盘图标的位置
  280.  
  281. IntPtr hProcess = OpenProcess(ProcessAccess.VMOperation | ProcessAccess.VMRead | ProcessAccess.VMWrite,
  282. false, trayPid); //打开进程,取得进程句柄
  283.  
  284. IntPtr address = VirtualAllocEx(hProcess, //在目标进程中申请一块内存,放TBBUTTON信息
  285. IntPtr.Zero,
  286. ,
  287. AllocationType.Commit,
  288. MemoryProtection.ReadWrite);
  289.  
  290. TBBUTTON btnData = new TBBUTTON();
  291. TRAYDATA trayData = new TRAYDATA();
  292.  
  293. // Console.WriteLine("Count:"+count);
  294.  
  295. var handel = Process.GetCurrentProcess().Id;
  296. // Console.WriteLine("curHandel:" + handel);
  297. for (uint j = ; j < count; j++)
  298. {
  299. // Console.WriteLine("j:"+j);
  300. var i = j;
  301. SendMessage(hTrayWnd, TB_GETBUTTON, i, address); //取得TBBUTTON结构到本地
  302. int iTmp = ;
  303. var isTrue = ReadProcessMemory(hProcess,
  304. address,
  305. out btnData,
  306. Marshal.SizeOf(btnData),
  307. out iTmp);
  308. if (isTrue == false) continue;
  309. //这一步至关重要,不能省略
  310. //主要解决64位系统电脑运行的是x86的程序
  311. if (btnData.dwData == IntPtr.Zero)
  312. {
  313. btnData.dwData = btnData.iString;
  314. }
  315. ReadProcessMemory(hProcess, //从目标进程address处存放的是TBBUTTON
  316. btnData.dwData, //取dwData字段指向的TRAYDATA结构
  317. out trayData,
  318. Marshal.SizeOf(trayData),
  319. out iTmp);
  320.  
  321. UInt32 dwProcessId = ;
  322. GetWindowThreadProcessId(trayData.hwnd, //通过TRAYDATA里的hwnd字段取得本图标的进程id
  323. out dwProcessId);
  324. //获取当前进程id
  325. // StringBuilder sb = new StringBuilder(256);
  326. // GetModuleFileNameEx(OpenProcess(ProcessAccess.AllAccess, false, dwProcessId), IntPtr.Zero, sb, 256);
  327. // Console.WriteLine(sb.ToString());
  328. if (dwProcessId == (UInt32) handel)
  329. {
  330.  
  331. Rect rect = new Rect();
  332. IntPtr lngRect = VirtualAllocEx(hProcess, //在目标进程中申请一块内存,放TBBUTTON信息
  333. IntPtr.Zero,
  334. Marshal.SizeOf(typeof (Rect)),
  335. AllocationType.Commit,
  336. MemoryProtection.ReadWrite);
  337. i = j;
  338. SendMessage(hTrayWnd, TB_GETITEMRECT, i, lngRect);
  339. isTrue = ReadProcessMemory(hProcess, lngRect, out rect, Marshal.SizeOf(rect), out iTmp);
  340.  
  341. //释放内存
  342. VirtualFreeEx(hProcess, lngRect, Marshal.SizeOf(rect), FreeType.Decommit);
  343. VirtualFreeEx(hProcess, lngRect, , FreeType.Release);
  344.  
  345. int left = rectTray.Left + rect.Left;
  346. int top = rectTray.Top + rect.Top;
  347. int botton = rectTray.Top + rect.Bottom;
  348. int right = rectTray.Left + rect.Right;
  349. rectNotify = new Rect();
  350. rectNotify.Left = left;
  351. rectNotify.Right = right;
  352. rectNotify.Top = top;
  353. rectNotify.Bottom = botton;
  354. isFind = true;
  355. break;
  356. }
  357. }
  358. VirtualFreeEx(hProcess, address, 0x4096, FreeType.Decommit);
  359. VirtualFreeEx(hProcess, address, , FreeType.Release);
  360. CloseHandle(hProcess);
  361. }
  362. return isFind;
  363. }
  364.  
  365. #endregion
  366.  
  367. public MouseEnterNotifyStatusChangedHandel MouseEnterNotifyStatusChanged;
  368. private object moveObject = new object();
  369. private bool isOver = false;
  370. private Timer timer = null;
  371.  
  372. public void MouseEnter()
  373. {
  374. lock (moveObject)
  375. {
  376. if (isOver) return;
  377.  
  378. //加载鼠标进入事件
  379. MouseEnter(true);
  380. CreateCheckTimer();
  381. timer.Enabled = true;
  382. }
  383. }
  384.  
  385. private void CreateCheckTimer()
  386. {
  387. if (timer != null) return;
  388. timer = new Timer();
  389. timer.Interval = ;
  390. timer.Elapsed += TimerOnElapsed;
  391. }
  392.  
  393. private void TimerOnElapsed(object sender, ElapsedEventArgs arg)
  394. {
  395. //300毫秒检测一次
  396. //判断鼠标是否在托盘图标内
  397. //如果在,那么就不管
  398. //如果不在,就加载鼠标离开事件,同时停止timer
  399. var isEnter = CheckMouseIsEnter();
  400. if (isEnter) return;
  401. timer.Enabled = false;
  402. MouseEnter(false);
  403. }
  404.  
  405. private void MouseEnter(bool isEnter)
  406. {
  407. isOver = isEnter;
  408. if (MouseEnterNotifyStatusChanged == null) return;
  409. MouseEnterNotifyStatusChanged(this, isEnter);
  410. }
  411.  
  412. public Point Point { get; set; }
  413.  
  414. private bool CheckMouseIsEnter()
  415. {
  416. //这里怎么检测呢
  417. //我很无语啊
  418. //第一步:获取当前鼠标的坐标
  419. //第二步:获取托盘图标的坐标
  420. // ???? 难难难难难难难难难难
  421. try
  422. {
  423.  
  424. Rect rectNotify = new Rect();
  425. var isTrue = FindNotifyIcon(ref rectNotify);
  426. if (isTrue == false) return false;
  427. POINT point = new POINT();
  428. GetCursorPos(out point);
  429. // Console.WriteLine(string.Format(@"
  430. //Left={0} Top={1} Right={2} Bottom={3}", rectNotify.Left, rectNotify.Top, rectNotify.Right, rectNotify.Bottom));
  431. // Console.WriteLine(point.X + " " + point.Y);
  432. //第三步:比较鼠标图标是否在托盘图标的范围内
  433. if (point.X >= rectNotify.Left && point.X <= rectNotify.Right &&
  434. point.Y >= rectNotify.Top && point.Y <= rectNotify.Bottom)
  435. {
  436. Point = new Point(point.X, point.Y);
  437. return true;
  438. }
  439. else
  440. {
  441. return false;
  442. }
  443. }
  444. catch (Exception)
  445. {
  446. return false;
  447. }
  448. }
  449. }
  450. }

c# 任务栏托盘图标鼠标进入MouseEnter和鼠标离开MouseLeave实现的更多相关文章

  1. mouseover与mouseenter,mouseout与mouseleave的区别

    mouseover事件:不论鼠标指针穿过被选元素或其子元素,都会触发 mouseover 事件,对应mouseout事件: mouseenter事件:只有在鼠标指针穿过被选元素时,才会触发 mouse ...

  2. Adobe Edge Animate –获取鼠标位置及跟随鼠标功能实现

    Adobe Edge Animate –获取鼠标位置及跟随鼠标功能实现 版权声明: 本文版权属于 北京联友天下科技发展有限公司. 转载的时候请注明版权和原文地址. 在网络上浏览有关Edge相关问题的时 ...

  3. win7旗舰版任务栏窗口不合并显示,鼠标移至窗口时可预览应用内容

    1.鼠标移至任务栏--右键--属性: 2.选择"当任务栏被占满时合并"或"从不合并",第一个选项更优: 3.右键桌面"计算机"的" ...

  4. mouseover和mouseenter,mouseout和mouseleave的区别-引发的探索

    相信小伙伴们都用过鼠标事件,比如mouseover和mouseout,mouseenter和mouseleave.它们都分别表示鼠标移入移出. 在使用的过程中,其实一直有个小疑问——它们之间究竟有什么 ...

  5. mouseover,mouseenter,mouseout,mouseleave的区别

    mouseover :不论鼠标指针穿过被选元素或其子元素,都会触发 mouseover 事件. mouseout :不论鼠标指针离开被选元素还是任何子元素,都会触发 mouseout 事件. mous ...

  6. html body标签的几个属性 禁用鼠标右键,禁用鼠标选中文字等

    <body oncontextmenu='return false' ondragstart='return false' onselectstart ='return false' onsel ...

  7. OpenCV鼠标画图例程,鼠标绘制矩形

    鼠标画矩形: // An example program in which the // user can draw boxes on the screen. // /* License: Oct. ...

  8. 【.Net】鼠标点击控制鼠标活动范围 ClipCursor

    可以使用API ClipCursor,如果你不嫌麻烦的话. 以下方法: Private Sub Form1_MouseDown(sender As System.Object, e As System ...

  9. 2017年10月21日 CSS常用样式&鼠标样式 以及 jQuery鼠标事件& jQuery图片轮播& jQuery图片自动轮播代码

    css代码 背景与前景 background-color:#0000; //背景色,样式表优先级高 background-image:url(路径); //设置背景图片 background-atta ...

随机推荐

  1. sqlite与sqlserver区别

    1.查询时把两个字段拼接在一起 --sqlserver-- select Filed1+'@'+Filed2 from table --sqlite-- select Filed1||'@'||Fil ...

  2. js为Object对象动态添加属性和值 eval c.k c[k]

    const appendInfo = () => { const API_SECRET_KEY = 'https://github.com/dyq086/wepy-mall/tree/maste ...

  3. pgsql 数据类型

  4. 洛谷3243 [HNOI2015]菜肴制作

    题目戳这里 Solution 错误的想法:正向建图,然后从入度为0的点选出最小u的开始输出,然后找出u连接的点v,并把v的度数减一,再次把入度为0的点加入小根堆,这样显然有错,因为只能局部保证最小,后 ...

  5. Django项目之【学员管理系统】

    Django项目[学员管理系统] 项目规划阶段 项目背景 现实生活中,特别是在学校,传统的excel统计管理学员信息的方式已经无法满足日渐增长的业务需求. 因此需一套方便易用的“学员管理系统”,来提高 ...

  6. Android数据格式化

    1.文件大小格式化: Log.d(TAG, Formatter.formatFileSize(this, 100)); //100 B Log.d(TAG, Formatter.formatFileS ...

  7. datetime-local设置初始值

    //全局变量 var format = ""; //构造符合datetime-local格式的当前日期 function getFormat(){ format = "& ...

  8. Maximum Subsequence Sum 【DP】

    Given a sequence of K integers { N​1​​, N​2​​, -, N​K​​ }. A continuous subsequence is defined to be ...

  9. Django 后台管理 之登录和注销

    Session:     session是服务器端生成保存的一个键值对 , session内部机制依赖于cookie . 用户登录后返回给客户端一个随机字符串,客户端带着随机字符串访问服务器,用于验证 ...

  10. blog真正的首页

    声明:此Django分类下的教程是追梦人物所有,地址http://www.jianshu.com/u/f0c09f959299,本人写在此只是为了巩固复习使用 上一节我们阐明了django的开发流程, ...