基本界面如下图所示:
①选择开始查找的按钮:将Default Button设为True(即设为软件界面上默认按回车键响应的按钮)
②如果**要在线程函数中传递多个参数,**那么我们可以在主对话框的.h文件中声明一个类,生成一个对象进行传递即可。
③利用原子互锁家族解决全局变量更新值的问题。
④利用消息响应函数解决工作线程操作主界面的问题。
设置定时器:这里我们要改变图标对应的灰和亮,需要用到定时器
类视图->主对话框Dlg->右侧属性->消息->WM_TIMER->选择添加
同时在OnInitDialog函数中进行启动定时器操作。
主对话框.h文件:
// 文件查找器Dlg.h : 头文件
//
#pragma once
#include "afxcmn.h"
#define WM_ADD_ITEM (WM_USER+110)//自定义消息号(名字可随便改)===第一步
class CFindItem
{
public:
CString m_PathFind;//要搜索的文件路径
CString m_FName;//要搜索的文件名
HWND m_hMainDlg;//主窗口的窗口句柄
CFindItem()
{
m_PathFind.Empty();//把容器置空
m_FName.Empty();
m_hMainDlg = NULL;
}
CFindItem(CFindItem &tItem)
{
m_PathFind = tItem.m_PathFind;//本对象的pathfind等于传递进来的pathfind
m_FName = tItem.m_FName;
m_hMainDlg = tItem.m_hMainDlg;
}
};
// C文件查找器Dlg 对话框
class C文件查找器Dlg : public CDialog
{
// 构造
public:
C文件查找器Dlg(CWnd* pParent = NULL); // 标准构造函数
// 对话框数据
enum { IDD = IDD_MY_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedBrowseBtn();
afx_msg void OnBnClickedStartBtn();
CListCtrl m_ResultList;
afx_msg void OnLvnItemchangedResultList(NMHDR *pNMHDR, LRESULT *pResult);
afx_msg LRESULT OnAddItem(WPARAM wParam,LPARAM lParam);//消息响应函数的声明,返回值为LRESULT===第二步
afx_msg void OnBnClickedStopBtn();
afx_msg void OnTimer(UINT_PTR nIDEvent);
};
主对话框.cpp文件:
// 文件查找器Dlg.cpp : 实现文件
//
#include "stdafx.h"
#include "文件查找器.h"
#include "文件查找器Dlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
LONG g_FindCount;//查找到的文件个数
LONG g_ThreadNum;
BOOL g_bFinding = FALSE;//当前运行的状态,默认为FALSE
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// 对话框数据
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
// C文件查找器Dlg 对话框
C文件查找器Dlg::C文件查找器Dlg(CWnd* pParent /*=NULL*/)
: CDialog(C文件查找器Dlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void C文件查找器Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_RESULT_LIST, m_ResultList);
}
BEGIN_MESSAGE_MAP(C文件查找器Dlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_BN_CLICKED(IDC_BROWSE_BTN, &C文件查找器Dlg::OnBnClickedBrowseBtn)
ON_BN_CLICKED(IDC_START_BTN, &C文件查找器Dlg::OnBnClickedStartBtn)
ON_NOTIFY(LVN_ITEMCHANGED, IDC_RESULT_LIST, &C文件查找器Dlg::OnLvnItemchangedResultList)
ON_MESSAGE(WM_ADD_ITEM,&C文件查找器Dlg::OnAddItem)
ON_BN_CLICKED(IDC_STOP_BTN, &C文件查找器Dlg::OnBnClickedStopBtn)
ON_WM_TIMER()
END_MESSAGE_MAP()
// C文件查找器Dlg 消息处理程序
BOOL C文件查找器Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
m_ResultList.SetExtendedStyle(m_ResultList.GetExtendedStyle() | LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT);//设置列表控件扩展样式:网格+单行选择,把以前的样式获取了,保证以前的样式存在,且进行按位或
m_ResultList.InsertColumn(0,_T("查找结果"),LVCFMT_LEFT,450);//向列表控件中插入一列,参数:0代表插一列,插入列的字符串,左对齐,宽度450
SetTimer(1,100,NULL);//启动定时器
#ifdef _DEBUG//如果定义DEBUG的宏,则说明当前的模式为调试模式,工程为relase模式的话它两不会走
SetDlgItemText(IDC_PATH_EDIT,_T("E:\\opencv\\build"));//设置根目录
SetDlgItemText(IDC_FNAME_EDIT,_T("StdAfx.h"));//设置文件名
#endif // _DEBUG
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void C文件查找器Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void C文件查找器Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR C文件查找器Dlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void C文件查找器Dlg::OnBnClickedBrowseBtn()
{
// TODO: 在此添加控件通知处理程序代码
TCHAR szPath[MAX_PATH] = {0};//定义缓冲区,MAX_PATH是Windows系统所支持的最长文件名的长度,TCHER 类型字符串有自适应编码的能力,转成计算机目前用的编码格式
BROWSEINFO mBroInfo = {0};//创建浏览文件夹对话框
mBroInfo.hwndOwner = m_hWnd;//父窗口句柄,父窗口是主对话框
ITEMIDLIST *pidl = SHBrowseForFolder(&mBroInfo);//该函数返回一个ITEMIDLIST结构的指针,ITEMIDLIST是一个结构,指明了默认浏览的根文件夹的位置
//需要注意的是,SHBrowseForFolder函数要求调用程序负责删除这个指针。
if(SHGetPathFromIDList(pidl,szPath))//取得文件夹路径到szPath里,这个函数將对象路径转化为文件夹路径
{
SetDlgItemText(IDC_PATH_EDIT,szPath);//将指定的文本送到控件中
}
CoTaskMemFree(pidl);//释放ITEMIDLIST指针
}
UINT __cdecl FindProc(LPVOID pParam)//在哪一个文件夹里查找,要知道查找的文件名是什么,一点查到结果了应该告诉谁
//因为要传递三个参数 而线程函数默认只有一个参数的位置参与传递 所以在.h文件中声明一个类
{
InterlockedIncrement(&g_ThreadNum);//每进入一次线程,执行+1操作
CFindItem *pItem = (CFindItem *)pParam;//对参数进行强制类型转换
CString strRootPath = pItem->m_PathFind;//把搜索的文件路径拿出来
if(strRootPath[strRootPath.GetLength()-1]!='\\')//用中括号取最后一个字符,看是否为反斜杠
strRootPath = strRootPath + _T("\\");//没有就给加上
CFileFind mFinder;//查找要用的类
BOOL bFind = mFinder.FindFile(strRootPath+_T("*.*"));//代表任意类型文件的查找,返回非0则表示执行成功
while(bFind && g_bFinding)//同时为True再进行查找
{
bFind = mFinder.FindNextFile();//找下一个文件 该函数返回值非0 还有符合条件的文件, 0表示是最后一个文件。
if(mFinder.IsDots())//该函数用来判断查找的文件属性是否是“.”文件,还是“..”文件夹,只要有一个就是1,0表示不是
{
continue;
}
else if (mFinder.IsDirectory())//如果开启的是目录 该函数用来判断查找的文件属性是否是路径文件,非0表示是,0表示不是。
{
//开启多线程
CFindItem *pSubItem = new CFindItem(*pItem);//吧pItem的值赋给*pSubItem
pSubItem->m_PathFind = mFinder.GetFilePath();//目录换一下 是新枚举到的目录
AfxBeginThread(FindProc,pSubItem);
}
else
{
//找到的是文件,进行对比
CString strFileName = mFinder.GetFileName();//该函数用来获得查找到的文件的全称 如“MFC.rar”
if(strFileName.CompareNoCase(pItem->m_FName)==0)//不区分大小写的与原查找文件进行比较
{
CString strPath = mFinder.GetFilePath();//获取文件的路径
// PostMessage(pItem->m_hMainDlg,WM_ADD_ITEM,0,(LPARAM)(LPCTSTR)strPath);//发送给对话框的窗口句柄/发送的消息号 找到的文件路径放到lParam当中 强转两次否则报错
//↑如果发送成功了就会调用additem这个消息响应函数
//PostMessage是不管处不处理就会返回,而sendMessage必须要等消息处理再返回 strPath是个局部变量,发送完管不管接受就会被释放所以应该为SendMessage
SendMessage(pItem->m_hMainDlg,WM_ADD_ITEM,0,(LPARAM)(LPCTSTR)strPath);
InterlockedIncrement(&g_FindCount);//每找到一个结果就加1
}
//线程函数不是类的成员函数,在工作线程中不建议在线程中直接操作界面,可以给界面发消息,让界面处理这个消息
}
}
delete pItem;//释放相应函数中的申请内存
InterlockedDecrement(&g_ThreadNum);//从线程函数中退出了,执行-1操作
return 0;
}
void C文件查找器Dlg::OnBnClickedStartBtn()//每遇到一个目录,就开启一个线程
{
//按按钮的时候就把查找/线程个数清零
g_bFinding = TRUE;//将当前状态改为TRUE
g_FindCount = 0;
g_ThreadNum = 0;
// TODO: 在此添加控件通知处理程序代码
//CFindItem item;//作用域仅存在按钮的响应函数当中,但AfxBeginThread创建一个线程马上返回,代码继续向下走,但此时item 的生命周期已经结束
//GetDlgItemText(IDC_PATH_EDIT,item.m_PathFind);//获取文件路径,放在对象的成员里
//GetDlgItemText(IDC_FNAME_EDIT,item.m_FName);
//AfxBeginThread(FindProc,&item);
//解决方案:在堆中申请一个内存,即使超过了大括号的作用域,堆中的内存也不会被释放
CFindItem *pItem = new CFindItem();//小括号别忘了,为了调用构造函数
GetDlgItemText(IDC_PATH_EDIT,pItem->m_PathFind);//获取文件路径,放在对象的成员里
GetDlgItemText(IDC_FNAME_EDIT,pItem->m_FName);
pItem->m_hMainDlg = GetSafeHwnd();//把当前主对话框的窗口句柄传过去,这样就可以在线程函数中使用
m_ResultList.DeleteAllItems();//清空上一次执行完之后的信息
AfxBeginThread(FindProc,pItem);//线程函数和点击响应函数是异步的
}
void C文件查找器Dlg::OnLvnItemchangedResultList(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
*pResult = 0;
}
LRESULT C文件查找器Dlg::OnAddItem(WPARAM wParam,LPARAM lParam)//消息响应函数的实现(afx_msg 就不用加了)上面还要在Begin message map关联起来
{
CString strPath = (LPCTSTR)lParam;//把找到文件路径获取到
m_ResultList.InsertItem(m_ResultList.GetItemCount(),strPath);//索引:获取行的个数/插入进去
return TRUE;
}
void C文件查找器Dlg::OnBnClickedStopBtn()
{
// TODO: 在此添加控件通知处理程序代码
g_bFinding = FALSE;
}
void C文件查找器Dlg::OnTimer(UINT_PTR nIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (g_ThreadNum > 0)//说明当前正在查找
{
GetDlgItem(IDC_PATH_EDIT)->EnableWindow(FALSE);//查找的过程中这几个按钮变灰
GetDlgItem(IDC_FNAME_EDIT)->EnableWindow(FALSE);
GetDlgItem(IDC_START_BTN)->EnableWindow(FALSE);
GetDlgItem(IDC_BROWSE_BTN)->EnableWindow(FALSE);
GetDlgItem(IDC_STOP_BTN)->EnableWindow(TRUE);//停止按钮要为TRUE
}
else//说明现在没有在运行 则把这几个按钮反回来
{
GetDlgItem(IDC_PATH_EDIT)->EnableWindow(TRUE);//查找的过程中这几个按钮变灰
GetDlgItem(IDC_FNAME_EDIT)->EnableWindow(TRUE);
GetDlgItem(IDC_START_BTN)->EnableWindow(TRUE);
GetDlgItem(IDC_BROWSE_BTN)->EnableWindow(TRUE);
GetDlgItem(IDC_STOP_BTN)->EnableWindow(FALSE);
}
CDialog::OnTimer(nIDEvent);
}
程序运行结果: