从c#调用PowerShell cmdlet会抛出CommandNotFoundException异常

本文关键字:CommandNotFoundException 异常 cmdlet 调用 PowerShell | 更新日期: 2023-09-27 17:50:47

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

System.Management.Automation。CommandNotFoundException未处理

术语'Add-AppxPackage'不被识别为cmlet的名称,函数、脚本文件或可操作程序。检查单词的拼写名称,或者如果包含路径,则验证路径是否正确再试一次。

片段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。Add-AppxPackage是在PowerShell v3.0中添加的,所以这个命令应该是存在的,如果我在Windows PowerShell命令提示符中手动运行这个命令,它就会正常工作。

你知道我在这里做错了什么吗?欢迎提出任何建议。

—Update—

我发现这篇文章和这一个,并意识到有一个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()返回的"结果"是空的,所以我仍然不知所措…

—Update 2—

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

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

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

如果你关注罗伯特的长评论线程。韦斯特朗德的回答,你会看到,由于某种原因,当运行/从Visual Studio启动,PowerShell不包括所有的PSModulePaths,当它直接从PowerShell命令提示符运行时,所以许多模块不存在。解决方案是找到我需要的模块(在我的例子中是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();

更新答案

你可以从我打开的另一篇文章中看到,这个问题是Visual Studio扩展(在我的情况下是StudioShell)中的一个bug,导致不是所有的PSModulePaths都被加载。卸载扩展后,所有模块都正确加载,我不再需要手动导入模块。

从c#调用PowerShell cmdlet会抛出CommandNotFoundException异常

在PowerShell中,终止错误(停止执行)和非终止错误(只写入错误流)是有区别的。

如果您想在自己的函数中创建一个非终止错误,只需使用Write-Error cmdlet。如果要创建终止错误,请使用Throw关键字。如果您运行Get-Help Write-Error, Get-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);
    }
}
相关文章:
  • 没有找到相关文章