想看演算法原理的不用看了。這裡直接用的現成的輪子。
正文開始之前,請容我先吐個槽。之前想比對我下載的一大堆影片,把重複的刪掉,所以需要計算檔案的MD5值。用Python的hashlib,我很快就寫出來幹活的指令碼。但是呢,我忽然覺得這不夠“優雅”——Windows畢竟不自帶Python。於是,我就想把它編譯成應用程式,至少是個命令列工具吧。
就這樣,我撿起許久不用的C。這玩意真的好坑啊,#include和import根本不是一回事……C語言設計之初就是為了解決跨平臺的問題,它的解決思路是由平臺提供編譯器。問題在於,VC++的庫和GCC的庫是一回事嗎?我感覺,還是一個平臺寫一套程式碼。
雖然Windows的文件一點都不好用,但是內容還是非常詳細的。下面的程式碼修改自文件中的一個C語言示例,原文連結:
https://docs。microsoft。com/en-us/windows/win32/seccrypto/example-c-program——creating-an-md-5-hash-from-file-content
程式碼(B站的程式碼塊功能簡直智*,請自行修改標頭檔案):
#include “stdio。h”
#include “windows。h”
#include “Wincrypt。h”
#include “wchar。h”
#define BUFSIZE 1024
#define MD5LEN 32
DWORD wmain(int argc, wchar_t *argv[])
{
DWORD dwStatus = 0;
BOOL bResult = FALSE;
HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0;
HANDLE hFile = NULL;
BYTE rgbFile[BUFSIZE];
DWORD cbRead = 0;
BYTE rgbHash[MD5LEN];
DWORD cbHash = 0;
CHAR rgbDigits[] = “0123456789abcdef”;
LPCWSTR filename = argv[1];
// Logic to check usage goes here。
hFile = CreateFile(filename,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
dwStatus = GetLastError();
wprintf(L“Error opening file %ls\nError: %d\n”, filename,
dwStatus);
return dwStatus;
}
// Get handle to the crypto provider
if (!CryptAcquireContext(&hProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT))
{
dwStatus = GetLastError();
printf(“CryptAcquireContext failed: %d\n”, dwStatus);
CloseHandle(hFile);
return dwStatus;
}
if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
{
dwStatus = GetLastError();
printf(“CryptAcquireContext failed: %d\n”, dwStatus);
CloseHandle(hFile);
CryptReleaseContext(hProv, 0);
return dwStatus;
}
while (bResult = ReadFile(hFile, rgbFile, BUFSIZE,
&cbRead, NULL))
{
if (0 == cbRead)
{
break;
}
if (!CryptHashData(hHash, rgbFile, cbRead, 0))
{
dwStatus = GetLastError();
printf(“CryptHashData failed: %d\n”, dwStatus);
CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
CloseHandle(hFile);
return dwStatus;
}
}
if (!bResult)
{
dwStatus = GetLastError();
printf(“ReadFile failed: %d\n”, dwStatus);
CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
CloseHandle(hFile);
return dwStatus;
}
cbHash = MD5LEN;
if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0))
{
for (DWORD i = 0; i < cbHash; i++)
{
printf(“%c%c”, rgbDigits[rgbHash[i] >> 4],
rgbDigits[rgbHash[i] & 0xf]);
}
wprintf(L“ %ls”, filename);
printf(“\n”);
}
else
{
dwStatus = GetLastError();
printf(“CryptGetHashParam failed: %d\n”, dwStatus);
}
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
CloseHandle(hFile);
return dwStatus;
}
功能與Linux的md5sum命令基本一致,效果如下:
使用PowerShell的Get-FileHash命令進行驗算,結果如下:
已知Bug:非英文字元可能無法正確顯示,但是不影響MD5值的計算。