寻找指针





5.00/5 (7投票s)
按下任意 CTRL 键,找出鼠标指针的位置...
引言
是否经常在打开的众多应用程序中丢失鼠标指针?我知道 Microsoft 允许你在鼠标属性中显示鼠标指针的位置。只需选中“当我按下 CTRL 键时显示指针的位置”复选框即可。
这对我兄弟来说效果并不理想(他有一个 4K 显示器),所以我决定为他做点什么。
这比我想象的更具挑战性。
背景
我的目标是创建一个 Windows 托盘应用程序,它可以执行以下操作:
- 创建托盘应用程序
- 创建一个系统范围的键盘/鼠标钩子,以便我可以捕获 CTRL 键
- 在鼠标指针所在的位置显示爆炸的炸弹。
这就是我想要做的全部。没什么大不了的。哦,该应用程序名为 Rodent。
以下是主要方法
public RodentTray() // This is where I subscribe to the Key/Mouse events and call
InitializeComponent(); // Which just sets up my menu objects for the tray icon.
QuitMenuItem_Click(object sender, EventArgs e) // This unsubscribes the Key/Mouse hook and quits the application.
CreateTheBombForm() // This is where I create the form thats used to display my images
ShowBomb(Form bombForm) // This displays the form and shows the images. It disposes of everything after the last frame is displayed.
你必须将以下内容添加到你的 using 语句中,并确保将其添加到解决方案资源管理器中的引用中
using Gma.System.MouseKeyHook;
Using the Code
经过多次错误的尝试后,我得到了这个。一个确保应用程序只有一个实例正在运行的类。我想我是在 Stack Overflow 上找到的这个。确保 Rodent 只能运行一个实例。
namespace Rodent { static class Program { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { // Make sure only one instance of Rodent is able to run SingleInstance.SingleInst(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Rodent.Classes.RodentTray()); } } }
SingleInstance.SingleInst();
这是应用程序调用的第一个类。
public class SingleInstance { // Detect a second copy of the application that is already running. // If the user tries to run a second copy of the application, // the existing instance should kill itself. Something like that. public static void SingleInst() { if (Process.GetProcessesByName (Process.GetCurrentProcess().ProcessName).Length > 1) Process.GetCurrentProcess().Kill(); } }
因为我希望这是一个真正的托盘应用程序,所以我发现了一个痛苦的事实,你不应该允许 C# 在 Application.Run 中创建默认的 Form1,而是创建一个类,该类将成为你的应用程序的入口点,它是一个 ApplicationContext。在第一个代码片段中引用粗体文本。
我的下一个任务是找到一种方法来钩入系统级别的鼠标和键盘。我找到了一种非常好的方法来使用 NuGet 包来实现这一点。非常感谢 George Mamaladze 在这项艰巨任务中所做的工作。我尝试了许多 .DLL,它们总是失败,要么是因为我无法理解如何使用它们,要么它们实际上根本不像广告宣传的那样工作。你可以通过 Visual Studio 中的“工具”选项通过 NuGet 包管理器获取此包,然后选择“浏览”,键入“Mouse”,你应该找到正确的包。
我不确定如何添加图像:所以我将粘贴整个应用程序如下。
using System; using System.Windows.Forms; using Gma.System.MouseKeyHook; using System.Drawing; using System.Drawing.Imaging; using System.Linq; namespace Rodent.Classes { class RodentTray : ApplicationContext { #region Global stuff public NotifyIcon RodentIcon; private IKeyboardMouseEvents m_Events; public int Rodent_X = 0; public int Rodent_Y = 0; public ImageList ImageBomb = new ImageList(); #endregion public RodentTray() { InitializeComponent(); RodentIcon.Icon = Rodent.Properties.Resources.Rodent; RodentIcon.Visible = true; Subscribe(Hook.GlobalEvents()); } private void InitializeComponent() { #region Create contex menus RodentIcon = new NotifyIcon(); // Create all context menu items and add them to notification tray icon MenuItem programMenuItem = new MenuItem("Rodent Release 1.0"); MenuItem quitMenuItem = new MenuItem("Close The Application"); ContextMenu contextMenu = new ContextMenu(); contextMenu.MenuItems.Add(programMenuItem); contextMenu.MenuItems.Add("-"); contextMenu.MenuItems.Add(quitMenuItem); RodentIcon.ContextMenu = contextMenu; // Wire up the menu item quitMenuItem.Click += QuitMenuItem_Click; #endregion } private void QuitMenuItem_Click(object sender, EventArgs e) { // Time to go RodentIcon.Visible = false; RodentIcon.Dispose(); Unsubscribe(); Application.Exit(); } public void CreateTheBombForm() { // Create the form which will display the Bomb Form BombForm = new Form(); BombForm.AutoScaleMode = AutoScaleMode.Dpi; BombForm.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; BombForm.AllowTransparency = true; BombForm.BackColor = System.Drawing.Color.Black; BombForm.TransparencyKey = System.Drawing.Color.Black; BombForm.StartPosition = System.Windows.Forms.FormStartPosition.Manual; BombForm.ShowInTaskbar = false; BombForm.ClientSize = new System.Drawing.Size(150, 204); BombForm.TopMost = true; BombForm.Activate(); // Give a little to the other guys Application.DoEvents(); BombForm.Top = Rodent_Y - 40; BombForm.Left = Rodent_X - 40; ShowBomb(BombForm); } private void ShowBomb(Form bombForm) { bombForm.Show(); ImageBomb.ImageSize = new Size(78, 121); int numberOfFrames = Rodent.Properties.Resources.SmallExplosion.GetFrameCount(FrameDimension.Time); Image[] frames = getFrames(Rodent.Properties.Resources.SmallExplosion); // Load up the array with our frames for (int imgCount = 0; imgCount < frames.Count(); imgCount++) { ImageBomb.Images.Add(frames[imgCount]); } // Get a Graphics object from the form's handle. Graphics theGraphics = Graphics.FromHwnd(bombForm.Handle); // Show the frames for (int imgCount = 0; imgCount < frames.Count(); imgCount++) { ImageBomb.Draw(theGraphics, new Point(1, 1), imgCount); Application.DoEvents(); System.Threading.Thread.Sleep(20); } theGraphics.Dispose(); bombForm.Dispose(); } Image[] getFrames(Image originalImg) { // Break the animated .gif into frames int numberOfFrames = originalImg.GetFrameCount(FrameDimension.Time); Image[] frames = new Image[numberOfFrames]; for (int imageCount = 0; imageCount < numberOfFrames; imageCount++) { originalImg.SelectActiveFrame(FrameDimension.Time, imageCount); frames[imageCount] = ((Image)originalImg.Clone()); } return frames; } #region Keyboard and Mouse hooks // Many thanks to the folks at NuGet for the wonderful work // they have done on the keyboard and mouse hooks. // Brilliant stuff private void Subscribe(IKeyboardMouseEvents events) { m_Events = events; m_Events.KeyUp += GlobalHookKeyPress; m_Events.MouseMove += M_GlobalHook_MouseMove; } public void Unsubscribe() { if (m_Events == null) return; m_Events.KeyUp -= GlobalHookKeyPress; m_Events.MouseMove -= M_GlobalHook_MouseMove; m_Events.Dispose(); } private void M_GlobalHook_MouseMove(object sender, MouseEventArgs e) { // Just track the mouse till we need the coordinates Rodent_X = e.X; Rodent_Y = e.Y; } private void GlobalHookKeyPress(object sender, KeyEventArgs e) { if(e.KeyCode == Keys.LControlKey || e.KeyCode == Keys.RControlKey) { CreateTheBombForm(); } } #endregion } }
希望这没问题。你可以下载整个项目并从 debug 文件夹运行 Rodent.exe。然后按任意 CTRL 键。
关注点
在开发这个应用程序的过程中,我学到了很多。让我困惑的一个主要问题是,播放 .mp4、gif 甚至 .AVI 文件不起作用,因为它们直到播放完毕才将系统返回给我。
这就是我创建以下方法的原因
getFrames(Rodent.Properties.Resources.SmallExplosion);
它将我嵌入的 .Gif 文件分解为帧,并允许我一次显示一张图像。在每个帧之间使用少量 .DoEvents(),我能够获得我需要的性能。
.ZIP 文件包含运行应用程序所需的一切,当然还有源代码。