微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

从 C# 调用时无法识别 Powershell 命令

如何解决从 C# 调用时无法识别 Powershell 命令

这是此处 Question 的延续,我创建了一个 PowerShell 命令,并且能够在 PowerShell 窗口中调用该命令,但是当尝试从 C# 方法调用时,出现错误由于无法识别 cmdlet,我尝试使用其他现有命令并得到相同的错误,因此我怀疑导入模块中存在问题,尽管我没有在流中收到该错误错误。我得到的唯一错误是“Get-RowAndPartitionKey 不是可识别的 cmndlt,请检查拼写.....”。

想知道是否还有其他方法,我应该尝试一下,或者我是否可以在这里调试更多以查看我的模块是否获取所有命令。现在我不知道如何解决这个问题。

 public string RunScript( string contentScript,Dictionary<string,EntityProperty> parameters )
    {
        List<string> parameterList = new List<string>();
        foreach( var item in parameters )
        {
            parameterList.Add( item.Value.ToString() );
        }
        using( PowerShell ps = PowerShell.Create() )
                       
        {
            IAsyncResult async =
             ps.AddCommand( "Import-Module" ).AddArgument( @"C:\Users\...\.D.PowerShell.dll" )
               .AddStatement()
               .AddCommand( "Get-RowAndPartitionKey" ).AddParameter( "Properties","test" )
               .BeginInvoke();

            StringBuilder stringBuilder = new StringBuilder();
            foreach( PSObject result in ps.EndInvoke( async ) )
            {
                stringBuilder.AppendLine( result.ToString() );
            }
            return stringBuilder.ToString();
        }
    }
}

以下方法不会在 Streams.Error 或 Verbose 中返回任何错误,但也没有输出

public async Task<IEnumerable<object>> RunScript( string scriptContents,List<string> scriptParameters )
        {
            // create a new hosted PowerShell instance using the default runspace.
            // wrap in a using statement to ensure resources are cleaned up.

            using( PowerShell ps = PowerShell.Create() )
            {
                // specify the script code to run.
                ps.AddScript( scriptContents );

                // specify the parameters to pass into the script.
                ps.AddParameter( "Properties","test") ;

                // execute the script and await the result.
                var pipelineObjects = await ps.InvokeAsync().ConfigureAwait( false );                
                return pipelineObjects;
            }
        }

脚本内容

 "\"$path = 'C:\\Users...\\.TabularData.PowerShell.dll'\\r\\nImport-Module $path\\r\\nGet-RowAndPartitionKeys\""
    

解决方法

以下是自包含PowerShell示例代码使用按需编译 C# 代码

  • 它表明该方法在原则上有效,如 this answer 中针对您原始问题的描述。

    • 先决条件:调用自定义 Get-RowAndPartitionKey" cmdlet 的 C# 项目中使用的 PowerShell SDK package 和 .NET 运行时必须与 PowerShell SDK 和 .NET 兼容用于编译包含该 cmdlet 的程序集 DLL 的运行时,通过 Import-Module 导入。

    • 下面的示例代码通过直接从 PowerShell 运行,使用 Add-Type cmdlet 按需编译 C# 代码,从而确保隐式地运行 - 它适用于 Windows PowerShell 以及 PowerShell (Core) 7+ .

      • 在实践中,我发现 .NET Framework 编译的 DLL(来自 Windows PowerShell)也适用于 PowerShell (Core) (.NET (Core) 5.0),但反之则不行。
  • 展示了故障排除技术,即:

    • -Verbose 开关添加到 Import-Module 调用以生成详细输出,列出从给定模块 (DLL) 导入的命令。
    • 打印这些详细信息(查找 // --- TROUBLESHOOTING CODE
    • 打印发生的任何非终止性 PowerShell 错误(与您必须在 C# 代码中处理的异常相反)。
# Create a (temporary) assembly containing cmdlet "Get-RowAndPartitionKey".
# This assembly can directly be imported as a module from PowerShell.
# The cmdlet simply outputs "Hi from Get-RowAndPartitionKey" and
# echoes the elements of the list passed to -Properties,one by one.
$tempModuleDll = Join-Path ([IO.Path]::GetTempPath()) "TempModule_$PID.dll"
Remove-Item -ErrorAction Ignore $tempModuleDll
Add-Type @'
  using System.Management.Automation;
  using System.Collections.Generic;
  [Cmdlet("Get","RowAndPartitionKey")]
  public class GetRowAndPartitionKeyCmdlet : PSCmdlet {
    [Parameter] public List<string> Properties { get; set; }
    protected override void ProcessRecord() {
      WriteObject("Hi from Get-RowAndPartitionKey: ");
      WriteObject(Properties,true);
    }
  }
'@ -ErrorAction Stop -OutputAssembly $tempModuleDll

# Compile a C# class ad hoc to simulate your project,and call its static
# method,which imports the module and effectively calls 
#   Get-RowAndPartitionKey -Properties "foo","bar"
(Add-Type @"
  using System;
  using System.Management.Automation;
  using System.Collections.Generic;
  using System.Text;

  public static class Foo {
    public static string RunScript(List<string> parameterList)
    {
      using (System.Management.Automation.PowerShell ps = PowerShell.Create())
      {
        IAsyncResult async =
          // Add -Verbose to the Import-Module call,so that the list of 
          // commands being imported is written to the verbose output stream.
          ps.AddCommand("Import-Module").AddArgument(@"$tempModuleDll").AddParameter("Verbose",true)
            .AddStatement()
            .AddCommand("Get-RowAndPartitionKey").AddParameter("Properties",parameterList)
            .BeginInvoke();

        StringBuilder stringBuilder = new StringBuilder();
        foreach (PSObject result in ps.EndInvoke(async))
        {
          stringBuilder.AppendLine(result.ToString());
        }
        
        // --- TROUBLESHOOTING CODE

        // Print verbose output from the Import-Module call
        foreach (var v in ps.Streams.Verbose) { Console.WriteLine("VERBOSE: " + v.ToString()); }

        // Print any errors.
        foreach (var e in ps.Streams.Error) { Console.WriteLine("ERROR: " + e.ToString()); }

        // ---

        return stringBuilder.ToString();
      }
    }
  }
"@ -ErrorAction Stop -PassThru)::RunScript(("foo","bar"))

# Clean-up instructions:
if ($env:OS -eq 'Windows_NT') {
  Write-Verbose -vb "NOTE: Re-running this code requires you to start a NEW SESSION."
  Write-Verbose -vb "After exiting this session,you can delete the temporary module DLL(s) with:`n`n  Remove-Item $($tempModuleDll -replace '_.+','_*.dll')`n "
} else {
  Write-Verbose -vb "NOTE: Re-running this code after modifying the embedded C# code requires you to start a NEW SESSION."
  Remove-Item $tempModuleDll
}

在我的 Windows 10 机器上,无论是 PowerShell (Core) 7.0.5 还是 Windows PowerShell 5.1,上述结果(省略了清理说明)如下,表明一切都按预期工作:

VERBOSE: Loading module from path 'C:\Users\jdoe\AppData\Local\Temp\TempModule_11876.dll'.
VERBOSE: Importing cmdlet 'Get-RowAndPartitionKey'.
Hi from Get-RowAndPartitionKey:
foo
bar

具体来说,第 VERBOSE: Importing cmdlet 'Get-RowAndPartitionKey'. 行表示自定义 cmdlet 已成功导入会话。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?