使用 C# 写一个哈希计算小工具
之前电脑上有一个用来计算文件哈希值的迷你小工具, 带界面能直接拖文件计算, 挺方便的. 正好最近闲着了, 于是想着自己仿造一个差不多的小工具, 顺便再补充一些缺少的哈希算法进去, 同时也能入门一下 C#
这门语言.
文末附有 Github 项目地址.
运行环境
环境选了 .NET Framework 4.6
及以上, 估计绝大部分 Windows 系统都已经自带了, 能直接运行软件.
软件功能
界面大概长这样:
支持下列算法:
- 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
: 对指定文件计算指定哈希算法的结果, 并累加计算进度值.ComputeWithOneTask
和ComputeWithMultiTask
: 每个文件的不同哈希算法计算任务. 两个方法都会创建子任务, 区别是前者每次只对 1 个算法创建 1 个任务计算, 后者根据算法数量创建多个同时计算. 然后轮询任务是否计算完成, 并更新进度条.TaskCompute
: 对每个文件计算每一种哈希算法的值.BeginCompute
: 创建后台计算任务, 创建完后立即返回, 不会使窗口假死.
调用关系为:
BeginCompute
-> TaskCompute
-> ComputeWithOneTask | ComputeWithMultiTask
-> TaskCompHash
其中 Task
开头的是要被 Task
类创建以多线程方式在后台执行的方法. 如此一来便完成了对每个文件的每种哈希值的计算过程.
而进度条的实现, 则是靠 pbValueSingle
和 pbValueTotal
两个成员来记录. 每次使用哈希算法计算时, 累加计算量相对于文件大小的百分比值, 这样对于一个文件的一种算法就有一个完整的单位 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