本节将以前面封装的CFileCutter类为基础,介绍完整文件切割器的开发过程。这个程序可以将任意大小的文件分割成用户指定的大小,又可以再次将它们合并回来。

这个程序要实现两个功能,一个是分割大文件为小文件,另一个是合并小文件成大文件。用户可以通过选项分组框的两个单项按钮指定请求的操作。图8.7所示为用户做出不同选择时程序界面的变化。

分割文件时,第一个编辑控件要求用户输入待分割的文件,第二个编辑控件要求用户输入分割后要保存到的目标文件夹。选项分组框中的组合框要求用户选择分割后文件的大小。用户提供完这些参数以后单击分割按钮,程序就会通知CFileCutter类的工作线程,开始分割文件。

合并文件时,第一个编辑控件要求用户输入待合并的文件所在的文件夹,第二个编辑控件要求用户输入合并后要保存到的文件夹。这两项确定以后,单击合并按钮程序就开始工作了。不过,这里所谓的合并文件是指将上面分割的文件恢复成一个文件,并不能做其他事情。

既然有了前面的CFileCutter类作为基础,本节要完成的不过是界面设计而已。各子窗口控件的ID号可以从图8.7左边的窗口上看出。

此文件切割程序的关键代码如下。

// Cutter.h文件
#include <afxwin.h>
#include <afxcmn.h>    // 为了使用CStatusBarCtrl类
#include "FileCutter.h" // 为了使用自定义类CFileCutter class CMyApp : public CWinApp
{public:BOOL InitInstance();
};class CMainDialog : public CDialog
{public:CMainDialog(CWnd* pParentWnd = NULL);protected:// 进度条控件对象CProgressCtrl m_Progress;CFileCutter* m_pCutter;// 动态控制程序界面的函数void UIControl();
protected:virtual BOOL OnInitDialog();virtual void OnCancel();afx_msg void OnSourceBrowser();afx_msg void OnDestBrowser();afx_msg void OnStart();afx_msg void OnStop();afx_msg void OnSelect();// 处理CFileCutter类发来的消息afx_msg long OnCutterStart(WPARAM wParam, LPARAM);afx_msg long OnCutterStatus(WPARAM wParam, LPARAM lParam);afx_msg long OnCutterStop(WPARAM wParam, LPARAM lParam);DECLARE_MESSAGE_MAP()
};
// MainDir.cpp文件
#include <afxdlgs.h> // 为了使用CFileDialog类
#include "DirDialog.h"
#include "resource.h"
#include "Cutter.h"CMyApp theApp;BOOL CMyApp::InitInstance()
{CMainDialog dlg;m_pMainWnd = &dlg;dlg.DoModal();return FALSE;
}CMainDialog::CMainDialog(CWnd* pParentWnd):CDialog(IDD_FILECUTTER_DIALOG, pParentWnd)
{}BEGIN_MESSAGE_MAP(CMainDialog, CDialog)
ON_BN_CLICKED(IDC_SOURCEBROWSER, OnSourceBrowser)   // 选择源文件的“选择”按钮
ON_BN_CLICKED(IDC_DESTBROWSER, OnDestBrowser)       // 选择目标文件的“选择”按钮
ON_BN_CLICKED(IDC_START, OnStart)           // 开始“分割”按钮
ON_BN_CLICKED(IDC_STOP, OnStop)             // “终止”分割按钮
ON_BN_CLICKED(IDC_SELECTSPLIT, OnSelect)        // 分割单选框按钮
ON_BN_CLICKED(IDC_SELECTMERGE, OnSelect)        // 合并单选框按钮
// 下面是3各CFileCutter类发来的消息
ON_MESSAGE(WM_CUTTERSTART, OnCutterStart)
ON_MESSAGE(WM_CUTTERSTATUS, OnCutterStatus)
ON_MESSAGE(WM_CUTTERSTOP, OnCutterStop)
END_MESSAGE_MAP()BOOL CMainDialog::OnInitDialog()
{CDialog::OnInitDialog();SetIcon(theApp.LoadIcon(IDI_MAIN), FALSE);// 创建CFileCutter对象m_pCutter = new CFileCutter(m_hWnd);// 默认选中分割单项框((CButton*)GetDlgItem(IDC_SELECTSPLIT))->SetCheck(1);// 初始化单位选择组合框。可以在这里继续添加其它项((CComboBox*)GetDlgItem(IDC_UNIT))->AddString("1");((CComboBox*)GetDlgItem(IDC_UNIT))->AddString("30");((CComboBox*)GetDlgItem(IDC_UNIT))->AddString("60");((CComboBox*)GetDlgItem(IDC_UNIT))->SetCurSel(0);// 子类化进度条控件。也就是让m_Progress对象取得进度条控件的控制权m_Progress.SubclassWindow(*GetDlgItem(IDC_PROGRESS));UIControl();return TRUE;
}void CMainDialog::UIControl()
{BOOL bIsWorking = m_pCutter->IsRunning();// 设置选项分组框中3个控件的状态GetDlgItem(IDC_SELECTSPLIT)->EnableWindow(!bIsWorking);GetDlgItem(IDC_SELECTMERGE)->EnableWindow(!bIsWorking);GetDlgItem(IDC_UNIT)->EnableWindow(!bIsWorking);// 设置分割、终止两个按钮的状态GetDlgItem(IDC_START)->EnableWindow(!bIsWorking);GetDlgItem(IDC_STOP)->EnableWindow(bIsWorking);if(bIsWorking){return;}// 根据用户的选择设置不同的文本BOOL bSplit = ((CButton*)GetDlgItem(IDC_SELECTSPLIT))->GetCheck();if(bSplit)  // 请求分割 {GetDlgItem(IDC_START)->SetWindowText("分割");GetDlgItem(IDC_SOURCETITLE)->SetWindowText("请选择要分割的文件:");GetDlgItem(IDC_DESTTITLE)->SetWindowText("请选择分割后保存到的文件夹:");GetDlgItem(IDC_UNIT)->EnableWindow(TRUE);}else      // 请求合并{GetDlgItem(IDC_START)->SetWindowText("合并");GetDlgItem(IDC_SOURCETITLE)->SetWindowText("请选择待合并文件的文件夹:");GetDlgItem(IDC_DESTTITLE)->SetWindowText("请选择合并后保存到的文件夹:");GetDlgItem(IDC_UNIT)->EnableWindow(FALSE);}// 初始化状态信息GetDlgItem(IDC_STATUSTEXT)->SetWindowText("    状态显示:");m_Progress.SetPos(0);}void CMainDialog::OnCancel()
{// 是否真的退出?BOOL bExit = TRUE;if(m_pCutter->IsRunning()){if(MessageBox("工作还未完成,确实要退出吗?", NULL, MB_YESNO) == IDNO){bExit = FALSE;}}if(bExit){delete m_pCutter;CDialog::OnCancel();}
}void CMainDialog::OnSelect()
{UIControl();
}void CMainDialog::OnSourceBrowser()
{BOOL bSplit = ((CButton*)GetDlgItem(IDC_SELECTSPLIT))->GetCheck();if(bSplit)   // 请求分割{CFileDialog sourceFile(TRUE);// 显示选择文件对话框if(sourceFile.DoModal() == IDOK){GetDlgItem(IDC_EDITSOURCE)->SetWindowText(sourceFile.GetPathName());// 设置默认目录// 例如,如果用户选择文件“D:\cd\精选歌曲.iso”,那么“D:\cd\精选歌曲”将会被设置为默认目录CString strDef = sourceFile.GetPathName();strDef = strDef.Left(strDef.ReverseFind('.'));GetDlgItem(IDC_EDITDEST)->SetWindowText(strDef);}}else      // 请求合并{CDirDialog sourceFolder;// 显示选择目录对话框if(sourceFolder.DoBrowse(*this) == IDOK){GetDlgItem(IDC_EDITSOURCE)->SetWindowText(sourceFolder.GetPath());// 设置默认目录// 例如,如果用户选择目录“D:\cd”,那么“D:\cd\cd”将会被设置为默认目录CString strDef = sourceFolder.GetPath();strDef.TrimRight('\\');strDef = strDef + '\\' + strDef.Mid(strDef.ReverseFind('\\') + 1);// 防止用户选择根目录strDef.TrimRight(':');GetDlgItem(IDC_EDITDEST)->SetWindowText(strDef);}}
}void CMainDialog::OnDestBrowser()
{CDirDialog destFolder;// 显示选择目录对话框if(destFolder.DoBrowse(*this) == IDOK){GetDlgItem(IDC_EDITDEST)->SetWindowText(destFolder.GetPath());}
}void CMainDialog::OnStart()
{CString strSource, strDest;// 检查输入GetDlgItem(IDC_EDITSOURCE)->GetWindowText(strSource);GetDlgItem(IDC_EDITDEST)->GetWindowText(strDest);if(strSource.IsEmpty() || strDest.IsEmpty()){MessageBox("文件或路径名称不能为空");return;}BOOL bSplit = ((CButton*)GetDlgItem(IDC_SELECTSPLIT))->GetCheck();if(bSplit)    // 请求分割{CString str;GetDlgItem(IDC_UNIT)->GetWindowText(str);m_pCutter->StartSplit(strDest, strSource, atoi(str)*1024*1024);}else     // 请求合并{m_pCutter->StartMerge(strDest, strSource);}
}void CMainDialog::OnStop()
{m_pCutter->SuspendCutter();if(MessageBox("确实要终止吗?", NULL, MB_YESNO) == IDYES){m_pCutter->StopCutter();}else{m_pCutter->ResumeCutter();}
}
// 下面的代码处理CFileCutter类发来的消息
long CMainDialog::OnCutterStart(WPARAM wParam, LPARAM)      // WM_CUTTERSTART 开始工作
{// 设置进度条范围int nTotalFiles = wParam;   // 总文件数量m_Progress.SetRange(0, nTotalFiles);UIControl();return 0;
}long CMainDialog::OnCutterStatus(WPARAM wParam, LPARAM lParam) // WM_CUTTERSTATUS 工作进度
{// 设置进度条进度int nCompleted = (int)lParam;m_Progress.SetPos(nCompleted);// 显示状态CString s;s.Format(" 完成%d个文件", nCompleted);GetDlgItem(IDC_STATUSTEXT)->SetWindowText(s);return 0;
}long CMainDialog::OnCutterStop(WPARAM wParam, LPARAM lParam)   // WM_CUTTERSTOP 停止工作
{int nErrorCode = wParam;switch(nErrorCode){case CFileCutter::exitSuccess:MessageBox("操作成功完成", "成功");break;case CFileCutter::exitSourceErr:MessageBox("源文件出错", "失败");break;case CFileCutter::exitDestErr:MessageBox("目标文件出错", "失败");break;case CFileCutter::exitUserForce:MessageBox("用户终止", "失败");break;}UIControl();return 0;
}

1.接受自定义消息
程序必须接受CFileCutter类发来的消息。一种方法是直接重载CWnd的成员函数OnWndMsg,另一种方法是使用ON_MESSAGE宏。在MFC中它的定义如下。

#define ON_MESSAGE(message, memberFxn) \
{ message, 0, 0, 0, AfxSig_lwl, \  (AFX_PMSG)(AFX_PMSGW)(LRESULT (CWnd::*)(WPARAM, LPARAM))&memberFxn
},

此宏的数字签名说明消息处理函数要有类型为WPARAM和LPARAM的两个参数,返回值的类型为long。例如,处理WM_CUTTERSTART的函数是OnCutterStart,

afx_msg long OnCutterStart(WPARAM wParam, LPARAM);

要在消息映射表中添加消息映射项:

ON_MESSAGE(WM_CUTTERSTART, OnCutterStart)

2.子类化控件窗口
MFC中管理进度条控件的类是CProgressCtrl。程序申请了一个CProgressCtrl类的对象m_Progress,在OnInitDialog函数中调用CWnd:: SubclassWindow函数将此对象与对话框中的进度条控件关联了起来。

m_Progress.SubclassWindow(*GetDlgItem(IDC_PROGRESS));

SubclassWindow函数的作用是子类化指定的窗口,下面是它的伪实现代码。

BOOL CWnd::SubclassWindow(HWND hWnd)
{ if (!Attach(hWnd))  return FALSE;  // 允许执行其他子类化代码  PreSubclassWindow();  // 设置它的消息处理函数地址为AfxWndProc,保存原来的函数地址,以便对消息做默认处理  WNDPROC* lplpfn = GetSuperWndProcAddr();  WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC,  (DWORD)AfxGetAfxWndProc());  ASSERT(oldWndProc != (WNDPROC)AfxGetAfxWndProc());  if (*lplpfn == NULL)  *lplpfn = oldWndProc;  return TRUE;
}

函数做了两件事情,第一是使这个窗口的窗口句柄与当前CWnd对象关联起来,第二是设置它的消息处理函数地址为AfxWndProc,以取得消息处理权。

3.CProgressCtrl类
CProgressCtrl类封装了对进度条控件的操作。SetRange函数设置进度条的范围,SetPos函数设置当前进度,SetStep设置步长等。

8.6 【实例】文件切割器开发实例相关推荐

  1. eclipse java开发实例_eclipse+webservice开发实例

    1.參考文献: 2.实例1(主要看到[2]) 2.1.系统功能: 开发一个计算器服务CalculateService,这个服务包括加(plus).减(minus).乘(multiply).除(divi ...

  2. 基于angularjs的单页面实例_angularjs网站开发实例

    客户端 JavaScript 的 5 个弊端 译注:原来的标题是:"我们为啥不用AngularJS:-",后来作者觉得不妥就改掉了,因为AngularJS是通常适用于单页面程序框架 ...

  3. ewb文件用multisim打开_MULTISIM(EWB)电路仿真实例文件打包

    [实例简介] MULTISIM(EWB)电路仿真实例文件打包 [实例截图] [核心代码] 1867ea12-47ef-4a0e-9b93-eb6c54f35937 ├── Multisim9文件 │  ...

  4. php实训ppt,PHP动态网站开发实例教程教学全套课件(刘万辉)配套资料 任务3:运用目录与文件实现投票统计.ppt...

    PHP动态网站开发实例教程教学全套课件(刘万辉)配套资料 任务3:运用目录与文件实现投票统计.ppt (18页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! ...

  5. android打印doc文件,安卓系统APP打印开发实例.doc

    S&PS Solution Tips PAGE 5 | Page 主题 安卓系统APP打印开发实例 适用范围 带网口Honeywell系列 intermec系列 解决方案 当你想要在安卓DPA ...

  6. JBOSS+EJB3之Entity 开发实例

    我用的是 mysql 数据库, EJB3.0 相对于2.0 变化蛮大的,真的是迫于 Spring+Hibernate 的逼式,ejb 3.0 已经出现几年了,自从她轻装上阵,也不知道现在的应用情况如何 ...

  7. 《Unity 3.x游戏开发实例》一1.5 欢迎来到Unity 3D

    本节书摘来异步社区<Unity 3.x游戏开发实例>一书中的第1章,第1.5节,作者: [加]Ryan Henson Creighton 译者: 师蓉 责编: 陈冀康,更多章节内容可以访问 ...

  8. AgileEAS.NET平台开发实例-药店系统-准备开发环境(上)

    开篇 上一篇我们主要是讲述了如何根据数据库原型设计器提供的相关功能来构建ORM提供的相关信息,例如根据数据库与实体对象之间通过数据原型来进行双向的映射.本篇 我们将会根据上篇<AgileEAS. ...

  9. 安卓音乐播放器开发实例

    本文将引导大家做一个音乐播放器,在做这个Android开发实例的过程中,能够帮助大家进一步熟悉和掌握学过的ListView和其他一些组件.为了有更好的学习效果,其中很多功能我们手动实现,例如音乐播放的 ...

最新文章

  1. java sip 携带sdp_SIP中的SDP offer/answer交换初探
  2. Orchard模块开发全接触3:分类的实现及内容呈现(Display)
  3. 【错误记录】PyCharm 运行 Python 程序报错 ( PEP 8: E305 expected 2 blank lines after class or function definiti )
  4. linux学习比较好的网站,推荐一个好的在线学习Linux/Unix的好网站
  5. Python基础——Anaconda的安装使用
  6. Octave添加搜索路径
  7. Ubuntu LAMP
  8. asp.net Session在web.config中的三种配置方式——详解
  9. 初探react,用react实现一个todoList功能
  10. 变压器的同名端,以及判别方法
  11. 功能测试-测试定义与原则
  12. 五眼网络安全机构联合发布事件响应指南
  13. nodejs代码将word转成图片
  14. No symbols loaded
  15. 选取域名需要注意的几个方面
  16. 郁闷湖上被宰的文章: 仰恩大学评估+废CET !
  17. 华为手机计算机如何表达平方,华为手机郑平方:荣耀成功源于共享质量体系
  18. pycharm快速注释快捷键
  19. office文件自动保存
  20. 基于STM32F103C8T6的循迹避障小车完整制作过程(详细)----上篇(第123点)

热门文章

  1. 解决windows 10无法打开.hlp帮助文件的问题
  2. React实现优雅的弹窗
  3. 原谅帽大作战网页版服务器连接失败,原谅帽大作战网页PC版
  4. 基于TP3.2的发送邮箱做找回密码
  5. 星淘惠:亚马逊新品上新推广方法,全知道
  6. 解决element-UI改变分页时,表格序号总是从1开始的问题.
  7. 百无聊赖之JavaEE从入门到放弃(十一)string类 常量池原理 内部类
  8. WebGL1.0 常用API及参数
  9. ARP攻击和防御实验
  10. 计算机主机内部包括什么,电脑主机的内部构造是什么?