使用 C# 写一个哈希计算小工具

之前电脑上有一个用来计算文件哈希值的迷你小工具, 带界面能直接拖文件计算, 挺方便的. 正好最近闲着了, 于是想着自己仿造一个差不多的小工具, 顺便再补充一些缺少的哈希算法进去, 同时也能入门一下 C# 这门语言.

文末附有 Github 项目地址.

运行环境

环境选了 .NET Framework 4.6 及以上, 估计绝大部分 Windows 系统都已经自带了, 能直接运行软件.

软件功能

界面大概长这样:

hashtool.png

支持下列算法:

  • MD5
  • SHA1
  • SHA2-256
  • SHA2-512
  • SHA3-256
  • SHA3-512
  • SM3
  • CRC32

支持以下功能:

  • 拖动或者使用打开按钮对多个文件进行指定的哈希值计算
  • 实时显示当前文件计算进度与总任务计算进度
  • 可以指定计算文件哈希时是否对每个算法使用单独的线程进行加速

实现思路

窗口界面

这是我第一次用 C# 写窗口程序, 学习了一下决定用 WinForms 这个框架, 因为作为入门程序来说, 它足够简单, 容易上手.

VS 新建一个 WinForms 项目, 项目结构不复杂, 有一个主程序文件和基本的窗口文件, 我们的主要任务就是补充窗口文件的代码.

思路也很简单, VS 提供了简单的窗口设计界面, 能够用键鼠直接设计窗口元素和窗口显示的内容, 然后我们自己写对应的事件响应函数, 并把对应的函数和响应事件在设计窗口里绑定即可.

这里将主窗口 MainWnd 的代码分作了两份:

  • MainWnd.cs: 实现事件响应函数.
  • MainWnd.Func.cs: 实现主窗口真正要用到的计算功能函数.

两份文件用 partial 类将功能组合到一起.

最后额外补充了一个 About 窗口, 在 VS 菜单里添加窗口就能直接生成需要的模板文件.

多线程与进度条

作为一个窗口程序, 为了避免窗口响应卡死, 所有耗时的计算过程都必须用多线程的方式放到后台执行. 再就是程序支持用多线程并行多个算法之间的计算, 因此多线程的使用和同步问题是第一个麻烦之处.

第二个有点麻烦的地方就是显示进度条, 由于可以一次性计算多个文件, 所以分了当前文件进度和总进度, 在多线程时也是需要考虑同步问题的.

首先多线程用的 System.Threading.Tasks.Task 类, 这是自带封装好的多线程 API, 如果勾选了多线程选项, 那么就将每个算法的计算都分配一个 Task, 最后将结果汇总到一起.

有关的几个方法实现如下:

  • TaskCompHash: 对指定文件计算指定哈希算法的结果, 并累加计算进度值.
  • ComputeWithOneTaskComputeWithMultiTask: 每个文件的不同哈希算法计算任务. 两个方法都会创建子任务, 区别是前者每次只对 1 个算法创建 1 个任务计算, 后者根据算法数量创建多个同时计算. 然后轮询任务是否计算完成, 并更新进度条.
  • TaskCompute: 对每个文件计算每一种哈希算法的值.
  • BeginCompute: 创建后台计算任务, 创建完后立即返回, 不会使窗口假死.

调用关系为:

BeginCompute -> TaskCompute -> ComputeWithOneTask | ComputeWithMultiTask -> TaskCompHash

其中 Task 开头的是要被 Task 类创建以多线程方式在后台执行的方法. 如此一来便完成了对每个文件的每种哈希值的计算过程.

而进度条的实现, 则是靠 pbValueSinglepbValueTotal 两个成员来记录. 每次使用哈希算法计算时, 累加计算量相对于文件大小的百分比值, 这样对于一个文件的一种算法就有一个完整的单位 1, 所以总计算量就是文件数乘以算法数.

哈希算法实现

C# 的系统库里已经提供了大部分常见的哈希算法, 都在 System.Security.Cryptography 命名空间中, 我们需要额外实现一下SM3 和 SHA3 等算法.

具体的算法原理另写一篇记录, 这里主要看看实现思路.

查阅一下 API 文档, 可以看到 System.Security.Cryptography 提供了一个哈希算法的基类 HashAlgorithm 给我们用来继承, 并且需要重写下列方法:

  • Initialize: 初始化方法, 也就是计算一个哈希的初始准备工作, 例如一些缓冲区的初始值可以在这里设置.
  • HashCore: 核心计算过程, 有三个参数, 接收任意长度的字节数组输入, 并完成这一部分内容的计算. 这个方法可以完成对大文件的分块哈希值计算.
  • HashFinal: 结束计算方法, 比如一些尾部填充操作可以在这里进行.

继承并重写后, 我们的哈希算法就有统一的基类和接口, 这也方便我们后续的方法调用.

编译与发布

编译的方式和普通项目一样, 在菜单的 "生成" 项里进行生成即可.

值得关注的是生成时的配置, 右键查看项目属性, 里面可以设置目标框架, 如果考虑兼容性, 选低一点比较好, 这里我选了 4.6 的版本.

再就是生成里可以设置目标平台, 尽量选成 x64, 速度上有一些小的优化.

相关资源

Github 项目地址: https://github.com/ww-rm/Hashtool