【问题标题】:Calling PowerShell cmdlet from C# throws CommandNotFoundException从 C# 调用 PowerShell cmdlet 会引发 CommandNotFoundException
【发布时间】:2014-03-28 02:49:23
【问题描述】:

我正在尝试从 C# 调用 Add-AppxPackage cmdlet。我在 running PowerShell from C# code 上找到了 MSDN 文章。我已经引用了 System.Management.Automation 程序集并尝试了以下代码 sn-ps,所有这些在尝试调用 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。 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)中的一个错误,导致并非所有 PSModulePath 都被加载。卸载该扩展后,所有模块都已正确加载,我不再需要手动导入模块。

【问题讨论】:

    标签: c# powershell invoke


    【解决方案1】:

    在 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);
        }
    }
    

    【讨论】:

    • 您应该使用它来读取您在调用Add-AppxPackage 函数时最有可能收到的非终止错误。
    • 感谢代码,但看起来这是一个终止错误; powerShell.Invoke() 会抛出一个 CommandNotFoundException,因此此后它永远不会遇到任何代码(除非我将它包装在 try-catch 中)。如果我确实将它包装在 try-catch 中,则 powerShell.HadErrors 为真,但 powerShell.Streams.Error 为空。问题不在于 .appx 文件不存在,而是它无法识别 Add-AppxPackage 命令,即使它使用的是具有此命令的 PowerShell v4.0。
    • 在您的Snippet 4 中,您提到没有引发异常但没有安装应用程序。那是我基于样本的 sn-p。有什么改变;您现在是否也在那个 sn-p 中收到异常?
    • 啊,好的。我刚刚使用 Snippet 4 中的代码再次运行它,你是对的;当我以这种方式运行它时,它是一个非终止错误(即没有抛出异常)。 powerShell.Streams.Error 包含相同的错误消息,但 Add-AppxPackage 不是可识别的 cmdlet。
    • 如果它确实存在,那么问题可能是你没有正确的环境变量可用?你能比较一下(Get-Item Env:\PSModulePath).Value -Split ";"的结果吗?此外,您能否尝试使用绝对路径加载 AppX 模块(我认为它应该位于 Import-Module C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\Appx\Appx.psd1,可以通过在 PowerShell 命令提示符中执行 (get-module appx -ListAvailable).Path 找到该路径,您可以在其中找到相关模块)和看看行不行?
    猜你喜欢
    • 2014-01-23
    • 2013-06-08
    • 1970-01-01
    • 2018-12-17
    • 2014-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    相关资源
    最近更新 更多