C#调用PowerShell WSFC FailoverCluster模块加载失败问题咨询
由于没有集成方法或API可直接访问Windows Server故障转移集群(WSFC)功能,我尝试调用PowerShell cmdlet获取WSFC状态返回给C#程序,用于健康检查。
方案1:通过System.Diagnostics.Process调用PowerShell(可正常运行)
该方案通过启动powershell.exe执行Get-ClusterResource命令,能正常获取到包含目标对象的PowerShell控制台输出,但返回结果仅为纯字符串格式,代码如下:
System.Diagnostics.ProcessStartInfo processInfo = new System.Diagnostics.ProcessStartInfo(); processInfo.FileName = @"powershell.exe"; processInfo.Arguments = @"& {Get-ClusterResource -Name '"+ _wsfcResourceName +"'}"; processInfo.Verb = "runas"; processInfo.RedirectStandardError = true; processInfo.RedirectStandardOutput = true; processInfo.UseShellExecute = false; processInfo.CreateNoWindow = true; System.Diagnostics.Process process = new System.Diagnostics.Process(); process.StartInfo = processInfo; process.Start(); var stdout = process.StandardOutput.ReadToEnd(); var stderror = process.StandardError.ReadToEnd();
方案2:通过System.Management.Automation.Runspace调用(报错)
改用Runspace方式实现的代码如下:
System.Management.Automation.Runspaces.InitialSessionState initialSessionState = System.Management.Automation.Runspaces.InitialSessionState.CreateDefault(); initialSessionState.ImportPSModule(new[] { "FailoverClusters"}); initialSessionState.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Unrestricted; using System.Management.Automation.Runspaces.Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(initialSessionState); runspace.Open(); using (PowerShell PowerShellInst = PowerShell.Create(runspace)) { PowerShellInst.AddCommand($@"Get-ClusterResource"); PowerShellInst.AddParameter("Name", _wsfcResourceName); // 以下写法也返回相同结果 //PowerShellInst.AddScript($@"Get-ClusterResource -Name '{_wsfcResourceName}'"); //PowerShellInst.AddScript($@"Import-Module FailoverClusters; Get-ClusterResource -Name '{_wsfcResourceName}'"); Collection<PSObject> PSOutput = PowerShellInst.Invoke(); var rsrcResults = PSOutput.ToList(); // ...其余代码 }
程序从PowerShell层抛出异常:
The 'Get-ClusterResource' command was found in the module 'FailoverClusters', but the module could not be loaded. Fore more information, run 'Import-Module FailoverClusters'.
当前程序运行在IIS Web应用中,我已经尝试过以下解决方法,均无效:
- 单独执行
Import-Module FailoverClusters -Force -Verbose命令,未返回报错 - 在Runspaces.InitialSessionState中指定
initialSessionState.ImportPSModule(new[] { "FailoverClusters"}); - 将处理该逻辑的类库设置为32位运行
- 调整了数十种不同的代码写法
请问是否有人可以提供解决建议?
我将代码调整为如下形式:
PowerShellInst.Commands.AddCommand("Import-Module").AddArgument(@"FailoverClusters"); PowerShellInst.Commands.AddParameter("SkipEditionCheck"); PowerShellInst.Commands.AddParameter("Force"); PowerShellInst.Commands.AddParameter("Verbose");
调用Collection<PSObject> PSOutput = PowerShellInst.Invoke();时,出现了新的异常:
System.Management.Automation.CmdletInvocationException: Could not load type 'System.Diagnostics.Eventing.EventDescriptor' from assembly 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. ---> System.TypeLoadException: Could not load type 'System.Diagnostics.Eventing.EventDescriptor' from assembly 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. at Microsoft.FailoverClusters.UI.Common.TraceWriterEtw.TraceCmdletEnter(String cmdletName, String invocation, String parameters, UInt32 sequence, String source, String tag) at Microsoft.FailoverClusters.UI.Common.TraceCmdlet.Starting() at Microsoft.FailoverClusters.UI.Common.TraceSampleBase.Start() at Microsoft.FailoverClusters.UI.Common.ClusterLog.StartTraceCmdlet(TraceSource source, String cmdletName, String invocation, String parameters) at Microsoft.FailoverClusters.PowerShell.FCCmdlet.ProcessRecord() at Microsoft.FailoverClusters.PowerShell.ClusterPipeCmdlet.ProcessRecord() at System.Management.Automation.CommandProcessor.ProcessRecord() --- End of inner exception stack trace --- at System.Management.Automation.Runspaces.PipelineBase.Invoke(IEnumerable input) at System.Management.Automation.PowerShell.Worker.ConstructPipelineAndDoWork(Runspace rs, Boolean performSyncInvoke) at System.Management.Automation.PowerShell.Worker.CreateRunspaceIfNeededAndDoWork(Runspace rsToUse, Boolean isSync) at System.Management.Automation.PowerShell.CoreInvokeHelper[TInput,TOutput](PSDataCollection`1 input, PSDataCollection`1 output, PSInvocationSettings settings) at System.Management.Automation.PowerShell.CoreInvoke[TInput,TOutput](PSDataCollection`1 input, PSDataCollection`1 output, PSInvocationSettings settings) at System.Management.Automation.PowerShell.Invoke(IEnumerable input, PSInvocationSettings settings)
目前我还不清楚该异常的含义。
"Import-Module errors out when it attempts to load the module
with an error message stating that Import-Module was not able to load the
.NET type System.Diagnostics.Eventing.EventDescriptor. Since the EventDescriptor
type is not available in .NET Core, you cannot use the ServerManager module natively.
The only alternatives are for the Server Manager team to update their code,
or for you to run the cmdlet in Windows PowerShell (either explicitly or via the compatibility mechanism)"
这段内容解释了第一种方案可以正常运行的原因,但也让我回到了最初的问题:有没有办法将process.StandardOutput.ReadToEnd()拿到的输出转换为结构化对象?
内容的提问来源于stack exchange,提问作者Sputnikk23

