文件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来实现。资料很多,不过多复述。

最后总结:

功能很简单的小工具,初学者,写的不好,如有好的建议,请尽情的提。

posted @ 2020-04-12 11:39  尼古拉斯王大拿  阅读(260)  评论(0)    收藏  举报