文件MD5获取工具开发记录(二)
接上一篇: https://www.cnblogs.com/guochandabao/articles/12683838.html
这里文件操作,采用boost/filesystem
为什么选择这个呢?因为方便呗。。偷懒而已。
首先对工程进行一些必要的设置,我这里是通过vcpkg安装的boost。具体过百度一堆文章。
1.使用静态库:
在项目的 xxxxx.vcxproj中,找到如下:
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
或
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
在他们下边加:
<VcpkgTriplet>x86-windows-static</VcpkgTriplet>
<VcpkgEnabled>true</VcpkgEnabled>
原来的工程也做了修改,这里把以后会用到的结构体,单独放在一个 .h文件中
OperationFile.h
#pragma once #include <boost/filesystem.hpp> #include <Wincrypt.h> class OperationFile { public: static BOOL ReadFileInofPath(const char* path, std::vector<FILEINFO>& vec); protected: static CString RemoveSubstr(const std::string& src/*原字符串*/, const std::string& Substr/*子串*/); static BOOL OperationFile::GetMd5(CString FileDirectory, CString& strFileMd5); };
OperationFile.cpp
#include "pch.h" #include "OperationFile.h" BOOL OperationFile::ReadFileInofPath(const char* path, std::vector<FILEINFO>& vec) { boost::filesystem::path file_path = path;//要读取的文件夹 if (!boost::filesystem::exists(file_path)) //推断文件存在性 return FALSE;//文件不存在.退出 //这里文件的遍历直接用boost中的一个的方法. boost::filesystem::recursive_directory_iterator beg_iter(file_path); boost::filesystem::recursive_directory_iterator end_iter; for (boost::filesystem::recursive_directory_iterator beg_iter(file_path); beg_iter != end_iter; beg_iter++) { if (boost::filesystem::is_directory(*beg_iter))//推断file_path是否为文件夹 { continue; } else { //是文件了,那么开始处理文件信息 FILEINFO t; t.Name = beg_iter->path().filename().string().c_str(); t.RelativePath = RemoveSubstr(beg_iter->path().string(), path); GetMd5(beg_iter->path().string().c_str(), t.MD5); t.Extension = beg_iter->path().extension().string().c_str(); vec.push_back(t); } } } CString OperationFile::RemoveSubstr(const std::string& src/*原字符串*/, const std::string& Substr/*子串*/) { size_t a = Substr.length(); size_t b = src.length(); return (src.substr(a, b)).c_str(); } //#include <Wincrypt.h>//此函数中用到 BOOL OperationFile::GetMd5(CString FileDirectory,CString& strFileMd5) { HANDLE hFile = CreateFile(FileDirectory, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); if (hFile == INVALID_HANDLE_VALUE) //如果CreateFile调用失败 { //提示CreateFile调用失败,并输出错误号。visual studio中可在“工具”>“错误查找”中利用错误号得到错误信息。 CloseHandle(hFile); return FALSE; } HCRYPTPROV hProv = NULL; if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) == FALSE) //获得CSP中一个密钥容器的句柄 { return FALSE; } HCRYPTPROV hHash = NULL; //初始化对数据流的hash,创建并返回一个与CSP的hash对象相关的句柄。这个句柄接下来将被 CryptHashData调用。 if (CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash) == FALSE) { return FALSE; } DWORD dwFileSize = GetFileSize(hFile, 0); //获取文件的大小 if (dwFileSize == 0xFFFFFFFF) //如果获取文件大小失败 { return FALSE; } byte* lpReadFileBuffer = new byte[dwFileSize]; DWORD lpReadNumberOfBytes; if (ReadFile(hFile, lpReadFileBuffer, dwFileSize, &lpReadNumberOfBytes, NULL) == 0) //读取文件 { return FALSE; } if (CryptHashData(hHash, lpReadFileBuffer, lpReadNumberOfBytes, 0) == FALSE) //hash文件 { return FALSE; } delete[] lpReadFileBuffer; CloseHandle(hFile); //关闭文件句柄 BYTE *pbHash; DWORD dwHashLen = sizeof(DWORD); if (!CryptGetHashParam(hHash, HP_HASHVAL, NULL, &dwHashLen, 0)) //我也不知道为什么要先这样调用CryptGetHashParam,这块是参照的msdn { return FALSE; } pbHash = (byte*)malloc(dwHashLen); if (CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &dwHashLen, 0))//获得md5值 { for (DWORD i = 0; i < dwHashLen; i++) //输出md5值 { char str[3] = { 0 }; //CString strFilePartM = _T(""); sprintf_s(str, sizeof(str), "%02X", pbHash[i]); strFileMd5 += str; } } //善后工作 if (CryptDestroyHash(hHash) == FALSE) //销毁hash对象 { return FALSE; } if (CryptReleaseContext(hProv, 0) == FALSE) { return FALSE; } return TRUE; }
同时,修改了原来OnGetdispInfo函数
//项目不同显示内容不同,这里需要进行修改 void MFC_VirtualList::OnGetdispInfo(NMHDR* pNMHDR, LRESULT* pResult) { LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR; LV_ITEM* pItem = &(pDispInfo)->item; COLORREF TextBkColor[2] = { RGB(255,0,0), RGB(0,255,0) }; int itemid = pItem->iItem;//得到第几行 //对文字信息做处理 if (pItem->mask & LVIF_TEXT) { CString text; switch (pItem->iSubItem) { case 0: text = listdata[itemid].file.Name; break; case 1: text = listdata[itemid].file.MD5; break; case 2: text = "." + listdata[itemid].file.RelativePath; break; } lstrcpyn(pItem->pszText, text, pItem->cchTextMax); } pItem->mask |= LVIF_STATE; pItem->stateMask = LVIS_STATEIMAGEMASK; if (listdata[itemid].ischeck) { //Turn check box on.. pItem->state = INDEXTOSTATEIMAGEMASK(2); } else { //Turn check box off pItem->state = INDEXTOSTATEIMAGEMASK(1); } *pResult = 0; }
结构体如下:
struct FILEINFO { CString Name;//名字 CString MD5;//MD5 CString RelativePath;//相对路径. CString Extension;//拓展名 }; //list中数据的类型. struct LISTDATA { BOOL ischeck;//是否选中 FILEINFO file; };
最终测试如下:

接下来对目录进行设置和选择。
主要就是几个功能的使用:
1.选择文件夹。
char szDir[MAX_PATH] = { 0 }; BROWSEINFO bi; ITEMIDLIST *pidl = NULL; bi.hwndOwner = NULL; bi.pszDisplayName = szDir; bi.pidlRoot = NULL; bi.lpszTitle = "文件目录"; bi.ulFlags = BIF_RETURNFSANCESTORS; bi.lpfn = NULL; bi.lParam = 0; bi.iImage = 0; pidl = SHBrowseForFolder(&bi); if (NULL == pidl) return; if (!SHGetPathFromIDList(pidl, szDir)) return; SetDlgItemText(IDC_EDIT_FILEPATH, szDir);
2.把读取的数据写到文件保存。
BOOL OperationFile::WriteFileToDisk(const char* FileName, vector<LISTDATA>& vec) { boost::filesystem::path file_path = FileName;//设置目录 CFile file; if (boost::filesystem::exists(file_path)) //推断文件存在性 boost::filesystem::remove(file_path);//文件存在,先删除再创建 if (!file.Open(FileName, CFile::modeCreate | CFile::typeBinary | CFile::modeWrite | CFile::modeRead)) return FALSE; //开始写入文件 for (auto& x : vec) { if (x.ischeck) { CString str; str = x.file.Name + "|" + x.file.MD5 + "|" + x.file.RelativePath + "@"; file.Write(str.GetBuffer(), str.GetLength()); str.ReleaseBuffer(); } } file.Close(); return TRUE; }
3.文件目录的保存。其实也是用读写文件就可以,当然也可以用ini来实现。资料很多,不过多复述。
最后总结:
功能很简单的小工具,初学者,写的不好,如有好的建议,请尽情的提。
浙公网安备 33010602011771号