我正在尝试从 C# 调用 Add-AppxPackage
cmdlet。我在 running PowerShell from C# code 上找到了 MSDN 文章.我引用了 System.Management.Automation 程序集并尝试了以下代码片段,所有这些代码片段在尝试调用 powerShell.Invoke() 时都会导致相同的异常:
System.Management.Automation.CommandNotFoundException was unhandled
The term 'Add-AppxPackage' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
片段 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 命令提示符手动运行此命令,它会正常工作。
知道我在这里做错了什么吗?任何建议表示赞赏。
-- 更新--
我找到了 this post和 this 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。
回答
如果您关注 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)中,导致并非所有 PSModulePaths 都被加载。卸载该扩展后,所有模块都已正确加载,我不再需要手动导入模块。
最佳答案
在 PowerShell 中,终止错误(停止执行)和非终止错误(仅写入错误流)之间存在差异。
如果您想在自己的函数中创建非终止错误,只需使用 Write-Error
命令。如果要创建终止错误,请使用 Throw
关键词。如果运行 Get-Help Write-Error
,您可以阅读有关这些概念的更多信息, Get-Help about_Throw
和 Get-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);
}
}
关于c# - 从 C# 调用 PowerShell cmdlet 会引发 CommandNotFoundException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22488780/