c# - 从 C# 调用 PowerShell cmdlet 会引发 CommandNotFoundException

标签 c# powershell invoke

我正在尝试从 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 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。

回答

如果您关注 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_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);
    }
}

关于c# - 从 C# 调用 PowerShell cmdlet 会引发 CommandNotFoundException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22488780/

相关文章:

C#比较两个集合的更有效方法

powershell - Powershell永久映射的驱动器不会保存下次登录的凭据

立即调用 Javascript 函数

sql-server - 无法获取 ManagedComputer 的 smo 对象

c# - 跨线程操作无效 : Control accessed from a thread other than the thread it was created on

vb.net - VB.NET 中的 Me.Invoke 实际上并不是 "Invoke"- 线程在 Invoke 语句上停滞

c# - 窗口关闭时正在运行的任务会发生什么情况?

c# - F# 类型到 Json 正在输出 Name@ 和 Name

c# - 如何在 C# 中将 Xml 转换为 Json,反之亦然

python - 使用python和pywinrm从linux通过ssl连接到powershell