0%

文件切割系统

文件切割系统

通过这个程序的编写,是一个提升,这段代码提现了明显的Windows程序编写的优点,程序的编写我是参考书籍,虽然书籍有部分出错,但是大体的思想还是非常好的。

程序思想

首先我们需要了解文件分割合并工具的一个操作流程,分割文件与合并文件其实就是对文件的一个操作(读取创建写入),我们完全可以通过MFC给我们的CFile类。
程序流程:我们选择分割单选框的时候,我们供用户一个文件打开通用对话框,我们选择合并单选框的时候我们提供用户一个文件夹选择对话框,让给用户选择一个文件夹,然后合并里面的一些固定文件名称的文件(1__文件.*)。

看下程序操作的一些截图:
选择程序
分割的文件
程序运行成功

这个是简单的分割,其实原理不是太难,而且也是有一定的缺陷,但还是一个很典型的Win32程序。

首先我们需要构造我们这个程序的编写思路,首先我们需要我们之前写好的那个CDirDialog类,传送门:https://wker666.github.io/2020/01/19/%E9%80%9A%E7%94%A8%E5%AF%B9%E8%AF%9D%E6%A1%86/
导入这个类之后,但是直接导入到MFC中是不行的,你会发现重定义,需要将我们的那个头文件和源文件分开写。
为了我们后期开发的一个可维护性很高的程序话呢,我感觉适当的“高内聚低耦合”是一个比较重要的一点,所以我们将我们的文件分割操作写到一个类中,正式这个类,完美的提现了Win32编程的巧妙与灵活性。

程序流程

首先我们重要的CFileCutter类是需要提供一个完整的流程,就是从构造函数一被执行我们就需要开启一个线程,这个线程是类中的一个函数,但是类函数是不能作为线程函数的,这个时候就想到了一个很聪明的做法,那就是类的友元函数,虽然函数的格式固定了,但是友元函数的格式不是固定的,开启线程之后,我们设置一个事件对象,这个事件对象是用来控制线程是否继续的,还有有一点很重要就是我们需要一个关键代码段的对象,因为我们可能在多个地方调用成员变量,所以这个东西也是很关键的一点,并且我们使用PostMessage的方式进行传输我们的消息,这样使我们的接受信息变得不需要有回调函数,程序变得方便了许多。

头文件定义

CFileCutter.h头文件的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#ifndef __FILECUTTER_H__
#define __FILECUTTER_H__

#define WM_CUTTERSTART WM_USER+100 //wParam = nTotalCount
#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 StartSplit(LPCTSTR lpszDestDir,LPCTSTR lpszSourceFile,UINT nFileSize);
BOOL StartMerge(LPCTSTR lpszDestDir,LPCTSTR lpszSourceFile);
BOOL SuspendCutter();
BOOL ResumeCutter();
void StopCutter();
BOOL IsRunning(){return m_bRunning;}
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;//临界区的的一个参定值

private:
HWND m_hWndNotify;//接受信息的窗口句柄
HANDLE m_hWorkEvent;//通知信息的事件对象句柄
CWinThread * m_pThread;//工作线程
BOOL m_bSuspend;//暂停标志
BOOL m_bExitThread;//退出标志
};


#endif//__FILECUTTER_H__

为了防止重定义我们的类,我们需要使用到一些预编译指令。

  1. 四个参数信息,分别是:源文件,目标文件,是否是分割,每个分割文件的大小
  2. 两个状态信息:是否继续,是否在运行
  3. 一个参定值:关键代码段
  4. 五个受保护的值:窗口句柄(用来接收消息投递的),事件对象,工作线程的一个句柄,是否暂停,是否停止
  5. 一个枚举类型:包含了当前状态信息(操作成功,用户强行停止,源文件错误,目标文件错误)
  6. 两个真正的操作函数
  7. 一个友元线程
  8. 一个重置状态信息的函数,用来清空我们的一些操作信息。
  9. 两个间接启动的操作线程
  10. 三个线程操作函数,分别是暂停,恢复,停止
  11. 一个检验是不是在运行的函数

看完这些之后,我们对我们的类函数有了一定的了解之后我们就要来编写成员函数了。

类的定义

首先我们需要包含我们的头文件:

1
2
#include "stdafx.h"
#include "CFileCutter.h"

