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

壁纸收集 - C# WinForms 实用工具 - v1.1.0.2

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.74/5 (18投票s)

2020年4月8日

CPOL

18分钟阅读

viewsIcon

31090

downloadIcon

2595

一个易于使用的工具,用于保存您的 Windows Spotlight(锁屏界面)图像,用作桌面壁纸

引言

Windows 10 为我们带来了 Windows 聚焦的精美艺术作品。Windows 聚焦包含微软分享的不断变化的、来自世界各地的精美图像,显示在您的锁屏/登录屏幕上,并附带一些关于图像拍摄地点和有时拍摄方式的背景信息。Windows 聚焦允许您对呈现给您的图像进行点赞或踩,以便它可以提供更多您喜欢的图像类型。很有可能您的选择被用于修剪其图像识别和分类的人工智能后端。

许多人,包括我自己,都对收集这些令人惊叹的图像作为 Windows 壁纸感兴趣。下面的文章将讨论我如何整合各种 C# 类来创建这个实用工具,以及如何轻松地使用该实用工具收集 Windows 聚焦的图像供您自己使用。

Main Form U I

微软承认任何人都可以探索 Windows 聚焦提供的数据。

此页面引领我深入了开发之旅,最初只是一个简单的 .BAT(批处理)文件,最终形成了这个应用程序。

背景

为了解决我在开发此应用程序时遇到的挑战,文章将涵盖几个主题,例如自定义控件、密码加密以及访问密码保护的 UNC 路径。

虽然我认为本文涵盖的主题在任何方面都不是开创性的,但它们可能对正在学习 C# 的人有所帮助。我希望这篇文章能鼓励其他初学者作者通过组合这个实用工具和其他 Code Project 文章中的部分内容来创造新的工具。

使用程序

运行程序时,它首先会从您的配置文件中收集您的 Windows 用户名。这用于构建一个路径,指向 Windows 聚焦存储您在锁屏上看到的“略有隐藏”的图像的位置(假设您已将 Windows 聚焦设置为锁屏背景……为什么不呢!)。

这可以在“源文件夹:”标签下的文本框中看到。您不必修改此处的值。

Image Source Folder

所有文件夹都提供了文件夹选择和浏览按钮,以便您可以选择并直观地探索它们。

Folder Select And Browse Buttons

这些更多地用于目标文件夹,包括本地和下面的 UNC 文件夹。

接下来,您应该考虑您对哪些图像方向最感兴趣。例如,起初我只想要保存风景图像作为桌面壁纸;也就是说,水平尺寸大于垂直尺寸的图像(就像大多数电脑显示器一样)。提供复选框可让您选择您的偏好。

Image Mode Section Checkboxes

当您单击“浏览”按钮时(在不久的将来),模式选择复选框将决定自动选择哪些图像进行保存/复制。

目前有两种规则用于浏览和显示您要选择和收集的图像;一种是文件大小规则,一种是文件年龄或修改日期规则。这些项目只会浏览大于指定大小(以 KB 为单位)的图像以及修改日期晚于指定日期的图像。默认日期是应用程序运行前一个月。

接下来,单击“浏览”规则旁边的“浏览”按钮。应用程序将根据您的浏览规则收集图像,并在主窗口面板中显示它们,并且会“选择”任何符合您上面勾选的选择标准复选框的图像。

Image Gallery Example

您可以通过单击图像本身或旁边的复选框来选择将要(稍后)“处理”的图像。

注意:如果您只从一台机器保存图像,可以选择使用 重命名 功能。这样,您的图像就可以被命名为 Wallpaper00001.jpg 等,而不是保留一些巨大的十六进制字符串(例如 36a2432655c43fced237e...)的原始图像文件名。当您从多台机器收集图像并将其存储在同一个“”文件夹(例如,一个 UNC 文件夹)中时,这会产生问题。

应用程序右下角有一组手动图像选择按钮,可供您选择使用。按钮标签应该不言自明。

Manual image selection buttons

选择要保存的图像后,您需要指定保存位置。以下控件允许您选择在稍后单击“复制到本地”和“同步到 UNC 路径”按钮时图像的保存位置。

Image destination folders

目标文件夹只是一个您选择用来存储图像的常规文件夹。Windows 聚焦图像实际上只是 JPEG 文件,只需稍作修改即可使用。

我的 Netgear 路由器能够与连接到我家庭网络的任何计算机共享文件和文件夹。我发现将我的“主”壁纸图像集合放在一个密码保护的网络共享上很有用,这样我房子里的所有计算机都可以使用这些图像。UNC 用户名、密码、域和用户路径用于连接到我的密码保护网络共享。您可以使用同一网络上任何具有写入权限的设备上的任何密码保护的 UNC 路径。

使用 UNC 路径是可选的。如果您不想使用此功能,只需将所有字段留空即可。

