// MainDir.h文件 // 07MainDir
#include <afxwin.h>
#include <afxcmn.h> // 为了使用CStatusBarCtrl类
#include "FileCutter.h"
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()
};
///
// DirDialog.h文件
#ifndef __DIRDIALOG_H_
#define __DIRDIALOG_H_
#include <shlobj.h>
class CDirDialog
{
public:
CDirDialog();
// 显示对话框
BOOL DoBrowse(HWND hWndParent, LPCTSTR pszTitle = NULL);
// 取得用户选择的目录名称
LPCTSTR GetPath() { return m_szPath; }
protected:
BROWSEINFOA m_bi;
// 用来接受用户选择目录的缓冲区
char m_szDisplay[MAX_PATH];
char m_szPath[MAX_PATH];
};
CDirDialog::CDirDialog()
{
memset(&m_bi, 0, sizeof(m_bi));
m_bi.hwndOwner = NULL;
m_bi.pidlRoot = NULL;
m_bi.pszDisplayName = m_szDisplay;
m_bi.lpszTitle = NULL;
m_bi.ulFlags = BIF_RETURNONLYFSDIRS;
m_szPath[0] = '\0';
}
BOOL CDirDialog::DoBrowse(HWND hWndParent, LPCTSTR pszTitle)
{
if(pszTitle == NULL)
m_bi.lpszTitle = "选择目标文件夹";
else
m_bi.lpszTitle = pszTitle;
m_bi.hwndOwner = hWndParent;
LPITEMIDLIST pItem = ::SHBrowseForFolder(&m_bi);
if(pItem != 0)
{
::SHGetPathFromIDList(pItem, m_szPath);
return TRUE;
}
return FALSE;
}
#endif //__DIRDIALOG_H_
// FileCutter.h文件
#ifndef __FILECUTTER_H_
#define __FILECUTTER_H_
#include <afxwin.h>
// CFileCutter类发给主窗口的通知消息
#define WM_CUTTERSTART WM_USER + 100 // wParam == nTatolCount
#define WM_CUTTERSTOP WM_USER + 101 // wParam == nExitCode, lParam == nCompletedCount
#define WM_CUTTERSTATUS WM_USER + 102 //lParam == nCompletedCount
class CFileCutter
{
public:
// 工作退出代码
enum ExitCode{
exitSuccess, // 成功完成任务
exitUserForce, // 用户终止
exitSourceErr, // 源文件出错
exitDestErr // 目标文件出错
};
// 构造函数
CFileCutter(HWND hWndNotify);
// 属性
BOOL IsRunning() const { return m_bRunning; }
// 操作
BOOL StartSplit(LPCTSTR lpszDestDir, LPCTSTR lpszSourceFile, UINT uFileSize);
BOOL StartMerge(LPCTSTR lpszDestFile, LPCTSTR lpszSourceDir);
BOOL SuspendCutter();
BOOL ResumeCutter();
void StopCutter();
// 具体实现
public:
~CFileCutter();
protected:
// 重置参数信息和状态标志
void Reset();
// 进行真正的分割操作
void DoSplit();
// 进行真正的合并操作
void DoMerge();
// 工作线程
UINT friend _CutterEntry(LPVOID lpParam);
// 参数信息
CString m_strSource;
CString m_strDest;
UINT m_uFileSize;
BOOL m_bSplit;
// 状态标志
BOOL m_bContinue;// 是否继续工作
BOOL m_bRunning;// 是否处于工作状态
// 同步以上两组数据
CRITICAL_SECTION m_cs;// Data gard
private:
// 对象的生命周期全局有效的数据
HWND m_hWndNotify;// 接受消息通知事件的窗口句柄
HANDLE m_hWorkEvent;// 通知开始工作的事件对象句柄
CWinThread* m_pThread;// 工作线程
BOOL m_bSuspend;// 暂停标志
BOOL m_bExitThread;// 退出标志
};
#endif // __FILECUTTER_H_
// 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;
}
/
// FileCutter.cpp文件
#include "FileCutter.h"
// 内部工作线程
UINT _CutterEntry(LPVOID lpParam)
{
// 得到CFileCutter对象的指针
CFileCutter* pCutter = (CFileCutter*)lpParam;
// 循环处理用户的工作请求
while(::WaitForSingleObject(pCutter->m_hWorkEvent, INFINITE) == WAIT_OBJECT_0 &&
!pCutter->m_bExitThread)
{
// 设置状态标志,说明正在工作
::EnterCriticalSection(&pCutter->m_cs);
pCutter->m_bRunning = TRUE;
::LeaveCriticalSection(&pCutter->m_cs);
// 开始真正的工作
if(pCutter->m_bSplit)
pCutter->DoSplit();
else
pCutter->DoMerge();
// 准备接受新的工作任务
pCutter->Reset();
}
return 0;
}
//-----------------------接口成员------------------------//
CFileCutter::CFileCutter(HWND hWndNotify)
{
// 初始化全局有效变量
m_hWndNotify = hWndNotify;
m_bExitThread = FALSE;
m_bSuspend = FALSE;
// 创建等待事件对象
m_hWorkEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
// 创建工作线程
m_pThread = AfxBeginThread(_CutterEntry, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL);
m_pThread->m_bAutoDelete = FALSE;
m_pThread->ResumeThread();
// 初始化工作期间有效变量
// 创建关键代码段
::InitializeCriticalSection(&m_cs);
Reset();
}
void CFileCutter::Reset()
{
::EnterCriticalSection(&m_cs);
// 重置参数信息
m_strSource.Empty();
m_strDest.Empty();
m_uFileSize = 0;
m_bSplit = TRUE;
// 重置状态标志
m_bContinue = TRUE;
m_bRunning = FALSE;
::LeaveCriticalSection(&m_cs);
}
CFileCutter::~CFileCutter()
{
// 设置结束标志
m_bExitThread = TRUE;
// 设置强制退出标志
::EnterCriticalSection(&m_cs);
m_bContinue = FALSE;
::LeaveCriticalSection(&m_cs);
// 防止线程在m_hWorkEvent事件上等待
::SetEvent(m_hWorkEvent);
// 确保工作线程结束
::WaitForSingleObject(m_pThread->m_hThread, INFINITE);
// 释放所有资源
::CloseHandle(m_hWorkEvent);
::DeleteCriticalSection(&m_cs);
delete m_pThread;
}
BOOL CFileCutter::StartSplit(LPCTSTR lpszDestDir, LPCTSTR lpszSourceFile, UINT uFileSize)
{
if(m_bRunning)
return FALSE;
// 保存参数
::EnterCriticalSection(&m_cs);
m_strSource = lpszSourceFile;
m_strDest = lpszDestDir;
m_uFileSize = uFileSize;
m_bSplit = TRUE;
::LeaveCriticalSection(&m_cs);
// 通知线程开始工作
::SetEvent(m_hWorkEvent);
return TRUE;
}
BOOL CFileCutter::StartMerge(LPCTSTR lpszDestFile, LPCTSTR lpszSourceDir)
{
if(m_bRunning)
return FALSE;
// 保存参数
::EnterCriticalSection(&m_cs);
m_strSource = lpszSourceDir;
m_strDest = lpszDestFile;
m_bSplit = FALSE;
::LeaveCriticalSection(&m_cs);
// 通知线程开始工作
::SetEvent(m_hWorkEvent);
return TRUE;
}
BOOL CFileCutter::SuspendCutter()
{
if(!m_bRunning)
return FALSE;
// 暂停工作线程
if(!m_bSuspend)
{
m_pThread->SuspendThread();
m_bSuspend = TRUE;
}
return TRUE;
}
BOOL CFileCutter::ResumeCutter()
{
if(!m_bRunning)
return FALSE;
// 唤醒工作线程
if(m_bSuspend)
{
m_pThread->ResumeThread();
m_bSuspend = FALSE;
}
return TRUE;
}
void CFileCutter::StopCutter()
{
// 设置强制退出标志
::EnterCriticalSection(&m_cs);
m_bContinue = FALSE;
::LeaveCriticalSection(&m_cs);
// 防止线程处于暂停状态
ResumeCutter();
}
//-------------------------实现代码-------------------------//
void CFileCutter::DoSplit()
{
int nCompleted = 0;
CString strSourceFile = m_strSource;
CString strDestDir = m_strDest;
CFile sourceFile, destFile;
// 打开源文件
BOOL bOK = sourceFile.Open(strSourceFile,
CFile::modeRead|CFile::shareDenyWrite|CFile::typeBinary);
if(!bOK)
{
// 通知用户,源文件出错
::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitSourceErr, nCompleted);
return;
}
// 确保目标目录存在(逐层创建它们)
int nPos = -1;
while((nPos = strDestDir.Find('\\', nPos+1)) != -1)
{
::CreateDirectory(strDestDir.Left(nPos), NULL);
}
::CreateDirectory(strDestDir, NULL);
if(strDestDir.Right(1) != '\\')
strDestDir += '\\';
// 通知用户,开始分割文件
int nTotalFiles = sourceFile.GetLength()/m_uFileSize + 1;
::PostMessage(m_hWndNotify, WM_CUTTERSTART, nTotalFiles, TRUE);
// 开始去读源文件,将数据写入目标文件
const int c_page = 4*1024;
char buff[c_page];
DWORD dwRead;
CString sDestName;
int nPreCount = 1;
UINT uWriteBytes;
do
{
// 创建一个目标文件
sDestName.Format("%d__", nPreCount);
sDestName += sourceFile.GetFileName();
if(!destFile.Open(strDestDir + sDestName, CFile::modeWrite|CFile::modeCreate))
{
::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitDestErr, nCompleted);
sourceFile.Close();
return;
}
// 向目标文件写数据,直到大小符合用户的要求,或者源文件读完
uWriteBytes = 0;
do
{
// 首先判断是否要求终止执行
if(!m_bContinue)
{
destFile.Close();
sourceFile.Close();
if(!m_bExitThread)
::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitUserForce, nCompleted);
return;
}
// 进行真正的读写操作
dwRead = sourceFile.Read(buff, c_page);
destFile.Write(buff, dwRead);
uWriteBytes += dwRead;
}while(dwRead > 0 && uWriteBytes < m_uFileSize);
// 关闭这个目标文件
destFile.Close();
// 通知用户,当前的状态信息
nCompleted = nPreCount++;
::PostMessage(m_hWndNotify, WM_CUTTERSTATUS, 0, nCompleted);
}while(dwRead > 0);
// 关闭源文件
sourceFile.Close();
// 通知用户,工作完成
::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitSuccess, nCompleted);
}
void CFileCutter::DoMerge()
{
int nCompleted = 0;
CString strSourceDir = m_strSource;
CString strDestFile = m_strDest;
if(strSourceDir.Right(1) != '\\')
strSourceDir += '\\';
if(strDestFile.Right(1) != '\\')
strDestFile += '\\';
// 取得源目录中待合并的文件的文件名称和数量
CString strFileName;
int nTotalFiles = 0;
CFileFind find;
BOOL bRet;
if(find.FindFile(strSourceDir + "*.*"))
{
do
{
bRet = find.FindNextFile();
if(find.IsDirectory() && find.IsDots())
continue;
if(find.GetFileName().Find("__", 0) != -1)
{
nTotalFiles++;
strFileName = find.GetFileName();
}
}while(bRet);
}
find.Close();
if(nTotalFiles == 0)
{
// 通知用户,源文件出错
::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitSourceErr, nCompleted);
return;
}
// 取得文件名称
strFileName = strFileName.Mid(strFileName.Find("__") + 2);
// 确保目标目录存在(逐层创建它们)
int nPos = 0;
while((nPos = strDestFile.Find('\\', nPos+1)) != -1)
{
::CreateDirectory(strDestFile.Left(nPos + 1), NULL);
}
::CreateDirectory(strDestFile, NULL);
// 创建目标文件
CFile sourceFile, destFile;
strDestFile += strFileName;
if(!destFile.Open(strDestFile, CFile::modeRead|CFile::modeWrite|CFile::modeCreate))
{
::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitDestErr, nCompleted);
return;
}
// 通知用户,开始分割文件
::PostMessage(m_hWndNotify, WM_CUTTERSTART, nTotalFiles, nCompleted);
// 开始去读源文件,将数据写入目标文件
const int c_page = 4*1024;
char buff[c_page];
int nPreCount = 1;
CString sSourceName;
DWORD dwRead;
do
{
// 打开一个源文件
sSourceName.Format("%d__", nPreCount);
sSourceName += strFileName;
if(!sourceFile.Open(strSourceDir + sSourceName, CFile::modeRead|CFile::shareDenyWrite))
{
break;
}
// 将这个源文件中的数据全部写入目标文件
do
{
if(!m_bContinue)
{
sourceFile.Close();
destFile.Close();
if(!m_bExitThread)
::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitUserForce, nCompleted);
return;
}
dwRead = sourceFile.Read(buff, c_page);
destFile.Write(buff, dwRead);
}
while(dwRead > 0);
sourceFile.Close();
// 通知用户,当前的状态信息
nCompleted = nPreCount++;
::PostMessage(m_hWndNotify, WM_CUTTERSTATUS, 0, nCompleted);
}
while(TRUE);
// 通知用户,工作完成
::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitSuccess, nCompleted);
}