多线程开发实例:文件查找器 全部原码附带详细注释。

基本界面如下图所示:
①选择开始查找的按钮:将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);
}

程序运行结果:

在这里插入图片描述
在这里插入图片描述

©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页