愚人节!邪恶的 Windows 对话框恶作剧





5.00/5 (13投票s)
使用一个通知区域应用程序,让一个随机出现和消失的对话框来捉弄你的同事。
引言
场景是这样的。一位同事坐到他的办公桌前。几分钟后,一个消息框弹出,告诉他 Windows 遇到一个错误。在他做出反应之前,对话框就消失了。他很紧张,然后觉得可以安全地忽略它。然后它又出现了。他去点击“取消”按钮,但窗口跳到了另一个位置。他追着它穿过他的两台显示器,直到它再次消失,然后又过了一会儿才出现。

在这里插入邪恶的笑声。但要小声点,因为你不想让他知道是你把这个小应用程序安装在他电脑上的。
这个应用程序是用 Visual Studio 2008 编写的,但应该适用于任何版本的 .NET Framework。它已经在 Windows 7 64 位和 XP 上进行了测试。
AppContext
这个恶作剧程序是通过继承 `ApplicationContext` 来运行的,这使得它可以在不需要窗体的情况下运行。如果你不熟悉这个类,或者从未写过通知区域应用程序,你可能想阅读我之前的一篇文章,在 VB.NET 中创建系统托盘应用程序[^]。从一个标准的 Windows 应用程序开始,然后添加这个类。
Public Class AppContext
Inherits ApplicationContext
#Region " Storage "
Private Frm As MsgForm
Private Rnd As Random
Private WithEvents Ico As NotifyIcon
Private WithEvents Menu As ContextMenuStrip
Private WithEvents mnuExit As ToolStripMenuItem
Private WithEvents Time As Timer
#End Region
#Region " Constructors "
Public Sub New()
Frm = New MsgForm
Rnd = New Random
Time = New Timer
Dim MaxHeight As Integer = 0
Dim Width As Integer = 0
Dim Boundary As Rectangle = Nothing
For Each S As Screen In Screen.AllScreens
Width += S.Bounds.Width
If S.Bounds.Height > MaxHeight Then MaxHeight = S.Bounds.Height
Next
mnuExit = New ToolStripMenuItem("Exit")
Menu = New ContextMenuStrip
Menu.Items.Add(mnuExit)
Ico = New NotifyIcon
Ico.Icon = My.Resources.MainIcon
Ico.ContextMenuStrip = Menu
Ico.Text = ""
Ico.Visible = True
Frm.Icon = My.Resources.MainIcon
Frm.Arena = New Rectangle(0, 0, Width - 1, MaxHeight - 1)
Time.Interval = Rnd.Next(30, 50) * 1000 'Seconds to next appearance
Time.Enabled = True
End Sub
#End Region
#Region " Event handlers "
Private Sub AppContext_ThreadExit_
(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Me.ThreadExit
Ico.Visible = False
End Sub
Private Sub mnuExit_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles mnuExit.Click
Application.Exit()
End Sub
Private Sub Time_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Time.Tick
Time.Enabled = False
Frm.Display()
Time.Interval = Rnd.Next(30, 50) * 1000 'Seconds to next appearance
Time.Enabled = True
End Sub
#End Region
End Class
类 `AppContext` 继承自 `System.Windows.Forms.ApplicationContext`。使用这个类可以使应用程序更容易扩展,并提供了 `ThreadExit` 事件,我们可以在其中确保无论应用程序如何结束,都能得到清理。为了能够关闭应用程序(来吧,即使是会计部门的那个人也应该有机会退出),我们创建了一个 `NotifyIcon`,通过它可以访问一个菜单。
为了增加趣味性,我们支持多显示器。通常,显示器是排列成一行的,所以我们的“舞台”是显示器的总宽度,高度是最高的那个。这定义了窗口可以跳转的区域。
我们使用一个 `Random` 对象来设置窗口出现的时间间隔,这有助于让他们猜测。为了演示,我将其设置为相当快,每 30 到 50 秒一次,但如果使用 300 到 900 秒(即 5 到 15 分钟),可能会更令人烦恼。
当应用程序运行时,一个图标会出现在通知区域(有时也称为系统托盘)。你(或者你的用户,如果他们好奇的话)可以右键单击图标,调出上下文菜单,然后选择 **退出**。这会结束应用程序线程,进而触发 `ThreadExit` 事件。无论线程如何结束,都会调用此事件:Windows 正在关机,用户通过进程管理器干预,等等。
MsgForm
恶作剧的窗体也很基本。
Friend Class MsgForm
Private WithEvents Time As Timer
Private pArena As Rectangle
Private Rnd As Random
Private Sounds() As System.Media.SystemSound = { _
Media.SystemSounds.Asterisk, _
Media.SystemSounds.Beep, _
Media.SystemSounds.Exclamation, _
Media.SystemSounds.Hand, _
Media.SystemSounds.Question _
}
Public Property Arena() As Rectangle
Get
Return pArena
End Get
Set(ByVal value As Rectangle)
pArena = value
End Set
End Property
Public Sub New()
InitializeComponent()
pArena = Screen.PrimaryScreen.Bounds
Rnd = New Random
Time = New Timer
End Sub
Public Sub Display()
Me.CenterToScreen()
Time.Interval = Rnd.Next(10, 40) * 500
Sounds(Rnd.Next(0, Sounds.Length - 1)).Play()
Me.Show()
Time.Enabled = True
End Sub
Private Sub MsgForm_FormClosing_
(ByVal sender As Object, ByVal e As FormClosingEventArgs) _
Handles Me.FormClosing
If e.CloseReason = CloseReason.UserClosing Then
e.Cancel = True
End If
End Sub
Private Sub MsgForm_MouseEnter(ByVal sender As Object, ByVal e As EventArgs) _
Handles Me.MouseEnter
Me.Location = New Point(Rnd.Next(0, pArena.Width - Me.Width), _
Rnd.Next(0, pArena.Height - Me.Height))
End Sub
Private Sub Time_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Time.Tick
Time.Enabled = False
Me.Hide()
End Sub
End Class
窗体有自己的 `Timer`:当计时器到期时,计时器会被禁用,窗体会被隐藏。请注意,这些值是以半秒为增量设置的。按当前代码,窗体会消失在 5 到 20 秒之间。
`Arena` 属性给出了窗体可以跳转的边界,而 `Sounds()` 数组则让窗体每次出现时都能播放一个随机的系统声音。
窗体通过调用其 `Display` 方法从 `AppContext` 激活。这会将窗体居中到当前显示器,设置计时器,播放一个随机声音,显示窗体并启动计时器。当 `Time` 到期时,计时器会被禁用,窗体会被隐藏。
`MouseEnter` 事件是事情变得有趣的地方。当用户将鼠标移入以点击其中一个按钮时,窗体就会跳转到桌面的一个随机位置。这将引发各种欢乐。如果用户设法移动到窗体的关闭按钮,那么在 `FormClosing` 事件中,关闭窗体的尝试会被取消,所以什么都不会发生,这只会增加一点点挫败感。
整合
由于这是用 VB 编写的,有一些额外的步骤(我认为)C# 程序员不需要做。第一件事是编写我们的入口点。
Public Module Launch
Public Sub Main()
Application.EnableVisualStyles()
Application.Run(New AppContext)
End Sub
End Module
接下来,我们必须告诉引导程序使用它,而不是使用起始窗体。这是通过访问 **我的项目** 界面,取消勾选 **启用应用程序框架**,然后选择 `Sub Main` 作为启动对象来完成的。禁用应用程序框架也会禁用视觉样式,这就是为什么要在 `Main` 中手动重新启用它们。严格来说,我们不需要这样做,但如果不这样做,窗体将无法正确显示。
最后一步是部署应用程序。假设你选择的受害者工作站已经安装了 .NET Framework,那么只需编译代码,将可执行文件复制到一个方便的位置,然后运行它就可以了。
给真正邪恶的人
按原样,这是一个令人讨厌但无害的恶作剧。虽然我真心希望你能保持它的无害性,但还有其他方法可以让它变得更令人讨厌。一个想法是扫描用户的机器查找声音文件,并在窗体出现时随机播放一个。或者你可以添加一个菜单,让你将应用程序线程 `sleep` 几个小时,然后再唤醒。也许窗体不显示系统警告,而是显示诸如“这里热吗,还是我的处理器过热了?”之类的抱怨,或者提供有用的建议,比如“算了吧,我们去喝一杯吧。”可能性仅限于你邪恶、扭曲的想象力。
玩得开心!如果你想出了一个特别阴险的变体,请告诉我!
历史
- 版本 1 - 2011-03-31 - 初始发布