目标:一个能按时间间隔,或文件夹发生改变时执行备份的软件。
环境:mfc vs2013
软件界面:
备份按钮是手动执行单次备份,写入配置文件按钮是将参数写入set.ini中,备份目标是一个目录地址,备份源是要备份的目录地址,”;“结束,可多个;
定时备份是按时间间隔备份,时间的单位是ms,开始监视是一旦文件改变立刻备份。
功能分析:
1)备份,其实就是复制操作。
在网上找了一下,复制文件夹可用壳函数SHFileOperation(&sos),sos是结构体SHFILEOPSTRUCT,填上参数后用SHFileOperation(&sos)执行相应操作,具体代码如下:
void Backup(CString src,CString dst){
Write(_T("back up dir ") + src + _T(" to ") + dst + _T("\r\n"), _T("./Log.txt"), _T("end"));
<span style="white-space:pre"> </span>CreateDirectory(dst,NULL);
SHFILEOPSTRUCT sos;
src += _T('\0');
dst += _T('\0');
sos.hwnd = NULL;
sos.pFrom = src;
sos.pTo = dst;
sos.wFunc = FO_COPY;
sos.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR;
SHFileOperation(&sos);
}
2)读写文件,主要是用来写程序log,读写set.ini,使用cfile读写。
void Write(CString strc, CString addr = _T("./Log.txt"),CString label=_T("begin"),CString label_create=_T("no")){
int len = strc.GetLength();
char *log = new char[len];
WideCharToMultiByte(CP_OEMCP, NULL, strc.GetBuffer(), -1, log, len, NULL, FALSE);
CFile mFile;
if (label_create == _T("yes")){
mFile.Open(addr, CFile::modeCreate | CFile::modeReadWrite);
}
else{
mFile.Open(addr, CFile::modeCreate | CFile::modeReadWrite | CFile::modeNoTruncate);
}
if (label == _T("end")){
mFile.SeekToEnd();
}
else{
mFile.SeekToBegin();
}
mFile.Write(log, len);
mFile.Close();
}
CString Read(CString str){
CFile mFile;
mFile.Open(str, CFile::modeRead);
int len = mFile.GetLength();
char *temp = new char[len];
mFile.Read(temp, len);
CString strc;
LPWSTR p = strc.GetBuffer(len);
MultiByteToWideChar(CP_ACP, 0, temp, -1, p, len);//字符集转换
strc.ReleaseBuffer();
mFile.Close();
return strc;
}
3)单次备份的实现:
void CAutoBakcupDlg::OnBnClickedButton1()
{
SYSTEMTIME st;
GetLocalTime(&st);
CString strTime;
strTime.Format(_T("%4d %2d %2d %2d %2d %2d"), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
ReadIni();
for (int i = 0; i < num; i++){
Backup(dir_arr[i], dst+_T("\\")+strTime);
}
Write(_T("back up dir\r\n"), _T("./Log.txt"), _T("end"));
//SetDlgItemText(IDC_EDIT2,Read(_T("./Log.txt")));
CEdit* pedit = (CEdit*)GetDlgItem(IDC_EDIT2);
pedit->SetSel(-1, -1);
pedit->ReplaceSel(_T("back up dir\r\n"));
pedit->LineScroll(pedit->GetLineCount());
}
在这儿,备份到了相应时间为名的文件夹下面
在这儿使用了定时器,在mfc中使用定时器还是很简单的,settimer后,添加消息处理程序onTimer,在里面调用上面的备份函数即可。
void CAutoBakcupDlg::OnBnClickedButton3()
{
// TODO: 在此添加控件通知处理程序代码
int m = GetDlgItemInt(IDC_EDIT4);
nIDEvent = CWnd::SetTimer(1,m,NULL);
Write(_T("timer is on\r\n"), _T("./Log.txt"), _T("end"));
CEdit* pedit = (CEdit*)GetDlgItem(IDC_EDIT2);
pedit->SetSel(-1, -1);
pedit->ReplaceSel(_T("timer is on\r\n"));
pedit->LineScroll(pedit->GetLineCount());
}
void CAutoBakcupDlg::OnBnClickedButton4()
{
// TODO: 在此添加控件通知处理程序代码
CWnd::KillTimer(nIDEvent);
Write(_T("timer is off\r\n"), _T("./Log.txt"), _T("end"));
CEdit* pedit = (CEdit*)GetDlgItem(IDC_EDIT2);
pedit->SetSel(-1, -1);
pedit->ReplaceSel(_T("timer is off\r\n"));
pedit->LineScroll(pedit->GetLineCount());
}
void CAutoBakcupDlg::OnTimer(UINT_PTR nIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
OnBnClickedButton1();
CDialogEx::OnTimer(nIDEvent);
}
5)检测到文件更改时执行备份。
使用FindFirstChangeNotification系列函数来监控文件夹的更改情况,发现更改就备份。
由于开始监视后,该函数就会一直处于循环状态,如果在mfc中直接调用会导致ui失去响应,因此把这个函数放到子线程中执行。发现文件更改就发送一个消息给主程序。实现如下:
子线程函数
UINT SPY(LPVOID lpParam){
CString dir=(LPCTSTR)lpParam;
DWORD dwWaitStatus;
HANDLE dwChangeHandle;
dwChangeHandle = FindFirstChangeNotification(
dir, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE
);
if (dwChangeHandle == INVALID_HANDLE_VALUE){
ExitProcess(GetLastError());
}
while (isonspy){
dwWaitStatus = WaitForSingleObject(dwChangeHandle, -1);
switch (dwWaitStatus){
case WAIT_OBJECT_0:
if (FindNextChangeNotification(dwChangeHandle) == false){
ExitProcess(GetLastError());
}
PostMessage(AfxGetMainWnd()->m_hWnd, WM_SPY, 0, 0);
break;
default:
break;
}
}
return 0;
}
调用子线程:
void CAutoBakcupDlg::OnBnClickedButton5()
{
// TODO: 在此添加控件通知处理程序代码
isonspy = true;
CWinThread ** subthread = new CWinThread * [num];
for (int i = 0; i < num; i++){
subthread[i] = AfxBeginThread(SPY, (LPVOID)(LPCTSTR)dir_arr[i]);
}
Write(_T("spy is on\r\n"), _T("./Log.txt"), _T("end"));
CEdit* pedit = (CEdit*)GetDlgItem(IDC_EDIT2);
pedit->SetSel(-1, -1);
pedit->ReplaceSel(_T("spy is on\r\n"));
pedit->LineScroll(pedit->GetLineCount());
}
响应消息:
afx_msg LRESULT CAutoBakcupDlg::OnSpy(WPARAM wParam, LPARAM lParam)
{
OnBnClickedButton1();
return 0;
}
剩下的就是实现细节方面的问题了。
新鲜出炉的效果图:
问题:
界面丑,
如果目标文件夹不存在的话,会出现bug,也就是说要备份到哪,目录需事先建好,(主要是递归建立目录不想写了)
拷贝大文件速度慢,其实backup也应该放到子线程里面
壳函数调试起来太操蛋了,还不如用createdirectory,copyfile自己写一个复制函数
暂时就这样。