编程恶作剧:一个简单的桌面干扰程序

想象一下,同事正在电脑前工作,突然一个错误消息框弹出,告诉他Windows遇到了一个错误。在他还没来得及反应的时候,消息框消失了。他开始感到焦虑,但认为可以安全地忽略它。然后,消息框又回来了。他试图点击取消按钮,但消息框在他的两个显示器之间跳来跳去,直到它再次消失,然后又回来了。

这里可以插入一个邪恶的笑声,但请保持安静,因为不希望他知道是在他的机器上安装了这个小程序。

这个程序是用Visual Studio2008编写的,但应该可以在任何版本的.NET Framework上运行。它已经在Windows 7 64位和XP上进行了测试。

应用程序上下文

这个恶作剧程序是基于ApplicationContext的派生类运行的,它允许程序在不需要表单的情况下运行。如果不熟悉这个类或者从未编写过通知区域应用程序,可能希望阅读之前的文章《在VB.NET中创建系统托盘应用程序》。

从一个标准的Windows应用程序开始,然后添加这个类。

Public Class AppContext Inherits ApplicationContext ' 存储区域 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 ' 构造函数 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 End If 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 ' 秒到下次出现 Time.Enabled = True End Sub ' 事件处理 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 ' 秒到下次出现 Time.Enabled = True End Sub End Class

AppContext类继承自System.Windows.Forms.ApplicationContext。使用这个可以让应用程序更容易扩展,并且提供了ThreadExit事件,可以保证无论应用程序如何结束,都能进行清理。为了能够关闭应用程序(来吧,即使是会计部门的那个家伙也应该给他一个出路),创建了一个NotifyIcon,可以通过它访问菜单。

为了让这个更有趣,允许使用多个显示器。通常,显示器是一排排列的,所以“竞技场”是显示器的总宽度,以及最高的显示器的高度。这定义了表单可以跳跃的区域。

使用Random对象来设置表单出现的时间,这有助于让他们猜测。为了演示目的,已经将其设置得相当快,每30到50秒一次,但如果使用300到900秒,也就是5到15分钟,可能会更烦人。

当应用程序运行时,通知区域(有时也称为系统托盘)会出现一个图标。(或者用户,如果他们好奇的话)可以右键单击图标来拉出上下文菜单并选择Exit。这将结束应用程序线程,进而触发ThreadExit事件。这个事件无论线程如何结束都会被调用:Windows正在关闭,用户通过进程管理器进入,等等。

恶作剧表单

恶作剧表单也相当基础。

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, 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, 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

接下来,必须告诉引导程序使用它而不是起始表单。这是通过转到My Project界面,取消选中Enable application framework并选择Sub Main作为启动对象来完成的。禁用应用程序框架也会禁用视觉样式,这就是为什么在Main中手动打开它们的原因。严格来说,不需要这样做,但如果不这样做,表单看起来就不会对。

最后一件事是部署应用程序。假设选择的受害者的工作站上已经安装了.NET框架,那么编译代码,将可执行文件复制到方便的地方,然后运行它就可以了。

对于真正的邪恶

正如所写的,这是一个烦人但无害的恶作剧。虽然真心希望保持它无害,但还有其他方法可以让它更加烦人。一个想法是扫描用户的机器以查找声音文件,并在表单出现时随机播放一个。或者可以添加一个菜单,让可以将应用程序线程休眠几个小时然后再唤醒。也许表单可以显示像“这里很热还是处理器过热了?”这样的抱怨,或者提供有用的建议,比如“去喝杯啤酒吧。”可能性只受邪恶、扭曲的想象力的限制。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485