如果您使用 Dropbox 或其他云存储,您可以更进一步,将任一文件夹指定为您的云共享,从而将您的图像同步到云端。我的路由器已经具备此功能,并且比按月支付 Dropbox 的费用要便宜得多。

一旦选择了您的图像并指定了目标文件夹,您只需单击“复制到本地”按钮即可。这将把所有选定的图像文件复制并重命名到您选择的目标目录。

如果您正在使用 UNC 路径选项,您可以单击“同步到 UNC 路径”按钮。这意味着同步,或复制回,UNC 路径上的所有文件到本地目标文件夹,前提是这些文件尚不存在于目标文件夹中。

我的本地网络上有很多(太多)机器。我在所有机器上都运行此程序。它们都将数据馈送到我的 UNC 路径文件夹。您可能没有意识到,每台机器从微软的 Windows 聚焦图库获得的图像都不同。换句话说,我可以从我的笔记本电脑收集图像 A、B 和 C,从我的工作站收集图像 D、E 和 F。我将它们全部复制到我的路由器共享上的 UNC 路径。然后,当我单击“同步到 UNC 路径”按钮时,我将在单击此按钮的节点上的本地文件夹中获得图像 A、B、C、D、E 和 F。

您可以使用目标文件夹或 UNC 路径浏览按钮(黄色文件夹图标按钮)来检查您当前的收藏。

Destination image browse buttons

单击我的目标文件夹上的黄色文件夹浏览按钮会显示我机器上收集到的所有本地可用图像。

Wallpaper gallery folder

这有助于验证功能,也方便您通过右键单击图像并选择“设置为桌面背景”来轻松选择图像。

Set as desktop wallpaper right-click option from folder view

重复图像检测

随着我的图像收藏随着该实用工具的 v1.0 版本不断增长,我开始注意到一些相同的图像,但出于某种原因,微软提供的文件名却不同。因此,我很快意识到需要在此实用工具中加入重复图像检测算法。接下来的屏幕截图是从特定一天的主图像浏览界面直接截取的。注意相同的文件大小、相同的图像,但文件名却不同。

The need for duplicate image removal logic

在 v1.0 中,我有一个逻辑来计算每个图像的基于哈希的校验和,因为我预料到将来可能会出现重复问题,但并没有处理这些校验和数据。在 v1.1.0.2 中,我终于能够将其用作比较数据的一部分,以检测和删除重复图像,从而节省宝贵的硬盘空间。

当您单击“扫描重复项”按钮时,程序将首先通过比较校验和值和文件大小来扫描指定本地存储文件夹中的重复图像。对于检测到的每个重复项,程序都会询问您是要删除还是保留该重复图像。

Duplicate images detected

本地目录处理完成后,程序会询问您是否要在 UNC 存储路径上执行相同的扫描。程序会警告您这可能需要很长时间,因为网络存储设备通常响应速度比本地连接的硬盘驱动器慢。

建议每隔几个月扫描一次远程 UNC 路径,因为如果您像我一样,并且使用此程序从多台机器收集图像以馈送到单个 UNC 存储路径,您最终肯定会遇到重复图像。这只是时间问题。

最终用户摘要

在编写此实用工具之前,我使用简单的 Powershell 脚本来处理文件复制、重命名、删除以及图像集合的通用管理。我认为一个应用程序可以帮助减少手动步骤,尤其是在几乎每天都在多台机器上运行时。此外,我相信一个相当简单的 Windows / .NET WinForms 应用程序可以帮助那些不知道批处理文件或 Powershell 脚本是什么的人。

希望您觉得这个应用程序有用!现在,让我们继续深入研究代码以及幕后所需的知识!

代码和 C# 类

这个应用程序中有数千行代码。我不会在这里列出所有内容,甚至不会列出有用的代码片段,我只会引导您参考附带的 Visual Studio 解决方案和项目。该解决方案是用 Visual Studio 2019 创建的,但在 2017 中也应该可用。作为一项学习练习,建议在 Visual Studio 中打开解决方案的同时阅读本文的这一部分。

子项目

我最先需要的是一个自定义控件,用于显示源图像比现有的 .NET 控件允许的更大的缩略图。虽然 ImageListImageView 很有用,但它们并没有完全满足我的需求。除了显示大的缩略图图像之外,我还需要保持原始图像的完整性,并存储/显示计算出的文件校验和,以帮助确定图像的唯一性(以避免重复图像),尤其是在已重命名图像的情况下。我还需要支持透明或半透明图像并为此类图像提供反馈(例如,以编程方式叠加其他图像等)。

因此,主项目的 ImageListViewer 子项目应运而生。(请参阅源代码。)该项目包含三个简单的类。

ImageListViewerControl 本质上是一个“复合”(而不是完全自定义/从头开始)控件,由 WinForms 的 PictureBoxTextboxCheckbox 组成。

