由于最近做的一个任务需要用到C#代码调用PowerShell远程执行代码,所以在参考了一些资料和自己实验整理出两种可行的方式。
分别为两种方式,一种是发送远程指令操作,一种是执行本地powershell脚本文件。
在进行操作之前,要先以管理员权限启动powershell软件,先将powershell执行权限调至最高,因为window默认不允许执行任何脚本文件。
所以我们要在powershell页面执行一条命令来改变powershell环境。
Set-ExecutionPolicy Unrestricted //最高级的权限,让powershell运行在无限制的环境下
在VS开始编写代码之前,我们还要先引入一个powershell本身自带的DLL文件,System.Management.Automation.dll,由于win7是默认内置的powershell2.0,所以这个dll一般存在C盘:
C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0
文件夹之中,添加引用后,using两个命名空间
原因是在C#和powershell之间建立交流需要用到其中的Runspace类,建立执行powershell命令的独立通信通道,而其中的pipeline.类可以为我们提供执行powershell指令后提供的返回值。
做的任务是C#通过Powshell远程操作网络服务器,首先是执行脚本的方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Management.Automation.Runspaces;
using System.Management.Automation;
using System.Management;
using System.Collections.ObjectModel;
using System.IO;
namespace DHCP.Test
{
class Class2
{
private static string RunPowershell(string filePath, string parameters)
{
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
Command scriptCommand = new Command(filePath);
Collection<CommandParameter> commandParameters = new Collection<CommandParameter>();
string[] tempParas = parameters.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < tempParas.Length; i += 2)
{
CommandParameter commandParm = new CommandParameter(tempParas[i], tempParas[i + 1]);
commandParameters.Add(commandParm);
scriptCommand.Parameters.Add(commandParm);
}
pipeline.Commands.Add(scriptCommand);
var re = pipeline.Invoke();//在pipeline管道类线程上执行委托,并且获取到执行命令后的返回值
string kk = "";
foreach (var a in re)
{
kk = a.ToString() + kk;//打印返回信息
}
if (pipeline.Error.Count > 0)
{
throw new Exception("脚本执行失败");
}
runspace.Close();//关闭通信通道
return kk;
}
static void Main(string[] args)
{
string re = RunPowershell(@".\test2.ps1", "");//执行项目中的脚本,注意,脚本ps1文件需要放在源代码DHCP.Test\bin\Debug 文件下
Console.WriteLine(re);
Console.ReadLine();
}
}
}
再接下来是直接执行远程指令的方法,通过Runspace类直接传输字符串型的powershell指令
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Management.Automation.Runspaces;
using System.Management.Automation;
using System.Management;
using System.Collections.ObjectModel;
namespace DHCP.Test
{
class Class1
{
static int ipnum = 0;
static string[] ip1 = new string[100];
//添加方法,需传入各项指定好的参数,MAC地址有点问题,
//各项参数 1.DHCP服务器的总IP 2.作用域的IP 3.保留地址的IP(即电脑的IP)4. 与保留地址对应的MAC地址 5.名称 6.备注
static void AddVlan(string DHCPip, string Vlanip, string ip, string mac, string ComputerName, string Remark)
//旧式执行代码,使用dos端的执行命令,不严谨,有点小瑕疵,无法对流动的保留地址进行更新,也就是活动标识为active的ip地址
{
string KK = "netsh Dhcp Server " + DHCPip + " Scope " + Vlanip + " Add reservedip " + ip + " " + mac + " \"" + ComputerName + "\" \"" + Remark + "\" \"BOTH\"";
string cmd = "$uname=\"123\\123\"\n$pwd = ConvertTo-SecureString -AsPlainText \"123\" -Force\n$cred = New-Object System.Management.Automation.PSCredential($uname,$pwd)\n" + "$a = {" + KK + "}\n" + "Invoke-Command -Credential $cred -command $a -ComputerName 10.10.11.111\n" + "$?";
if (CheckPSVlan(ip) == "False")
{
Console.WriteLine("ip:" + ip + "没有重复,正在添加");
InvokeSystemPS(cmd);
if (CheckPSVlan(ip) == "False")
{
Console.WriteLine("添加失败,mac地址" + mac + "有问题,请重新输入");
}
else
{
Console.WriteLine("已经成功添加ip:" + ip + "到域:" + Vlanip + "下");
}
}
else
{
Console.WriteLine("输入IP:" + ip + "有重复,请重新输入IP");
}
}
static void AddVlan1(string DHCPip, string Vlanip, string ip, string mac, string ComputerName, string Remark)
{
string KK = "netsh Dhcp Server " + DHCPip + " Scope " + Vlanip + " Add reservedip " + ip + " " + mac + " \"" + ComputerName + "\" \"" + Remark + "\" \"BOTH\"";
string cmd = "$uname=\"123\\123\"\n$pwd = ConvertTo-SecureString -AsPlainText \"123\" -Force\n$cred = New-Object System.Management.Automation.PSCredential($uname,$pwd)\n" + "$a = {" + KK + "}\n" + "Invoke-Command -Credential $cred -command $a -ComputerName 10.10.11.111\n" + "$?";
InvokeSystemPS(cmd);
}
static void AddVlanNew(string DHCPip, string Vlanip, string ip, string mac, string ComputerName, string Remark)
//新式的执行代码,使用powershell端的指令,Add方式
{
string str = "add-DhcpServerv4Reservation –ComputerName " + DHCPip + " -ScopeId " + Vlanip + " -IPAddress " + ip + " -ClientId " + mac + " -Name " + ComputerName + " -Description " + "\"" + Remark + "\"";
string cmd = "$uname=\"123\\123\"\n$pwd = ConvertTo-SecureString -AsPlainText \"123\" -Force\n$cred = New-Object System.Management.Automation.PSCredential($uname,$pwd)\n" + "$a = {" + str + "}\n" + "Invoke-Command -Credential $cred -command $a -ComputerName " + DHCPip + "\n" + "$?";
InvokeSystemPS(cmd);
}
static void DeleteVlan(string ip, string mac)//删除方法,IP为保留地址,mac为对应的mac地址,但是随意输入的mac地址也能删除,所以猜测IP才是最主要的
{
string KK1 = "netsh dhcp server 10.10.11.111 scope 10.10.22.0 delete reservedip " + ip + " " + mac;
string cmd1 = "$uname=\"123\\123\"\n$pwd = ConvertTo-SecureString -AsPlainText \"123\" -Force\n$cred = New-Object System.Management.Automation.PSCredential($uname,$pwd)\n" + "$a = {" + KK1 + "}\n" + "Invoke-Command -Credential $cred -command $a -ComputerName 10.10.11.111\n" + "$?";
if (CheckPSVlan(ip) != "False")
{
InvokeSystemPS(cmd1);
Console.WriteLine("IP:" + ip + "已经成功删除");
}
else
{
Console.WriteLine("目标IP:" + ip + "不存在");
}
}
//不仅仅10.10.1.133一个基地
// static string CheckPSVlan(string DHCPIP,string ip)
//正式使用时候备用,传入查询ip的地址和DHCP服务器的IP
static string CheckPSVlan(string ip)//通过GET方法检查有没有重复的IP,如果没有则返回False(注意是字符串,F为大写),即域下面没有存在保留地址
//查询指定IP信息,不过只返回该IP下的IPAddress信息
{
string KK1 = "Get-DhcpServerv4Lease -IPAddress " + ip + " | fl IPAddress";
string cmd1 = "$uname=\"123\\123\"\n$pwd = ConvertTo-SecureString -AsPlainText \"123\" -Force\n$cred = New-Object System.Management.Automation.PSCredential($uname,$pwd)\n" + "$a = {" + KK1 + "}\n" + "Invoke-Command -Credential $cred -command $a -ComputerName 10.10.11.111\n" + "$?";
//如果返回值为False时,则没有对应IP,如果返回其他值则是已经存在重复IP
return InvokeSystemPS(cmd1);
}
public static string InvokeSystemPS(string cmd)//提交方法,将命令传入,打开与powershell交互的工作流,提交命令,并获得返回值
{
string kk = "";
try
{
List<string> ps = new List<string>();
//开启计算机的安全设置,允许执行可能会用到
//开启最高的执行权限
//Unrestricted——允许所有的script运行
ps.Add("Set-ExecutionPolicy RemoteSigned");
ps.Add("Set-ExecutionPolicy -ExecutionPolicy Unrestricted");
ps.Add(cmd);
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
foreach (var scr in ps)
{
pipeline.Commands.AddScript(scr);
}
var test = pipeline.Invoke();//Execute the ps script
foreach (var item in test)
{
//Type typename = item.ImmediateBaseObject.GetType();//获得通道中返回的最原始基类对象
string member = test[0].Members.ElementAt(3).Value.ToString();//返回对象中的第四个属性,保留活动标识,AddressState
string a = member.Substring(0, member.Length);//分隔保留活动标识字符串
//var A = Convert.ChangeType(item.ImmediateBaseObject, typename);//返回指定类型的对象
Console.WriteLine(typename);//打印返回基类对象信息
Console.WriteLine(a);//打印保留活动标识字符串
//Console.WriteLine(item.ToString());//打印从通道流中的返回值信息
//}
foreach (var a in test)
{
kk = a.ToString() + kk;
Console.WriteLine(kk);//打印返回信息
}
runspace.Close();
}
catch (Exception ex)
{
throw ex;
}
return kk;
}
static string CheckPSVlan1(string ip)
//查询指定IP信息,并返回此IP下的所有信息
{
string KK1 = "Get-DhcpServerv4Lease -IPAddress " + ip;
string cmd1 = "$uname=\"123\\123\"\n$pwd = ConvertTo-SecureString -AsPlainText \"123\" -Force\n$cred = New-Object System.Management.Automation.PSCredential($uname,$pwd)\n" + "$a = {" + KK1 + "}\n" + "Invoke-Command -Credential $cred -command $a -ComputerName 10.10.11.111\n" + "$?";
//如果返回值为False时,则没有对应IP,如果返回其他值则是已经存在重复IP
//get 命令,获取服务器中是否存在指定IP,查询语句
return InvokeSystemPS(cmd1);
}
static void Main(string[] args)
{
List<string[]> ip = new List<string[]>();
string[] es = { "10.10.11.111", "10.10.22.0", "10.10.22.127", "4B-99-B6-D4-AE-8D", "测试电脑", "这是一个备注" };
ip.Add(es);
//AddVlanNew(es[0],es[1],es[2],es[3],es[4],es[5]);
CheckPSVlan1("10.10.22.127");
//CheckPSVlan("10.10.22.127");
//AddVlan1(ip[0][0], ip[0][1], ip[0][2], ip[0][3], ip[0][4], ip[0][5]);
//DeleteVlan(ip[0][2],ip[0][3]);
Console.ReadLine();
}
}
}
这两种方法,耗时基本上一致,远程执行指令稍微比脚本执行快上0.6S左右(插入5条数据)
基本耗时都在35-40S之间波动,原因是查询IP信息所花费时间较长,一次查询信息需要大概15S左右的时间,插入仅需要5s左右
还有个需要注意的问题,使用前最好将powershell升级到3.0版本,win7一般自带版本为2.0,2.0版本执行交互命令的时候会出现无法执行的情况,原因是版本低的问题