65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (13投票s)

2011 年 3 月 31 日

CPOL

5分钟阅读

viewsIcon

60495

downloadIcon

592

使用一个通知区域应用程序,让一个随机出现和消失的对话框来捉弄你的同事。

引言

场景是这样的。一位同事坐到他的办公桌前。几分钟后,一个消息框弹出,告诉他 Windows 遇到一个错误。在他做出反应之前,对话框就消失了。他很紧张,然后觉得可以安全地忽略它。然后它又出现了。他去点击“取消”按钮,但窗口跳到了另一个位置。他追着它穿过他的两台显示器,直到它再次消失,然后又过了一会儿才出现。

The fake system warning dialog

在这里插入邪恶的笑声。但要小声点,因为你不想让他知道是你把这个小应用程序安装在他电脑上的。

这个应用程序是用 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 - 初始发布
© . All rights reserved.