C# 中未获取 Powershell 结果集

Posted

技术标签:

【中文标题】C# 中未获取 Powershell 结果集【英文标题】:Powershell resultset not being picked up in C# 【发布时间】:2017-05-14 07:41:05 【问题描述】:

单击 button1 时,将执行以下代码,该代码将运行 PowerShell 脚本以获取当前的 SQL Server 实例。但是,当它运行时,结果集(结果变量)从 PowerShell 输出中计数为 0 行。当我在本机 PowerShell 中运行相同的代码时,它会显示 3 行的实例名称。

如果我遗漏了什么,谁能告诉我?

private void button1_Click(object sender, EventArgs e)

    //If the logPath exists, delete the file
    string logPath = "Output.Log";
    if (File.Exists(logPath))
    
        File.Delete(logPath);
    
    string[] Servers = richTextBox1.Text.Split('\n');

    //Pass each server name from the listview to the 'Server' variable
    foreach (string Server in Servers)
    
        //PowerShell Script
        string PSScript = @"
        param([Parameter(Mandatory = $true, ValueFromPipeline = $true)][string] $server)

        Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned -Force;
        Import-Module SQLServer;
        Try 
        
            Set-Location SQLServer:\\SQL\\$server -ErrorAction Stop; 
            Get-ChildItem | Select-Object -ExpandProperty Name;
         
        Catch 
        
            echo 'No SQL Server Instances'; 
        
        ";

        //Create PowerShell Instance
        PowerShell psInstance = PowerShell.Create();

        //Add PowerShell Script
        psInstance.AddScript(PSScript);

        //Pass the Server variable in to the $server parameter within the PS script
        psInstance.AddParameter("server", Server);

        //Execute Script
        Collection<PSObject> results = new Collection<PSObject>();
        try
        
            results = psInstance.Invoke();
        
        catch (Exception ex)
        
            results.Add(new PSObject((Object)ex.Message));
        

        //Loop through each of the results in the PowerShell window
        foreach (PSObject result in results)
        
           File.AppendAllText(logPath, result + Environment.NewLine);
           // listBox1.Items.Add(result);
        
        psInstance.Dispose();
    

【问题讨论】:

这是 Windows 窗体应用程序还是其他什么?如果有别的,是什么? 是的。我已经添加了 WinForms 标签,谢谢。 检查 PowerShell 对象的 HadErrors 属性。如果是真的,您需要阅读Streams.Error 属性并检查错误。 CmdLet 可能已写入非终止错误。 HadErrors 属性在调用 powershell 脚本时返回 false @TravisEz13 代码看起来基本正确。我更新它以根据我使用$server 输入的路径执行Get-ChildItem,代码将输出写入output.log。你从哪里得到SQLServer 模块?如果 SQL 附带 SQL 是什么版本的 SQL?是否需要安装选项才能获得它?你得到的是No SQL Server Instances 还是零行? 【参考方案1】:

为了得到一个可能的 PowerShell 错误,我会尝试 sth。像这样:

private void button1_Click(object sender, EventArgs e)
        
            //If the logPath exists, delete the file
            string logPath = "Output.Log";
            if (File.Exists(logPath)) 
                File.Delete(logPath);
            

            string[] Servers = richTextBox1.Text.Split('\n');

            //Pass each server name from the listview to the 'Server' variable
            foreach (string Server in Servers) 
                //PowerShell Script
                string PSScript = @"
            param([Parameter(Mandatory = $true, ValueFromPipeline = $true)][string] $server)

            Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned -Force;
            Import-Module SQLServer;
            Try 
            
                Set-Location SQLServer:\\SQL\\$server -ErrorAction Stop; 
                Get-ChildItem | Select-Object -ExpandProperty Name;
             
            Catch 
            
                echo 'No SQL Server Instances'; 
            
            ";
                using (PowerShell psInstance = PowerShell.Create())                                
                    psInstance.AddScript(PSScript);
                    psInstance.AddParameter("server", Server);
                    Collection<PSObject> results = psInstance.Invoke();
                    if (psInstance.Streams.Error.Count > 0) 
                        foreach (var errorRecord in psInstance.Streams.Error) 
                            MessageBox.Show(errorRecord.ToString());
                        
                                   
                    foreach (PSObject result in results) 
                        File.AppendAllText(logPath, result + Environment.NewLine);
                        // listBox1.Items.Add(result);
                                   
                

            
        

【讨论】:

更仔细地查看代码,该代码捕获所有错误,然后使用echowrite-output 的别名)在出现错误时输出字符串。所以大多数错误实际上会导致结果,而不是错误。另外,在写这条评论之前,有一条评论说HadErrors属性是假的。【参考方案2】:

它不起作用的原因是psInstance.AddParameter 仅向命令添加参数,它不适用于脚本。 You'll need to find another way of getting the $server parameter into the script. 试试这两个 powershell 示例,看看我的意思。第一个将输出所有进程(忽略 AddParameter),而第二个仅显示 svchost 进程。

1)

$ps = [system.management.automation.powershell]::create()
$ps.AddScript("get-process")
$ps.AddParameter("name","svchost")
$ps.invoke()

2)

$ps = [system.management.automation.powershell]::create()
$ps.AddCommand("get-process")
$ps.AddParameter("name","svchost")
$ps.invoke()

【讨论】:

当我将 Powershell 代码更改为 Try echo $server; 时,它会返回预期的变量,所以这不是问题。它确实没有问题地传递参数【参考方案3】:

我设法通过使用 Win32_service 而不是 SQLPS 解决了这个问题。

Param([Parameter(Mandatory = $true, ValueFromPipeline = $true)][string] $server)

$localInstances = @()
[array]$captions = GWMI Win32_Service -ComputerName $server | ?$_.Name -match 'mssql *' -and $_.PathName -match 'sqlservr.exe' | %$_.Caption

ForEach($caption in $captions)

   if ($caption -eq 'MSSQLSERVER') 
   
       $localInstances += 'MSSQLSERVER'
   
   else 
   
       $temp = $caption | %$_.split(' ')[-1] | %$_.trimStart('(') | %$_.trimEnd(')')
       $localInstances += ""$server\$temp""
   

$localInstances;

【讨论】:

以上是关于C# 中未获取 Powershell 结果集的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中无法获取 PowerShell 命令的结果

如何使用 PowerShell 和 SQLCMD 获取存储过程返回值?

C#利用反射动态调用DLL并返回结果,和获取程序集的信息

查询结果设置为变量 - powershell

正则表达式(C#)中未终止的[]集[重复]

C# 通过DataSet 获取SQL 存储过程返回的多个结果集(tables)