从C#调用PowerShell cmdlet会抛出CommandNotFoundException

时间:2014-03-18 19:08:24

标签: c# powershell invoke

我正在尝试从C#调用Add-AppxPackage cmdlet。我在running PowerShell from C# code上找到了MSDN文章。我引用了System.Management.Automation程序集并尝试了以下代码片段,所有这些代码片段在尝试调用powerShell.Invoke()时都会导致相同的异常:

  

System.Management.Automation.CommandNotFoundException未处理

     

术语“Add-AppxPackage”无法识别为cmdlet的名称,   功能,脚本文件或可操作程序。检查拼写   名称,或者如果包含路径,请验证路径是否正确   再试一次。

摘录1:

var powerShell = PowerShell.Create();
powerShell.AddCommand(string.Format("Add-AppxPackage '{0}'", appxFilePath));

foreach (PSObject result in powerShell.Invoke())
{
    Console.WriteLine(result);
}

我理解为什么这不起作用,因为我不应该在AddCommand()函数中提供参数。

摘录2:

var powerShell = PowerShell.Create();
powerShell.AddCommand("Add-AppxPackage");
powerShell.AddParameter("Path", appxFilePath);

foreach (PSObject result in powerShell.Invoke())
{
    Console.WriteLine(result);
}

摘录3:

var powerShell = PowerShell.Create();
powerShell.AddCommand("Add-AppxPackage");
powerShell.AddArgument(appxFilePath);

foreach (PSObject result in powerShell.Invoke())
{
    Console.WriteLine(result);
}

我的C#项目目标.Net 4.5,如果我执行powerShell.AddCommand(“Get-Host”),它可以工作,它返回的版本是4.0。在PowerShell的v3.0中添加了Add-AppxPackage,因此命令应该存在,如果我从Windows PowerShell命令提示符手动运行此命令,它可以正常工作。

我在这里做错了什么想法?任何建议都表示赞赏。

- 更新 -

我找到了this postthis one,并意识到有一个AddScript()函数,所以我尝试了这个:

摘录4:

var powerShell = PowerShell.Create();
powerShell.AddScript(string.Format("Add-AppxPackage '{0}'", appxFilePath));

var results = powerShell.Invoke();
foreach (PSObject result in results)
{
    Console.WriteLine(result);
}

它没有抛出异常,但它也没有安装metro应用程序,并且从powerShell.Invoke()返回的“结果”是空的,所以我仍然不知所措......

- 更新2 -

所以我决定尝试创建一个新的PowerShell进程来运行我的命令,所以我尝试了这个:

Process.Start(new ProcessStartInfo("PowerShell", string.Format("-Command Add-AppxPackage '{0}'; $key = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyUp')", appxFilePath)));

但它仍然会抛出Add-AppxPackage不是可识别的cmdlet的相同错误。

ANSWER

如果您按照robert.westerlund的回答中的长评论主题进行操作,您将看到由于某些原因从Visual Studio运行/启动时,PowerShell不包括从PowerShell命令提示符直接运行时所执行的所有PSModulePaths ,这么多模块都不存在。解决方案是使用:

找到我需要的模块的绝对路径(在我的情况下是appx模块)
(Get-Module appx -ListAvailable).Path

然后在尝试调用其中一个cmdlet之前导入该模块。所以这是适合我的C#代码:

var powerShell = PowerShell.Create();
powerShell.AddScript(string.Format(@"Import-Module 'C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\Appx\Appx.psd1'; Add-AppxPackage '{0}'", appxFilePath));
var results = powerShell.Invoke();

更新的答案

您可以从this other post I opened看到,问题出在Visual Studio扩展中的错误(在我的案例中是StudioShell),导致并非所有PSModulePath都被加载。卸载该扩展后,所有模块都已正确加载,我不再需要手动导入模块。

1 个答案:

答案 0 :(得分:7)

在PowerShell中,终止错误(停止执行)和非终止错误(仅写入错误流)之间存在差异。

如果要在自己的函数中创建非终止错误,只需使用Write-Error cmdlet。如果要创建终止错误,请使用Throw关键字。如果您运行Get-Help Write-ErrorGet-Help about_ThrowGet-Help about_Try_Catch_Finally,则可以详细了解这些概念。

Add-AppxPackage与非现有包一起使用是一个非终止错误,因此将写入错误流,但不会抛出执行暂停异常。以下代码尝试添加非现有包,然后将错误写入控制台。

var powerShell = PowerShell.Create();
powerShell.AddScript("Add-AppxPackage NonExistingPackageName");

// Terminating errors will be thrown as exceptions when calling the Invoke method. 
// If we want to handle terminating errors, we should place the Invoke call inside a try-catch block.
var results = powerShell.Invoke();

// To check if a non terminating error has occurred, test the HadErrors property
if (powerShell.HadErrors)
{
    // The documentation for the Error property states that "The command invoked by the PowerShell 
    // object writes information to this stream whenever a nonterminating error occurs."
    foreach (var error in powerShell.Streams.Error)
    {
        Console.WriteLine("Error: " + error);
    }
}
else
{
    foreach(var package in results)
    {
        Console.WriteLine(package);
    }
}