如何解决从 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 举报,一经查实,本站将立刻删除。