以前給單位作過一個多進程監控的自動關機工具,詳見那篇blog。html
此次領導又加了需求,說要等進程監控結束後,不止須要關閉主控端server,還須要關閉其餘servers。因而就用到了我上篇文章所介紹的知識——經過PowerShell來遠程管理計算機。shell
因爲PowerShell和C#都是基於.NET的,因此也不須要膠水把這兩種語言粘合到一塊兒。能夠充分的利用二者各自的優勢,結合到一塊兒!(PowerShell在遠程管理server這方面是很擅長的。)session
因而我修改了以前的工具UI界面,多出了兩個textbox,分別用來選擇server配置文件(須要關閉的除主控端的server的相關信息都記錄在該配置文件中)和PowerShell腳本文件(主要負責遠程方面的操做):ide
server配置文件格式以下,一行對應一臺server,每一行中的server ip、用戶名、密碼用空格隔開:工具
選用的PowerShell腳本文件代碼以下:this
function ShutDownRemoteComputers { param($ip,$user,$pwd) #winrm s winrm/config/client '@{TrustedHosts=10.1.23.60"}' $sen = "'@{TrustedHosts=`""+$ip+"`"}'" winrm s winrm/config/client $sen $pw = convertto-securestring -AsPlainText -Force -String $pwd $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $user,$pw $session = New-PSSession $ip -Credential $cred icm $session {shutdown -s -t 0} }
Winform程序在主控端結束進程檢查後,會先關閉server配置文件中的servers,而後關閉主控端server(本機)。spa
代碼以下(粉色部分爲新加的和遠程相關的主要內容):rest
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using System.Diagnostics; using System.Text.RegularExpressions; using System.IO; using System.Management.Automation; using System.Management.Automation.Runspaces; namespace AutoShutDown2 { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } private void chooseFileButton_Click(object sender, EventArgs e) { OpenFileDialog fileName = new OpenFileDialog(); fileName.Filter = "文本文件|*.*|C#文件|*.cs|全部文件|*.*"; if (fileName.ShowDialog() == DialogResult.OK) { filePath.Text = fileName.FileName; } } private void filePath_Click(object sender, EventArgs e) { filePath.Text = ""; } private void startButton_Click(object sender, EventArgs e) { if (filePath.Text.ToString().Substring(filePath.Text.Length - 3, 3) == "txt") { if (Regex.IsMatch(duration.Text, "^([0-9]{1,})$")) { if (int.Parse(duration.Text) >= 30) { if (serverFilePath.Text == "") { MessageBox.Show("You should choose a server configuration file first."); } else { MessageBox.Show("PCAS will check with a duration of " + duration.Text + "s."); this.Hide(); //Check the processes with the duration. DurationStart(); } } else { MessageBox.Show("The integer number should be greater than 30 seconds."); } } else { MessageBox.Show("You can only type in an integer for duration."); duration.Text = ""; } } else { MessageBox.Show("You can only choose a txt to be a configuration file."); filePath.Text = ""; } } private void DurationStart() { //Check the process's status with the duration. System.Timers.Timer tmr = new System.Timers.Timer(int.Parse(duration.Text)*1000); tmr.Elapsed += new System.Timers.ElapsedEventHandler(CheckProcess); tmr.AutoReset = true; tmr.Enabled = true; } private void CheckProcess(object source, System.Timers.ElapsedEventArgs e) { //Check the processes's status in the config file. using (FileStream fs = new FileStream(filePath.Text, FileMode.Open)) { using (StreamReader sr = new StreamReader(fs)) { string line; int numOfTheProcesses = 0; while ((line = sr.ReadLine()) != null) { var processes = System.Diagnostics.Process.GetProcesses(); foreach (var process in processes) { if (process.ProcessName == line) { //Find the objective process. //MessageBox.Show(line); numOfTheProcesses++; } } } if (numOfTheProcesses == 0) { //No such process, shut down the computer. //MessageBox.Show("The computer is ready to be shut down."); //Shut down the computer ShutDown(); } sr.Close(); } fs.Close(); } } private void ShutDown() { //Shut down the other computers. ShutDownOthers(serverFilePath.Text, scriptPathText.Text); //Shut down this computer. System.Diagnostics.Process myProcess = new System.Diagnostics.Process(); myProcess.StartInfo.FileName = "cmd.exe"; myProcess.StartInfo.UseShellExecute = false; myProcess.StartInfo.RedirectStandardInput = true; myProcess.StartInfo.RedirectStandardOutput = true; myProcess.StartInfo.RedirectStandardError = true; myProcess.StartInfo.CreateNoWindow = true; myProcess.Start(); Thread.Sleep(3000); myProcess.StandardInput.WriteLine("shutdown -s -t 0"); //MessageBox.Show("Shut down self."); } private void ShutDownOthers(string serverFilePath,string scriptPath) { //Read servers from the server file and shut down the servers. //Read the servers. string filePath = serverFilePath; using (FileStream fs1 = new FileStream(filePath, FileMode.Open)) { try { using (StreamReader sr1 = new StreamReader(fs1)) { string line; try { while ((line = sr1.ReadLine()) != null) { var elements = line.Split(); string ip = elements[0].ToString(); string user = elements[1].ToString(); string pwd = elements[2].ToString(); //Shut down the server checked from the line. //Open the PowerShell. using (Runspace runspace = RunspaceFactory.CreateRunspace()) { //MessageBox.Show("Run PowerShell."); string script = File.ReadAllText(scriptPath); runspace.Open(); PowerShell ps = PowerShell.Create(); ps.Runspace = runspace; ps.AddScript(script); ps.Invoke(); ps.AddCommand("ShutDownRemoteComputers").AddParameter("ip", ip).AddParameter("user", user).AddParameter("pwd", pwd); ps.Invoke(); //MessageBox.Show("Shut down others"); } } } finally { sr1.Close(); } } } finally { fs1.Close(); } } } private void chooseServerFileButton_Click(object sender, EventArgs e) { OpenFileDialog fileName = new OpenFileDialog(); fileName.Filter = "文本文件|*.*|C#文件|*.cs|全部文件|*.*"; if (fileName.ShowDialog() == DialogResult.OK) { serverFilePath.Text = fileName.FileName; } } private void serverFilePath_Click(object sender, EventArgs e) { serverFilePath.Text = ""; } private void scriptPathText_Click(object sender, EventArgs e) { scriptPathText.Text = ""; } private void chooseScriptButton_Click(object sender, EventArgs e) { OpenFileDialog fileName = new OpenFileDialog(); fileName.Filter = "文本文件|*.*|C#文件|*.cs|全部文件|*.*"; if (fileName.ShowDialog() == DialogResult.OK) { scriptPathText.Text = fileName.FileName; } } } }
至於遠程所須要在主控端和被控端所作的準備工做,一語歸納就是在主控端和被控端都Enable-PSRemoting(全選A),而後互相配置Winrm信任就ok了(這兩步都是在PowerShell中進行的,詳見經過PowerShell來遠程管理計算機這篇blog)。code
//Open the PowerShell. using (Runspace runspace = RunspaceFactory.CreateRunspace()) { //MessageBox.Show("Run PowerShell."); string script = File.ReadAllText(scriptPath); runspace.Open(); PowerShell ps = PowerShell.Create(); ps.Runspace = runspace; ps.AddScript(script); ps.Invoke(); ps.AddCommand("ShutDownRemoteComputers") .AddParameter("ip", ip) .AddParameter("user", user) .AddParameter("pwd", pwd); ps.Invoke(); }
上面這段代碼就是在C#中調用PowerShell腳本的關鍵。想要在C#中引用PowerShell須要事先add reference:orm
找到這個reference最快捷的方式就是在PowerShell中輸入[psobject]
.Assembly.Location
:
而後在代碼裏using相應的命名空間就能夠了:
親測經過後得到了相關部門領導贈送的可愛多一個。