stdafx.h这个头文件是要包含的,因为MFC的东西都在里面。
_CutterEntry这个使我们的线程函数,里面主要是控制着是否分割合并文件的,控制端就是我们的事件对象。具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
UINT _CutterEntry(LPVOID lParam)
{
CFileCutter * pCutter = (CFileCutter *)lParam;
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;
}

首先我们接受我们传过来的文件切割对象,强制转换一下,然后用WaitForSingleObject等待我们的事件对象响应,无线等待,并且我们的退出为假。当我们满足了这些要求之后,我们先进入临界区对象,将我们的正在运行设置为真,然后离开(这个真是太仔细了),然后判断是切割还是合并。

然后我们要完成的是构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
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();
}

这个构造函数是用来初始化我们我们状态信息和启动我们的线程,初始化关键代码段对象的。并且我们需要接受我传递过来的窗口句柄,并重新设置一些信息。
然后我们要看我们的Reset()这个可能常用,主要是用来刷新我们参数信息的。

1
2
3
4
5
6
7
8
9
10
11
12
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);
}

重置一些信息,注意还是要进入关键代码段与离开。

在我们的析构函数中,我们需要清空信息,通知活动线程我们结束了,并且删除我们创建的这些对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
CFileCutter::~CFileCutter()
{
//设置结束标志
m_bExitThread = TRUE;
EnterCriticalSection(&m_cs);
m_bContinue = FALSE;
LeaveCriticalSection(&m_cs);
SetEvent(m_hWorkEvent);//防止限制线程等待m_hWorkEvent
WaitForSingleObject(m_pThread->m_hThread,INFINITE);//保证线程已经完毕
CloseHandle(m_hWorkEvent);
DeleteCriticalSection(&m_cs);
delete m_pThread;
}

首先还是先进入关键代码段,设置我们不运行了,要退出,然后告诉我们的线程函数,别等了,我们要结束了,然后等待线程的完毕,关闭事件对象,删除关键代码段,删除线程。

间接切割合并文件的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
BOOL CFileCutter::StartSplit(LPCTSTR lpszDestDir,LPCTSTR lpszSourceFile,UINT nFileSize)
{
if(m_bRunning)
return FALSE;
EnterCriticalSection(&m_cs);
m_strSource = lpszSourceFile;
m_strDest = lpszDestDir;
m_uFileSize = nFileSize;
m_bSplit = TRUE;
LeaveCriticalSection(&m_cs);
SetEvent(m_hWorkEvent);
return TRUE;
}
BOOL CFileCutter::StartMerge(LPCTSTR lpszDestDir,LPCTSTR lpszSourceFile)
{
if(m_bRunning)
return FALSE;
EnterCriticalSection(&m_cs);
m_strSource = lpszSourceFile;
m_strDest = lpszDestDir;
m_bSplit = FALSE;
LeaveCriticalSection(&m_cs);
SetEvent(m_hWorkEvent);
return TRUE;
}

这个就是先判断是不是在运行,然后进关键代码段,然后设置一些信息,然后通知线程该工作了。

再接下来就是我们的一些暂停恢复结束操作了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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();
}

暂停的话呢先判断是不是在运行,然后调用线程的暂停方法,设置成员变量,恢复也是。停止的话呢就是直接将继续这个变量给结束,然后恢复线程。

