在Windows操作系统中,用户可以通过鼠标属性设置来显示鼠标指针的位置。然而,对于拥有4K显示屏的用户来说,这种显示方式可能不够直观。因此,决定为弟弟编写一个更直观的应用程序,名为“Rodent”,它能够在按下CTRL键时在鼠标指针位置显示爆炸效果。
这个任务比预想的要复杂得多。目标是创建一个Windows系统托盘应用程序,它能够:
以下是实现这个功能的主要方法:
RodentTray() - 这是程序的入口点,它订阅键盘和鼠标事件,并调用InitializeComponent()方法来设置托盘图标的菜单项。
QuitMenuItem_Click(object sender, EventArgs e) - 这个方法取消订阅键盘和鼠标钩子,并退出应用程序。
CreateTheBombForm() - 这个方法创建用于显示爆炸效果的窗体。
ShowBomb(Form bombForm) - 这个方法显示窗体并展示图像,播放完最后一帧后释放所有资源。
要使用这些代码,需要在项目中添加以下引用:
using Gma.System.MouseKeyHook;
以下是确保应用程序只运行一个实例的类:
public class SingleInstance
{
public static void SingleInst()
{
if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1)
{
Process.GetCurrentProcess().Kill();
}
}
}
为了创建一个真正的系统托盘应用程序,了解到不应该让C#创建默认的Form1,而是创建一个类作为应用程序的入口点,并且它是一个ApplicationContext。
接下来,需要找到一种方法来在系统级别捕获鼠标和键盘。找到了一个非常好的解决方案,那就是使用NuGet包。非常感谢George Mamaladze为这项艰巨任务所做的工作。可以通过Visual Studio的NuGet包管理器来获取这个包。
在多次尝试之后,创建了一个类,确保应用程序只运行一个实例。
namespace Rodent
{
static class Program
{
[STAThread]
static void Main()
{
SingleInstance.SingleInst();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Rodent.Classes.RodentTray());
}
}
}
以下是应用程序的主类:
class RodentTray : ApplicationContext
{
// 全局变量
public NotifyIcon RodentIcon;
private IKeyboardMouseEvents m_Events;
public int Rodent_X = 0;
public int Rodent_Y = 0;
public ImageList ImageBomb = new ImageList();
public RodentTray()
{
InitializeComponent();
RodentIcon.Icon = Rodent.Properties.Resources.Rodent;
RodentIcon.Visible = true;
Subscribe(Hook.GlobalEvents());
}
private void InitializeComponent()
{
// 创建上下文菜单
RodentIcon = new NotifyIcon();
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;
quitMenuItem.Click += QuitMenuItem_Click;
}
private void QuitMenuItem_Click(object sender, EventArgs e)
{
RodentIcon.Visible = false;
RodentIcon.Dispose();
Unsubscribe();
Application.Exit();
}
public void CreateTheBombForm()
{
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();
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);
for (int imgCount = 0; imgCount < frames.Length; imgCount++)
{
ImageBomb.Images.Add(frames[imgCount]);
}
Graphics theGraphics = Graphics.FromHwnd(bombForm.Handle);
for (int imgCount = 0; imgCount < frames.Length; imgCount++)
{
ImageBomb.Draw(theGraphics, new Point(1, 1), imgCount);
Application.DoEvents();
System.Threading.Thread.Sleep(20);
}
theGraphics.Dispose();
bombForm.Dispose();
}
Image[] getFrames(Image originalImg)
{
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;
}
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)
{
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();
}
}
}
在开发这个应用程序的过程中,学到了很多。一个让困惑的主要问题是播放.mp4、gif或.avi文件时,它们不会将系统控制权交还给,直到它们播放完毕。这就是创建以下方法的原因:
Image[] getFrames(Image originalImg)
{
// 将动画.gif分解成帧
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;
}