MFC 制作简易文件查看器

    科技2024-11-28  17

    制作一个文件查看器,并输出到 list 控件中

    1 创建一个基于对话框 MFC项目2 向对话框内添加一些控件,比如(个性化):3 根据需求对各个控件添加 变量3.1 初始化 list 控件 4 编写响应消息的函数(OnBnClickedButton2())4.1 遍历文件函数4.2 遍历进程4.2.1 遍历模块 总结

    实现效果(按钮写错,懒得截图再改,,,,遍历进程)

    纯小白,外行转行,其中逻辑和实现肯定有不足的地方; 如有错误,欢迎指正

    1 创建一个基于对话框 MFC项目

    新建项目 MFC 应用(基于对话框)

    2 向对话框内添加一些控件,比如(个性化):

    根据自己想要实现的功能,添加控件,比如 “路径:”【Static Text】 ,这种不需要做出响应和改变的控件,就可选用 静态文本因为要根据输入的路径进行遍历,所以我们需要输入文本,即选择 【Edit Control】我们要将遍历后得到的结果显示出来,选择输出到 【List Control】 控件,将其设置成 report 模式而将 path 、遍历 并输出显示到 list控件 联系起来,需要一个按钮 【Button】控件

    3 根据需求对各个控件添加 变量

    根据第二步中提到的控件和需求,要生成【两个成员变量】和【一个成员函数】 可编辑框 -> 添加变量 (值);list控件 -> 添加变量(控件)双击 button(遍历文件)

    3.1 初始化 list 控件

    BOOL CfileMgrDlg::OnInitDialog(); // 这里设置初始化代码 // TODO: 在此添加额外的初始化代码 // 初始化 list 控件 // 1 设置 list 风格 m_list.SetExtendedStyle(LVS_EX_GRIDLINES); // 2 添加列 m_list.InsertColumn(0, L"文件名", LVCFMT_CENTER, 300); m_list.InsertColumn(1, L"文件大小(KB)", LVCFMT_CENTER, 150); // 这里先注释掉遍历进程的部分 // m_list.InsertColumn(0, L"进程信息", LVCFMT_CENTER, 400); // m_list.InsertColumn(1, L"PID", LVCFMT_CENTER, 100);

    4 编写响应消息的函数(OnBnClickedButton2())

    需要获取编辑框内的路径 UpdateData(TRUE);根据获取到的路径,遍历 EnumFile(m_path); // 自己编写函数 // 遍历文件按钮 void CfileMgrDlg::OnBnClickedButton2() { // TODO: 在此添加控件通知处理程序代码 // 获取编辑框中的目标路径 UpdateData(TRUE); // 遍历文件 EnumFile(m_path); }

    4.1 遍历文件函数

    EnumFile;同样在 XxxDlg.h 中声明,因为要传入 m_path 参数,并输出显示到 List 中

    CString 好方便,可以看一下它的用法

    FindFirstFile(fullpath, &fileData); 用法

    // 函数原型 FindFirstFileW( _In_ LPCWSTR lpFileName, _Out_ LPWIN32_FIND_DATAW lpFindFileData // 所以它需要一个指针,又由于是 _Out_ ,所以函数结束时输出到这个地址; ); // 所以要先定义出一个结构体 WIN32_FIND_DATA fileData = {}; HANDLE hFile = FindFirstFile(fullpath, &fileData); // 遍历文件 void EnumFile(CString filepath); void CfileMgrDlg::EnumFile(CString filepath) { // 拼接完整路径 CString fullpath = filepath + L"\\*"; // 查找第一个文件 WIN32_FIND_DATA fileData = {}; HANDLE hFile = FindFirstFile(fullpath, &fileData); if (hFile != INVALID_HANDLE_VALUE) { int i = 0; // 插入文件行数? do { // 这里需要过滤两个文件夹,避免递归时无线循环 if (wcscmp(fileData.cFileName, L".") == 0|| wcscmp(fileData.cFileName, L"..") == 0 ) { continue; } // 如果找到的是目录,递归遍历 if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { EnumFile(filepath + L"\\" + fileData.cFileName); continue; } // 如果要遍历某种特定类型的文件,if(wcscmp(L".exe",PathFindExtension(fileData.cFileName))==0) // 如果是普通文件,就输出到 List m_list.InsertItem(i, filepath + L"\\" + fileData.cFileName); // 获取文件大小 DWORD dwSize = (fileData.nFileSizeHigh * (MAXDWORD + 1)) + fileData.nFileSizeLow; // 将字节转换为KB dwSize /= 1024; // 转换成字符串格式 wchar_t tmp[100] = {}; wsprintf(tmp, L"%d", dwSize); m_list.SetItemText(i, 1, tmp); i++; // 下一个文件 } while (FindNextFile(hFile, &fileData)); } }

    获取文件大小,查找 MSDN, WIN32_FIND_DATA fileData = {}; 所以查找的是,WIN32 那个结构体

    ​ 【文件的大小等于(nFileSizeHigh *(MAXDWORD +1))+ nFileSizeLow】

    // 获取文件大小 DWORD dwSize = (fileData.nFileSizeHigh * (MAXDWORD + 1)) + fileData.nFileSizeLow;

    4.2 遍历进程

    初始化内添加列的信息,代码类似(遍历文件和进程不能同时使用) // 遍历进程 void CfileMgrDlg::OnBnClickedButton3() { // TODO: 在此添加控件通知处理程序代码 // 创建快照 HANDLE hSnapProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); PROCESSENTRY32 pe = { sizeof(pe) }; if (Process32First(hSnapProcess, &pe)) { int i = 0; do { // 插入进程信息 m_list.InsertItem(i, pe.szExeFile); // 转换成字符穿格式 wchar_t tmp[100] = {}; wsprintf(tmp, L"%d", pe.th32ProcessID); m_list.SetItemText(i, 1, tmp); i++; } while (Process32Next(hSnapProcess, &pe)); } }
    4.2.1 遍历模块

    需求:点击遍历好的进程,并可以弹出模块信息

    1 所以一定主对话框一定有 响应双击(自定义)的消息2 响应双击消息的函数内,会弹出另一个对话框,里面有相应进程的【模块信息】

    实现 :

    1

    使用类向导 ctrl+shift+x

    自定义命名生成的代码

    根据鼠标双击得到的 行数 void CfileMgrDlg::OnDblclkShowModule(NMHDR* pNMHDR, LRESULT* pResult) { LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR); // TODO: 在此添加控件通知处理程序代码 // 获取[行数] int item = pNMItemActivate->iItem; // 获取该行的进程 PID CString strPid = m_list.GetItemText(item, 1); // 字符串转换成整型 DWORD dwPid = 0; swscanf_s(strPid, L"%d", &dwPid);

    遍历哪个进程的模块,我们需要一个新的对话框来 显示 这些信息

    2

    新增一个对话框资源,并右键添加类(不知道为什么没有初始化函数,类向导添加,选对自己刚刚添加的类名,在虚函数内 OnInitDialog -> 编辑代码)

    拖入 List Control 控件,右键它添加变量 m_list,并初始化

    // 增加初始化函数,类向导内 BOOL CModule::OnInitDialog() { CDialogEx::OnInitDialog(); // TODO: 在此添加额外的初始化 // 初始化list控件 // 设置list的扩展风格 m_list.SetExtendedStyle(LVS_EX_GRIDLINES); // 添加列 m_list.InsertColumn(0, L"模块名", LVCFMT_CENTER, 300); m_list.InsertColumn(1, L"模块基址", LVCFMT_CENTER, 200); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE }

    此时,回过头看,刚刚主对话框响应 双击List控件 的消息的 函数

    所以我们还差一个遍历模块(指定进程ID) 的函数,还差一个遍历模块的函数没有写 EnumModule(dwPid) // 响应双击进程信息,弹出另一个对话框显示出该进程的模块信息 void CfileMgrDlg::OnDblclkShowModule(NMHDR* pNMHDR, LRESULT* pResult) { LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR); // TODO: 在此添加控件通知处理程序代码 // 获取行数 int item = pNMItemActivate->iItem; // 获取该行的进程 PID CString strPid = m_list.GetItemText(item, 1); // 字符串转换成整型 DWORD dwPid = 0; swscanf_s(strPid, L"%d", &dwPid); // 创建显示模块信息的对话框 CModule* pObjModule = new CModule; // 模态对话框,方便点 //pObjModule->DoModal(); // hehe, 有bug // 创建非模态窗口 pObjModule->Create(IDD_DIALOG1, this); // 显示窗口 pObjModule->ShowWindow(SW_SHOW); // 遍历模块 pObjModule->EnumModule(dwPid); // 关于为什么要堆空间,局部变量出函数会被销毁 // 点开后就闪退 //CModule ObjModule; //ObjModule.Create(IDD_DIALOG1, this); //ObjModule.ShowWindow(SW_SHOW); //ObjModule.EnumModule(dwPid); *pResult = 0; } // 响应双击进程信息,弹出另一个对话框显示出该进程的模块信息 void CfileMgrDlg::OnDblclkShowModule(NMHDR* pNMHDR, LRESULT* pResult) { LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR); // TODO: 在此添加控件通知处理程序代码 // 获取行数 int item = pNMItemActivate->iItem; // 获取该行的进程 PID CString strPid = m_list.GetItemText(item, 1); // 字符串转换成整型 DWORD dwPid = 0; swscanf_s(strPid, L"%d", &dwPid); // 创建显示模块信息的对话框 CModule* pObjModule = new CModule; // 模态对话框,方便点 //pObjModule->DoModal(); // hehe, 有bug // 创建非模态窗口 pObjModule->Create(IDD_DIALOG1, this); // 显示窗口 pObjModule->ShowWindow(SW_SHOW); // 遍历模块 pObjModule->EnumModule(dwPid); // 就是它 // 关于为什么要堆空间,局部变量出函数会被销毁 // 点开后就闪退 //CModule ObjModule; //ObjModule.Create(IDD_DIALOG1, this); //ObjModule.ShowWindow(SW_SHOW); //ObjModule.EnumModule(dwPid); *pResult = 0; } 因为我们需要显示的信息位于双击后弹出的对话框,所以应该在Module 类内位创建它 void CModule::EnumModule(DWORD pid) { // 遍历模块 HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); MODULEENTRY32 me = { sizeof(me) }; if (Module32First(hSnap, &me)) { int i = 0; do { // 插入模块的信息 m_list.InsertItem(i, me.szModule); // 转换成字符串格式 wchar_t tmp1[100] = {}; wsprintf(tmp1, L"%x", me.modBaseAddr); m_list.SetItemText(i, 1, tmp1); i++; } while (Module32Next(hSnap, &me)); } }

    总结

    根据需求添加控件(想象力)由需求,判断每个控件的功能逻辑清晰后,增加需要的代码,如 EnumFile细节: 记得初始化 listUpdateDataEnumFile 函数中,去除 ”.“ 、"…" ,防止无限递归获取文件大小(为什么要转换为wchar)

    ps:15pb学习了近两个月,从最开始的 C语言都不会,到后来的坦克大战,就是每天好累,也很充实。。。 明天写阶段小项目了,总结了作业中的小题目,发个博客纪念一下,感谢15pb老师的辛勤教学,每天辅导老师跟自习到22点,多小白的问题都给耐心的解答,感谢

    Processed: 0.010, SQL: 8