此控件具有一些简单的属性和本地函数来调节其外观。为了清晰地表示源图像,一个“透明背景网格”,类似于 GIMP 或其他图像程序中使用的网格,被绘制为该控件的背景。这,加上一个简单的缩放算法,该算法将源图像居中并缩放到大的缩略图,以便您清楚地看到图像方向,这是创建此控件的主要目的。请注意,此控件内的位图图像合并逻辑支持 alpha 通道。您可以通过将浏览规则文件大小减小到 0 并重新浏览源图像来测试这一点。源目录通常(但并非总是)包含许多透明图标类图像,与精美的 Windows 聚焦图片相比,它们的大小非常小。这就是存在浏览规则控件的原因!:-P

目前显示源图像文件名、校验和和文件大小作为元数据。未来版本将包含更多元数据。

此控件中的单击事件用于设置控件的 Selected 属性,并向上级控件 ImageListViewerControlCollection 触发事件。

ImageListViewerControlCollection 是一个 WinForms Panel,具有预先配置的某些滚动条属性和外观属性。

此控件使用一组简单的属性和一个网格/对象定位算法来绘制一个或多个 ImageListViewerControl 子控件。

此控件还会将来自其子控件的单击事件向上(有时称为冒泡,因为气泡会上升)传递给控件的父级,在本例中是主应用程序。

ImageListViewerControlEventArgs 类由上述两个控件使用,以通知父应用程序 WallpaperCollection.exe 关于自定义控件内部发生的鼠标事件。

这种层次结构是必要的,以便控件能够响应它们内部的事件,同时也能够通知父应用程序正在发生的事情以及与事件相关的相关数据。

主项目

主项目应用程序 WallpaperCollection 项目大量使用了子项目控件,但还包含一些有趣的类,用于提供该应用程序所需的各种功能。

您可能会在自己的代码中发现有用的第一个类是 FileChecksum 类。

此类使用 .NET 的 Cryptography 类,可以轻松地从给定文件中生成校验和/或哈希值,还可以根据先前生成的校验和值验证现有文件是否未被修改。使用此类就像这样简单:

// create new instance of our checksum class based on a existing filename
FileChecksum fc = new FileChecksum(tempFile.FullName);

// call method to calculate the file checksum
fc.CalculateChecksum(this);

// fetch the checksum for your use
string myChecksum = fc.Checksum;

Wallpaper Collection 使用此类来记录图像校验和,并将其存储在本地数据文件中已处理的图像中,以便将来避免重复。目前,计算出的校验和显示在关联图像的 ImageListViewerControl 中。目前没有做太多其他事情,但为未来功能奠定了基础。

此类中的 VerifyChecksum 方法使用 PrecalculatedHashChecksumFilename 属性来验证现有文件在创建预计算(哈希)校验和时未被修改。这可以提醒您文件在公开发布后被未经您同意地修改,或者提醒您上传损坏等。

您还可以使用此类配合回调函数,例如,图形进度条百分比更新。但这需要对现有代码进行细微的更改,因为此应用程序的用例与我最初编写此类时的用例不同。回调函数对于处理 L A R G E 文件很有用,以便用户在文件被评估和创建或验证校验和时获得视觉反馈(通常在另一个线程上)。如果有人感兴趣,我可以提供示例。

您可能会在自己的代码中发现有用的下一个类是 SecureStorageEngine 类。

此类有些独特,因为我将此类从旧的 Borland C++ Builder 源代码(多年前)转换而来,当时我又从手工编写的汇编语言转换而来。您可以在 C# 版本中清楚地看到汇编语言的根源。

代码虽然老旧,但很有用。它使用自定义的 256 位加密系统提供双向加密。这用于在应用程序设置文件中为每个 Windows 用户安全地存储 UNC 文件夹访问密码。不应再有将密码存储在任何纯文本文件中的借口!

值得一提的是,此类有几个构造函数,每个构造函数都创建不同的“主”密钥;这是所有加密和解密逻辑的基础。您可以使用默认构造函数的默认主密钥(请参阅此类源代码中的成员变量 _key)。您也可以指定自己的密钥(通过参数),最多 32 个字符。您可以依靠该类自动创建一个机器唯一的密钥(生成需要一到两秒钟),该密钥基于计算机的节点名称、主网卡的 MAC 地址等。

还值得指出的是,如果您通过字符串参数创建自己的密钥,您必须记住此密钥并在所有将来的解密尝试中使用它。如果您丢失了密钥,加密数据将保持无法读取。同样,使用创建机器唯一密钥的构造函数更安全,但也不能在机器之间移植。该类故意设计为不通过属性公开创建的机器唯一密钥。

