在這篇文章我們學(xué)習(xí)用C#來(lái)操作,并制作一個(gè)備份還原工具。
我們要操作cmd.exe使用到了Process類(lèi),使用這個(gè)類(lèi)首先要引入命名空間 System.Diagnostics,此類(lèi)提供對(duì)本地和遠(yuǎn)程進(jìn)程的訪問(wèn)并能夠啟動(dòng)和停止本地系統(tǒng)進(jìn)程。
/// <summary> /// 執(zhí)行Cmd命令 /// </summary> /// <param name="workingDirectory">要啟動(dòng)的進(jìn)程的目錄</param> /// <param name="command">要執(zhí)行的命令</param> public static void StartCmd(String workingDirectory, String command) { Process p = new Process(); p.StartInfo.FileName = "cmd.exe"; p.StartInfo.WorkingDirectory = workingDirectory; p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardInput = true; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardError = true; p.StartInfo.CreateNoWindow = true; p.Start(); p.StandardInput.WriteLine(command); p.StandardInput.WriteLine("exit"); p.WaitForExit(); p.Close(); }
在看看如何使用上方法:
string appDircectroy="C:\\Program Files\\MySQL\\MySQL Server 5.5\\bin"
string cmd="mysqldump -hlocalhost -uroot -proot --default-character-set=utf8 --lock-tables --routines --force --quick Dbname>d:\backup.sql"
StartCmd(appDircectroy,cmd);
這里我們同Process操作cmd.exe,調(diào)用mysqldump.exe 執(zhí)行命令"-hlocalhost -uroot -proot --default-character-set=utf8 --lock-tables --routines --force --quick Dbname>d:\backup.sql"備份數(shù)據(jù)庫(kù)。其實(shí)我們也可以直接通過(guò)Process類(lèi)直接調(diào)用mysqldump.exe進(jìn)程執(zhí)行命令,寫(xiě)法如下:
/// <summary> /// 使用mysqldump執(zhí)行command命令 /// </summary> /// <param name="AppPath">mysqldump.exe的目錄</param> /// <param name="command"></param> public static void StartMySqldump(string AppPath, string command) { ProcessStartInfo psi = new ProcessStartInfo(AppPath + @"\mysqldump.exe"); psi.Arguments = command; psi.UseShellExecute = false; psi.RedirectStandardOutput = true; psi.RedirectStandardInput = true; psi.RedirectStandardError = true; psi.CreateNoWindow = true; Process pro = Process.Start(psi); pro.WaitForExit(); pro.Close(); }
調(diào)用的方法和上面的使用基本一致,就是command命令不需要寫(xiě)mysqldump 改為"-hlocalhost -uroot -proot --default-character-set=utf8 --lock-tables --routines --force --quick Dbname>d:\backup.sql" 。
我再這里使用方法1,比較靈活方便,因?yàn)槲覀冊(cè)虻氖褂靡部梢杂玫酱朔椒▓?zhí)行mysql.exe還原命令,上篇中說(shuō)到還原時(shí),數(shù)據(jù)庫(kù)如何不存在的使用,我們要先創(chuàng)建數(shù)據(jù)庫(kù),再還原,那么就是要執(zhí)行兩天命令,我們可以改一下方法1,使它可以執(zhí)行多條命令,如下:
/// <summary> /// 執(zhí)行Cmd命令 /// </summary> /// <param name="workingDirectory">要啟動(dòng)的進(jìn)程的目錄</param> /// <param name="command">要執(zhí)行的命令</param> public static void StartCmd(String workingDirectory, String[] commands) { Process p = new Process(); p.StartInfo.FileName = "cmd.exe"; p.StartInfo.WorkingDirectory = workingDirectory; p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardInput = true; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardError = true; p.StartInfo.CreateNoWindow = true; p.Start(); foreach (string cmd in commands) { p.StandardInput.WriteLine(cmd); } p.StandardInput.WriteLine("exit"); p.WaitForExit(); p.Close(); }
有了上面的函數(shù),編寫(xiě)一個(gè)winform備份還原工具就比較容易實(shí)現(xiàn)了,但是現(xiàn)在我要備份300多個(gè)數(shù)據(jù)庫(kù),我要生成300多個(gè)數(shù)據(jù)庫(kù)備份文件,當(dāng)然我們也可以通過(guò)命令將300多個(gè)數(shù)據(jù)庫(kù),即所有數(shù)據(jù)庫(kù)(包含mysql自帶的系統(tǒng)庫(kù))備份到一個(gè)文件夾。還有一點(diǎn)要注意就是用戶(hù)體驗(yàn),假如備份的一個(gè)數(shù)據(jù)庫(kù)很大,那么直接調(diào)用就會(huì)吧UI卡死,所以我們要用異步線(xiàn)程來(lái)解決此問(wèn)題,我打算用四個(gè)線(xiàn)程同時(shí)執(zhí)行備份,直到300多個(gè)數(shù)據(jù)庫(kù)執(zhí)行完畢。
有人可能對(duì)線(xiàn)程不熟悉,不用怕,其實(shí).net提供了一個(gè)異步線(xiàn)程的封裝 BackgroundWorker,用此類(lèi)可以很簡(jiǎn)單實(shí)現(xiàn)。 我們用到BackgroundWorker提供的兩個(gè)事件DoWork和RunWorkerCompleted,DoWork中寫(xiě)線(xiàn)程開(kāi)始要執(zhí)行的任務(wù),RunWorkerCompleted中寫(xiě)線(xiàn)程執(zhí)行的任務(wù)結(jié)束要執(zhí)行的工作。看看簡(jiǎn)單的代碼示例:
BackgroundWorker backgroundWorker = new BackgroundWorker(); backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork); backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted); backgroundWorker.RunWorkerAsync(remainDbNames); //remainDbName是list<string>類(lèi)型, 傳入的參數(shù),我保存要備份的庫(kù)的名字,備份一個(gè)庫(kù)就移除一個(gè)庫(kù)
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { List<string> names = e.Argument as List<string>; //獲取傳入的參數(shù)ramainDbName
BackupMany(filePath, names, boolBackdata); //執(zhí)行備份方法 //e.Result = e.Argument.ToString(); //完成任務(wù)時(shí)向RunWorkerCompleted方法傳遞的結(jié)果參數(shù)
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
// writeText(textResult,true,"完成數(shù)據(jù)庫(kù)"+e.Result.ToString()+"備份操作" + Environment.NewLine);
}
使用就是那么簡(jiǎn)單,下面看看我啟用四個(gè)線(xiàn)程同時(shí)備份:
//只有在備份按鈕事件中生成個(gè)異步線(xiàn)程即可 for (int i = 0; i < 4; i++) { BackgroundWorker backgroundWorker = new BackgroundWorker(); backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork); backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted); backgroundWorker.RunWorkerAsync(remainDbNames); }
下面看看單個(gè)線(xiàn)程具體如何從remainDbName中取數(shù)據(jù)庫(kù)的名字執(zhí)行備份
object lockobject = new object(); //備份時(shí)的lock對(duì)象
delegate void WriteText(TextBox textbox, bool append, string text); private void writeText(TextBox textbox, bool append, string msg) { if (this.InvokeRequired) { WriteText d = new WriteText(writeText); object[] obj = new object[3]; obj[0] = textbox; obj[1] = append; obj[2] = msg; this.Invoke(d, obj); } else { if (append) textbox.AppendText(msg); else textbox.Text = msg; } }
private void BackupMany(string path, List<string> dbNames, bool backupdata) { string name = null; lock (lockobject) { if (dbNames.Count > 0) { name = dbNames[0]; dbNames.RemoveAt(0); } } if (name != null) { Backup(path, name, backupdata); BackupMany(path, dbNames, backupdata); } else { timer1.Enabled = false; writeText(textResult, true, "全部執(zhí)行完成!"); } } private void Backup(string path, string databaseName, bool backupdata) { try { writeText(textResult, true, DateTime.Now.ToString() + " 開(kāi)始數(shù)據(jù)庫(kù)" + databaseName + "備份" + Environment.NewLine); //String command = "mysqldump --quick --host=localhost --default-character-set=gb2312 --lock-tables --verbose --force --port=端口號(hào) --user=用戶(hù)名 --password=密碼 數(shù)據(jù)庫(kù)名 -r 備份到的地址"; //構(gòu)建執(zhí)行的命令 String directory = path + "\\" + databaseName + ".sql"; String command; if (backupdata) { command = string.Format("mysqldump --quick --host={1} --default-character-set={2} --lock-tables --routines --force --port={3} --user={4} --password={5} {6} -r \"{0}\"", directory, host, characterSet, port, user, password, databaseName); } else { command = string.Format("mysqldump --quick --host={1} --default-character-set={2} -d --lock-tables --routines --force --port={3} --user={4} --password={5} {6} -r \"{0}\"", directory, host, characterSet, port, user, password, databaseName); } Cmd.StartCmd(appDirecroty, command); writeText(textResult, true, DateTime.Now.ToString() + @" 數(shù)據(jù)庫(kù)已成功備份到 " + directory + " 文件中" + Environment.NewLine); counter++; writeText(textBox1, false, "共" + totlecount + "個(gè)文件,已完成" + counter + "個(gè)文件"); } catch (Exception ex) { writeText(txtErr, true, DateTime.Now.ToString() + " 數(shù)據(jù)庫(kù)" + databaseName + "備份失!"); } }
writeText是向TextBox寫(xiě)入文本的方法,因?yàn)楦本(xiàn)程操作主線(xiàn)程生成的控件時(shí),會(huì)有線(xiàn)程安全問(wèn)題,所以要用異步委托。如何你知道這個(gè)怎么用,其實(shí)還有一個(gè)簡(jiǎn)單點(diǎn)的方法,就是把前程安全檢查給關(guān)閉掉,在窗體onload中可以加入Control.CheckForIllegalCrossThreadCalls = false; 這句就可以跨線(xiàn)程操作UI而不會(huì)報(bào)錯(cuò)。
還原的實(shí)現(xiàn)和備份的操作差不多,這里就不提了。本想上傳個(gè)一下代碼可是找不到上傳的方法。