最近在实现一个遍历文件夹,并把进度同步显示在进度条上的小程序。因为对MFC还不熟悉, 所以花了几天的时间研究实现遍历文件夹与进度条的同步实现。最简单的一种进度条的实现方法是单线程的,点一下按钮进度条动一下的方式,这种实现步骤如下:
(一)、单线程进度条实现。
首先,添加Progress控件,然后给它添加变量m_pro。其次,添加两个按钮控件:开始、前进。然后,添加两个button事件。
void CDemo05Dlg::OnButton1() //开始按钮
{
// TODO: Add your control notification handler code here
m_pro.SetRange(0,100);
m_pro.SetStep(5);
m_pro.SetPos(10);
}
void CDemo05Dlg::OnButton2() //前进按钮
{
// TODO: Add your control notification handler code here
m_pro.StepIt();
}
(二)、多线程进度条实现:遍历文件夹,同时进度条显示遍历进度。
标题
遍历文件夹是一个可大可小的程序,当遍历的路径中文件个数很多时,遍历全部的文件需要耗很长时间,为了不让用户错误的认为系统进入死机的状态,我们给程序加入进度条。但是如果直接添加进度条,在程序中控制他的进度,结果进度条只向前进一步,仍然像死机状态。那该怎么办呢?原因是,我们处理比较大的计算程序时,程序和进度条显示实在同一个进程中进行的,这样程序会先处理计算程序,导致进度条无法实时更新。
解决办法就是把处理大程序的这段逻辑让一个单独的线程去处理,并利用postmessage或sendmessage给主界面发送消息,让主界面去更新进度条。
具体代码和步骤如下。
首先,文本框、按钮、静态文本框、进度条等控件,然后在cpp文件中定义一个关联事件:
#define WM_UPDATEDATA WM_USER + 1999 //定义事件
BEGIN_MESSAGE_MAP(CDemo05Dlg,CDialog)
ON_MESSAGE(WM_UPDATEDATA,&CDemo05Dlg::OnUpdateData)//让事件与程序关联
END_MESSAGE_MAP
标题
第二步,在头文件中进行函数和变量的声明
afx_msg LRESULT OnUpdateData(WPARAM wParam,LPARAM lParam);
LRESULT onUpdateData(WPARAM wParam,LPARAM lParam);
static UINT MyThread(LPVOID pParam);//必须定义为static,线程函数可以且必须是全局函数或者是静态成员函数。
CWinThread *m_pMyThread;
int ProcessLogFile(CString filepath);
void setProcess(int value);
CString filePath;
int FileSearch(CString root);
int iProRange;//统计文件个数
CString m_FilePath;//全局变量,保存最初的文件路径
CProgressCtrl m_ProgCtrl;//进度条变量
CStatic m_Num;
int iCount;//统计机器码文件
CString szNum;
CString szFilepath;//FileSearch函数中使用
标题
第三步、实现关联程序(返回类型一定要是LRESULT)
//线程处理进度条
LRESULT CDemo05Dlg::OnUpdateData(WPARAM wParam,LPARAM lParam)
{
int iTemp = (int )wParam;
m_ProgCtrl.SetPos(iTemp);//设置进度条的值
UpdateData(FALSE);//实时更新主界面
return 0;
}
标题
第四步、创建一个独立的线程处理大程序
在OnInitial函数中,对m_pMyThread初始化:
m_pMyThread = NULL;
在【遍历文件夹】按钮下,创建线程:
void CDemo05Dlg::OnBnClickedBtn()
{
// TODO: 在此添加控件通知处理程序代码
iCount=0;//在遍历函数中统计文件数,chongzhi
m_ProgCtrl.SetPos(0);//重置进度条
GetDlgItem(IDC_EDIT1)->GetWindowTextW(m_FilePath);//获取指定的文件路径
//MyThread为该大程序处理的入口函数,this为入口函数的参数
if (!m_FilePath.IsEmpty())
{
GetDlgItem(IDC_BTN)->EnableWindow(FALSE);//设置遍历按钮不可用
//统计文件个数,设进度条的range
FileSearch(m_FilePath);//统计文件个数,不是线程
m_ProgCtrl.SetRange(0,iProRange);
//创建新线程,实时更新进度条,将当前类this当做参数传递
m_pMyThread = AfxBeginThread(CDemo05Dlg::MyThread, this);
GetDlgItem(IDC_BTN)->EnableWindow(TRUE);
}
else
{
AfxMessageBox(L"请选择路径!");
}
}
统计文件个数函数FileSearch(m_FilePath),如下:
int CDemo05Dlg::FileSearch(CString root)
{
CFileFind fileFinder;
if (root.Right(1)!="/")
{
root+="/";
}
root+="*.*";
BOOL res=fileFinder.FindFile(root);//遍历目录下的文件、文件夹
while (res)
{
res=fileFinder.FindNextFile();
szFilepath=fileFinder.GetFilePath();
if (fileFinder.IsDirectory() && !fileFinder.IsDots())// 找到的是文件夹
{
FileSearch(szFilepath);// 递归
//AfxMessageBox(L"遍历文件夹");
}
else if (!fileFinder.IsDirectory() && !fileFinder.IsDots())// 找到的是文件
{
//获取文件类型
CString fileName = fileFinder.GetFileName();
int dotPos=fileName.ReverseFind('.');
CString fileExt=fileName.Right(fileName.GetLength()-dotPos);
if(fileExt == _T(".txt")) //若是txt文件
{
CString szFileName = fileName.Left(fileName.GetLength()-4); //获取文件名
if (szFileName == L"MyTxt")
{
iProRange ++;//统计MyTxt文件个数
}
}
}
}
return iProRange;
}
标题
第五步、实现遍历文件夹逻辑
UINT CDemo05Dlg::MyThread(LPVOID pParam)//注意返回类型为UINT
{
CDemo05Dlg *p=(CDemo05Dlg*)pParam; //获取传参(this)的值,指当前的类,
p->ProcessLogFile(p->m_FilePath);//调用生成函数,静态成员函数不可调用非静态变量,通过p间接调用。
p->szNum.Format(L"%d",p->iCount);
p->m_Num.SetWindowTextW(L"共 "+p->szNum+L"个文件");
return 0;
}
标题
第六步、向主线程发送消息让进度条实时更新
int CRealPipeSoftWareKeyVenderBatch::ProcessLogFile(CString root)
{
CFileFind fileFinder;
if (root.Right(1)!="/")
{
root+="/";
}
root+="*.*";
BOOL res=fileFinder.FindFile(root);//遍历目录下的文件、文件夹
while (res)
{
setProcess(iCount);//调用进度条实时更新函数实时更新进度条
res=fileFinder.FindNextFile();
filePath=fileFinder.GetFilePath();
if (fileFinder.IsDirectory() && !fileFinder.IsDots())// 找到的是文件夹
{
ProcessLogFile(filePath);// 递归
//AfxMessageBox(L"遍历文件夹");
}
else if (!fileFinder.IsDirectory() && !fileFinder.IsDots())// 找到的是文件
{
//获取文件类型
CString fileName = fileFinder.GetFileName();
int dotPos=fileName.ReverseFind('.');
CString fileExt=fileName.Right(fileName.GetLength()-dotPos);
if(fileExt == _T(".txt")) //若是txt文件
{
CString szFileName = fileName.Left(fileName.GetLength()-4); //获取文件名
if (szFileName == L"MyTxt")
{
iCount ++;//统计MyTxt文件个数
szNum.Format(L"%d",iCount);
m_Num.SetWindowTextW(L"第"+szNum+L"个");
。
。
。
//此处可进行一些其他的,对找到的文件的操作
。
。
。
}
}
}
}
}
最后,进度条更新函数
void CDemo05Dlg::setProcess(int value)
{
//向主线程发送消息更新进度条
SendMessage(WM_UPDATEDATA, value);
}
好了,运行程序看结果吧。