凭借现代 GPU 驱动的暴力破解软件,这当然不是“牢不可破”的加密。如果您使用的是完整的 256 位密钥以及足够长的密码(如您应该使用的),那么这应该可以阻止大多数暴力破解尝试,因为此类攻击所需的时间。与所有安全相关问题一样,更长的密码总是好的。

使用此类可能像以下一样简单:

// create new instance of our encryption engine
SecureStorageEngine sseEncrypt = new SecureStorageEngine("f0f70002sometextabcd9876bxaxffsi");

// set input property to encrypt
sseEncrypt.EncryptionInput = "YourPlaintextPasswordGoesHere"

// perform the encryption
sseEncrypt.Encrypt();

// fetch the encrypted value
string myEncryptedPassword = sseEncrypt.EncryptionOutput;

如果您不使用与上面显示的“f0f70002sometext....”相同的密钥来解码加密值,您将无法检索原始密码,除非诉诸于黑客或暴力解密方法。

目前,主密钥字符串参数只能使用字母数字字符。

您可能会在自己的代码中发现有用的另一个类是 UNCSecureAccess 类。此类基于我在 Code Project 上找到的一篇文章,题为 Connect to a UNC Path with Credentials,作者是 Code Project 用户 hayes.adrian(大概是 Adrian Hayes?!)

我非常感谢这位好心人提供了一个研究充分且完美的示例。这帮助我将这个图像应用程序连接到我的路由器上的安全共享。

此类使用 NetAPI32 的函数以编程方式访问网络 UNC 资源。我只对原始文章中提供的代码做了一些修改,以便更容易地与此应用程序一起使用(与原始文章中的 Windows 服务相比,这是一个交互式用户应用程序)。对于您 hayes.adrian,我表示感谢!这在我脑海中触发了许多新的项目想法。

有两个非常简单的类,WallpaperItemWallpaperItems,它们允许轻松操作源图像和收集源图像。第一个类还存储了我希望跟踪并保存到磁盘的一些图像元数据(例如,源图像的原始文件名、重命名后的文件名(如果已重命名)、文件校验和等)。

主项目中的其余代码只是将其他类连接到一个简单但有用的 WinForms 应用程序。

我在工作时间以及之后常常被迫盯着四面单调的墙壁。任何能从日常生活中解脱出来的事情,无论多么微小,在这种情况下,来自世界各地的 Windows 桌面壁纸,都是我周围环境的一个受欢迎的补充。我非常支持任何艺术追求。艺术是我们教育体系在经济困难时期以及生活中首先遭受损失的领域之一。对我来说,这是一个令人悲伤且不必要的与前几代人背离的现象,用一个相当极端的例子来比较,就像文艺复兴。

关注点

解决方案源代码中包含了许多对初学者 C# 程序员感兴趣的项目。例如:

如何在 WinForms 标题中显示嵌入在程序集中的版本信息

// put this with the rest of the "using" declarations at the top of your main WinForms class
using System.Reflection;

.
.
.

// put this in the constructor of your main WinForms class
Assembly asm = Assembly.GetExecutingAssembly();
this.Text = "Wallpaper Collection v" + asm.GetName().Version.ToString();

如何获取存储 .NET 应用程序设置的 Windows 登录用户配置文件文件夹

// get the window user's profile directory
string myUserProfileFolder = 
   Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\AppData\Local\";

源代码中还散布着其他有用的项目。是的,这些项目很容易通过 Google 搜索到,但乐趣在于探索!

我鼓励您使用 Visual Studio 调试器,并在您选择的断点启用时逐行单步执行代码,以查看程序启动时以及每次单击按钮时会发生什么。我希望您能在自己的 C# 项目中找到本文中包含的一些信息。至少,我希望您喜欢这个应用程序,并为您个人使用构建一个漂亮的壁纸收藏。

这个应用程序我只花了大约 4 个兼职日来编写,前提是我已经拥有了安全存储/加密类和文件校验和类,它们是从之前的项目中略有不同的变体。考虑到我为这个项目投入的时间,我认为结果非常值得,并且可以使许多人受益。

待办事项列表

现在已经添加了图像重复逻辑,我可以专注于剩余的目标。

在未来的版本中,我想添加加载/执行小型命令脚本的能力,以便整个过程可以自动化。换句话说,一旦构建了“命令”文件,您就可以运行此程序(在后台),它将根据您的配置自动收集和复制图像。

在时间允许的情况下,我打算弄清楚每个 Windows 聚焦图像的元数据是如何以及存储在本地的,例如照片的位置信息,这样我就可以记录并将这些信息编码到目标文件夹的位图(.JPG)文件中作为自定义块,以及/或显示它。

历史

  • v1.0.0.0 - 初始发布 - 不完美但肯定可用!
  • v1.1.0.2 - 添加了重复图像清理 UI 和逻辑
© . All rights reserved.