下面这个是核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
void CFileCutter::DoSplit()
{
int nCompleted = 0;
CString strSourceFile = m_strSource;
CString strDestDir = m_strDest;
CFile sourceFile,destFile;
if (!sourceFile.Open(strSourceFile,CFile::modeRead | CFile::shareDenyWrite | CFile::typeBinary))//shareDenyWrite打开文件的时候禁止其他程序进行读写
{
PostMessage(m_hWndNotify,WM_CUTTERSTOP,exitSourceErr,nCompleted);
return ;
}
//确保目录的存在,逐级创建文件目录
int nPos = -1;
while ((nPos = strDestDir.Find("\\",nPos+1)) != -1)
{
CString str = strDestDir.Left(nPos+1);
CreateDirectory(str,NULL);
}
CreateDirectory(strDestDir,NULL);
if(strDestDir.Right(1) != "\\")
strDestDir += "\\";
//通知用户,开始创建文件
int nTotalFiles = strSourceFile.GetLength()/m_uFileSize+1;
PostMessage(m_hWndNotify,WM_CUTTERSTART,nTotalFiles,TRUE);

const int c_page = 4*1024;
char Buffer[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::modeCreate | CFile::modeWrite))
{
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(Buffer,c_page);
destFile.Write(Buffer,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 strSourceFile = m_strSource;
CString strDestDir = m_strDest;

if (strSourceFile.Right(1) != "\\")
strSourceFile += "\\";
if (strDestDir.Right(1) != "\\")
strDestDir += "\\";
//取得文件的名称和数量

CString strFileName;
int nTotalFiles = 0;
CFileFind find;
BOOL bRet;
if (find.FindFile(strSourceFile+"*.*"))
{
do
{
bRet = find.FindNextFile();
if (find.IsDirectory() && find.IsDots())//有点异议,一等回头看一下
continue;
if (find.GetFileName().Find("__",0) != -1)
{
nTotalFiles++;
strFileName = find.GetFileName();
}
} while (bRet);
}//if end
find.Close();
if (nTotalFiles == 0)
{
PostMessage(m_hWndNotify,WM_CUTTERSTOP,exitSourceErr,nCompleted);
return;
}
strFileName = strFileName.Mid(strFileName.Find("__")+2);

//逐级创建目标目录

int nPos = -1;
while ((nPos = strDestDir.Find("\\",nPos+1)) != -1)
{
CreateDirectory(strDestDir.Left(nPos),NULL);//有点问题一等看下
}
CreateDirectory(strDestDir,NULL);

//创建目标文件
CFile sourceFile,destFile;
strDestDir += strFileName;
if (!destFile.Open(strDestDir,CFile::modeReadWrite | CFile::modeCreate))
{
PostMessage(m_hWndNotify,WM_CUTTERSTOP,exitDestErr,nCompleted);
return;
}
//开始合并
PostMessage(m_hWndNotify,WM_CUTTERSTART,nTotalFiles,nCompleted);

const int c_page = 1024*4;
char Buffer[c_page];
int nPreCount = 1;
CString sSourceName;
DWORD dwRead;
do
{
sSourceName.Format("%d__",nPreCount);
sSourceName += strFileName;
if(!sourceFile.Open(strSourceFile+sSourceName,CFile::modeRead | CFile::shareDenyWrite | CFile::typeBinary))//打不开失败的时候说明就没有了,这里我感觉用for循环比较好,还能提供错误处理,之后改一下。//
{
break;
}
// 写入文件
do
{
if (!m_bContinue)
{
sourceFile.Close();
destFile.Close();
if (!m_bExitThread)
PostMessage(m_hWndNotify,WM_CUTTERSTOP,exitUserForce,nCompleted);
return;
}
dwRead = sourceFile.Read(Buffer,c_page);
destFile.Write(Buffer,dwRead);
} while (dwRead > 0);
sourceFile.Close();
nCompleted = nPreCount++;
PostMessage(m_hWndNotify,WM_CUTTERSTATUS,0,nCompleted);
} while (TRUE);
destFile.Close();
//通知用户,工作完成
PostMessage(m_hWndNotify,WM_CUTTERSTOP,exitSuccess,nCompleted);
}

其实就是操作我们的文件,使用CFile这个类。
分割:先是打开源文件,失败的话呢投递消息,这里要POST而不是Send,然后我们再创建我们的目录,创建完毕目录之后纠正一下目标文件目录。然后我们获取一下文件理论上的道德个数,投递一个开始的消息。然后就是我们简单的初始化呢一下我们的各种信息,进入我们的循环,显示创建目标文件,创建写入的方法打开,出错的话呢就投递消息,然后在判断是不是强行终止了,强行终止的话呢我们就要关一些东西,然后投递我们的消息,然后再读源文件,在写源文件,每次读4KB,这个时候我们要读够用户传递给我们文件大小,关闭文件句柄,发送一个状态消息,一直循环,完毕之后关闭源文件,投递执行成功的消息。

合并:基本和我们的分割一样,显示纠正一下我们的源和目标路径,然后再找一下符合规范的文件,find.IsDirectory() && find.IsDots()这个其实我感觉有点异议但是没碰见出错。如果一个源文件都没有,肯定要投递错误源文件的错误,在创建目录,投递一下我们要开始了,给他目标文件的大小。再就是初始化变量,进入循环打开源文件,然后判断是不是要继续,然后读写再就是和我们的分割一样了。

类的实际调用

既然我们的类写好了,实际编写的话呢无非就是类的一个调用罢了,首先我们需要配置一下我们的组件,这里就不提了,没啥太大必要,照葫芦画瓢就可以了。

头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

// FileCutterDlg.h : 头文件
//


#pragma once
#include "CFileCutter.h"
#include "CDirDialog.h"
#include "afxcmn.h"

// CFileCutterDlg 对话框
class CFileCutterDlg : public CDialogEx
{
// 构造
public:
CFileCutterDlg(CWnd* pParent = NULL); // 标准构造函数

// 对话框数据
enum { IDD = IDD_FILECUTTER_DIALOG };

protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持


// 实现
protected:
HICON m_hIcon;
CFileCutter* m_pCutter;
CProgressCtrl m_Progress;

// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()

void UIControl(void);
public:
afx_msg void OnBnClickedSelectsplit();
afx_msg void OnBnClickedSelectmergr();
afx_msg void OnBnClickedSourcebrowser();
afx_msg void OnBnClickedDestbrowser();
afx_msg void OnBnClickedStart();
afx_msg void OnBnClickedStop();
afx_msg void OnBnClickedCancel();
protected:
afx_msg LRESULT OnCutterstart(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnCutterstop(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnCutterstatus(WPARAM wParam, LPARAM lParam);
};

就是一个简单的引入,比较关键的就是:

1
2
3
CFileCutter* m_pCutter;
CProgressCtrl m_Progress;
void UIControl(void);

这三个比较关键,最后一个就是用来调节UI的,其实就是判断你是不是在运行啊,是选了分割还是合并啊之类的。

源文件

CFileCutterDlg::OnInitDialog()初始化

1
2
3
4
5
6
7
8
m_pCutter = new CFileCutter(m_hWnd);
((CButton*)GetDlgItem(IDC_SELECTSPLIT))->SetCheck(1);
((CComboBox*)GetDlgItem(IDC_UINT))->AddString("1");
((CComboBox*)GetDlgItem(IDC_UINT))->AddString("30");
((CComboBox*)GetDlgItem(IDC_UINT))->AddString("60");
((CComboBox*)GetDlgItem(IDC_UINT))->SetCurSel(0);
//m_Progress.SubclassWindow(GetDlgItem(IDC_PROGRESS)->m_hWnd);
UIControl();

这里之前用了子类化控件的一个方法,但我实在没看到用的必要我就给注释,(其实主要是我发现报错,哈哈)
UIControl函数的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void CFileCutterDlg::UIControl(void)
{
BOOL bIsWorking = m_pCutter->IsRunning();
GetDlgItem(IDC_SELECTSPLIT)->EnableWindow(!bIsWorking);
GetDlgItem(IDC_SELECTMERGR)->EnableWindow(!bIsWorking);
GetDlgItem(IDC_UINT)->EnableWindow(!bIsWorking);

GetDlgItem(IDC_START)->EnableWindow(!bIsWorking);
GetDlgItem(IDC_STOP)->EnableWindow(!bIsWorking);

if (bIsWorking)
{
return;
}
if (((CButton*)GetDlgItem(IDC_SELECTSPLIT))->GetCheck())
{
//分割
GetDlgItem(IDC_START)->SetWindowText("分割");
GetDlgItem(IDC_SOURCETITLE)->SetWindowText("请选择要分割的文件:");
GetDlgItem(IDC_DESTTITLE)->SetWindowText("请选择分割后保存到的文件夹");
GetDlgItem(IDC_UINT)->EnableWindow(TRUE);
}else
{
//合并
GetDlgItem(IDC_START)->SetWindowText("合并");
GetDlgItem(IDC_SOURCETITLE)->SetWindowText("请选择要合并的文件夹:");
GetDlgItem(IDC_DESTTITLE)->SetWindowText("请选择合并后保存到的文件夹");
GetDlgItem(IDC_UINT)->EnableWindow(FALSE);
}
GetDlgItem(IDC_STATUSTEXT)->SetWindowText("状态显示");
m_Progress.SetPos(0);
}

这个的话呢其实也看起来不是很难,就是通过我们的类成员函数判断是不是在运行,是选择的分割还是合并,然后初始化一些控件。

单选框按钮被按下的时候:

1
2
3
4
5
6
7
8
9
10
void CFileCutterDlg::OnBnClickedSelectsplit()
{
UIControl();
}


void CFileCutterDlg::OnBnClickedSelectmergr()
{
UIControl();
}

这个就是当按单选框按钮被按下的时候我们进行的操作,就是用来刷新一下我们的一些UI。

当我们点击选择文件夹的时候:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void CFileCutterDlg::OnBnClickedSourcebrowser()
{
if (((CButton*)GetDlgItem(IDC_SELECTSPLIT))->GetCheck())
{
//分割
CFileDialog sourceFile(TRUE);
if (sourceFile.DoModal() == IDOK)
{
GetDlgItem(IDC_EDITSOURCE)->SetWindowText(sourceFile.GetPathName());
GetDlgItem(IDC_EDITDEST)->SetWindowText(sourceFile.GetPathName().Left(sourceFile.GetPathName().ReverseFind('.')));
}
}
else
{
//合并
CDirDialog sourceFolder;
if (sourceFolder.DoBrowse(*this) == IDOK)
{
GetDlgItem(IDC_EDITSOURCE)->SetWindowText(sourceFolder.GetDirPath());
CString strDef = sourceFolder.GetDirPath();
strDef.TrimRight('\\');
strDef = strDef + "\\" + strDef.Mid(strDef.ReverseFind('\\')+1);
strDef.TrimRight(":");//防止选择根目录
GetDlgItem(IDC_EDITDEST)->SetWindowText(strDef);
}
}
}


void CFileCutterDlg::OnBnClickedDestbrowser()
{
CDirDialog destFolder;
if (destFolder.DoBrowse(*this) == IDOK)
{
GetDlgItem(IDC_EDITDEST)->SetWindowText(destFolder.GetDirPath());
}
}

这个当我们点击源文件的时候我们就是先判断是风格还是合并,分割的话呢就是文件对话框,合并的话呢就是我们自己写的那个类,不要忘记导入就好了。然后比较聪明的就是:

1
2
strDef = strDef + "\\" + strDef.Mid(strDef.ReverseFind('\\')+1);
strDef.TrimRight(":");//防止选择根目录

这两句,就是生成一个与我们文件夹相同的子文件夹,然后防止了这个根目录(F:\)。

再就是我们点击分割开始的按钮的时候:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void CFileCutterDlg::OnBnClickedStart()
{
CString strSource,strDest;
GetDlgItem(IDC_EDITSOURCE)->GetWindowText(strSource);
GetDlgItem(IDC_EDITDEST)->GetWindowText(strDest);

if (strSource.IsEmpty() || strDest.IsEmpty())
{
MessageBox("文件或者文件路径不能为空");
return;
}

if (((CButton*)GetDlgItem(IDC_SELECTSPLIT))->GetCheck())
{
CString str;
GetDlgItem(IDC_UINT)->GetWindowText(str);
m_pCutter->StartSplit(strDest,strSource,atoi(str)*1024*1024);
}else
{
m_pCutter->StartMerge(strDest,strSource);
}

}


void CFileCutterDlg::OnBnClickedStop()
{
m_pCutter->SuspendCutter();
if (MessageBox("确定要终止么?",NULL,MB_YESNO) == IDYES)
{
m_pCutter->StopCutter();
}
else
{
m_pCutter->ResumeCutter();
}
}

显示判断是否有填写源文件与目标文件夹,然后判断是分割还是合并,最后调用类的开始方法进行操作。

最后就是我们的消息映射:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
afx_msg LRESULT CFileCutterDlg::OnCutterstart(WPARAM wParam, LPARAM lParam)
{
//设置进度条范围
int nTotalFiles = wParam;
m_Progress.SetRange32(0,nTotalFiles);
UIControl();
return 0;
}


afx_msg LRESULT CFileCutterDlg::OnCutterstop(WPARAM wParam, LPARAM lParam)
{
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;
}


afx_msg LRESULT CFileCutterDlg::OnCutterstatus(WPARAM wParam, LPARAM lParam)
{
int nCompleted = int (lParam);
m_Progress.SetPos(nCompleted);
CString s;
s.Format("完成%d个文件",nCompleted);
GetDlgItem(IDC_STATUSTEXT)->SetWindowText(s);
return 0;
}

效应映射需要我们在类向导中进行声明,不用类向导的话呢要自己写声明和消息映射。
接受到开始的时候,我们需要设置进度条,然后初始化UI,停止的话呢我们要进行停止原因的判断,状态的话呢无非就是完成个数的输出。

程序的不足

  1. 首先我们是在合并的时候我感觉用for循环更好一些
  2. 这个要是能用文件映射的话呢应该可以处理更大的文件。