【初体验】在dotnot6.0中执行Powershell命令或脚本

一、前言

近期有一个桌面OCR项目依赖了一个python ocr服务,所以想实现在c#代码中通过Powershell脚本来开关这个服务.

二、在c#代码中使用Powershell: Powershell Host App

1. 环境

  • c# console application.
  • dotnet 6.0.
  • 依赖: System.Management.Automation, 版本7.2.5
  • 依赖: Microsoft.PowerShell.SDK, 版本7.2.5

2. 示例代码

参照Microsoft文档中的示例写了一个程序,这个程序执行了一个Get-NetTCPConnection命令来获取已建立的本地商品号为8866的tcp链接.

PowerShell ps = PowerShell.Create();

ps.AddCommand("Get-NetTCPConnection")
    .AddParameter("LocalPort", "8866");
ps.Invoke<CommandInfo>();

运行程序后,报错 Exception thrown: 'System.IO.FileNotFoundException' in System.Private.CoreLib.dll ("Could not load file or assembly 'xxx/System.Management.Automation.resources.dll'. 系统找不到指定的文件。") 。一头雾水,不知道为什么会出现这个错误,google了一下没有找到满意的信息,然后通过nuget上的项目链接进入到Powershell的github主页。之后在issue中搜索System.Management.Automation.resources.dll找到了这个issue.

目前不清楚这些名为xxx.Resources.dll的用途,然后通过在everything中搜索System.Management.Automation.resources.dll,然后发现这些相关dll都存放在全局缓存目录下C:\Windows\Microsoft.NET\assembly\GAC_MSIL下,把这几个相关的xxx.Resources.dll拷贝到buids生成的runtimes/win/lib/6.0目录下再运行就不会再报此错了。但却报了另外一个异常:

System.Management.Automation.CommandNotFoundException
HResult=0x80131501
Message=The 'Get-NetTCPConnection' command was found in the module 'NetTCPIP', but the module could not be loaded. For more information, run 'Import-Module NetTCPIP'.
Source=System.Management.Automation
StackTrace:
at System.Management.Automation.Runspaces.PipelineBase.Invoke(IEnumerable input)
at System.Management.Automation.Runspaces.Pipeline.Invoke()
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](PSDataCollection1 input, PSDataCollection1 output, PSInvocationSettings settings)
at System.Management.Automation.PowerShell.CoreInvoke[TInput,TOutput](PSDataCollection1 input, PSDataCollection1 output, PSInvocationSettings settings)
at System.Management.Automation.PowerShell.Invoke[T](IEnumerable input, IList`1 output, PSInvocationSettings settings)
at System.Management.Automation.PowerShell.InvokeT
at Program.

$(String[] args) in D:\project\ConsoleApp1\ConsoleApp1\Program.cs:line 20
Inner Exception 1:
CmdletInvocationException: File C:\Windows\system32\WindowsPowerShell\v1.0\Modules\NetTCPIP\NetIPConfiguration.psm1 cannot be loaded because running scripts is disabled on this system. For more information, see about_Execution_Policies at https://go.microsoft.com/fwlink/?LinkID=135170.
Inner Exception 2:
PSSecurityException: File C:\Windows\system32\WindowsPowerShell\v1.0\Modules\NetTCPIP\NetIPConfiguration.psm1 cannot be loaded because running scripts is disabled on this system. For more information, see about_Execution_Policies at https://go.microsoft.com/fwlink/?LinkID=135170.
Inner Exception 3:
UnauthorizedAccessException: File C:\Windows\system32\WindowsPowerShell\v1.0\Modules\NetTCPIP\NetIPConfiguration.psm1 cannot be loaded because running scripts is disabled on this system. For more information, see about_Execution_Policies at https://go.microsoft.com/fwlink/?LinkID=135170.

这个异常是由于默认的安全策略阻止了NetTCPIP的导入,然后尝试解决这个问题

// 方法1 直接导入
var ps = PowerShell.Create();
ps.Commands.AddCommand("Import-Module").AddArgument("NetTCPIP")
ps.Invoke()

// 方法2 预加载
InitialSessionState iis = InitialSessionState.CreateDefault();
initialSession.ImportPSModule(new[] { modulePathOrModuleName1, ... });
Runspace runspace = RunspaceFactory.CreateRunspace(iis);
runspace.Open();
RunspaceInvoke invoker = new RunspaceInvoke(runspace);
Collection<PSObject> results = invoker.Invoke("...");

这两种方法因为没有解决安全策略问题,依然会抛出UnauthorizedAccessException异常,通过搜索"InitialSessionState设置执行策略"找到(这篇文章)[https://cloud.tencent.com/developer/article/1595256],最后通过设置AuthorizationManager解决了这人问题

InitialSessionState iss = InitialSessionState.CreateDefault();

// 关键代码
iss.AuthorizationManager = null;

Runspace rs = RunspaceFactory.CreateRunspace(iss);
rs.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = rs;

ps.AddCommand("Get-NetTCPConnection")
    .AddParameter("LocalPort", "8866");
ps.Invoke();

三、总结

本来以为简单的调用powershell的api,但是却出现了这么多问题,解决这些问题花了一下午的时间。由于时间关系,也没有再进一步探索。
最后,要熟悉新的领域或者技术(如powershell),都需要很多时间,希望自己以后更珍惜自己的时间。

以下是遗留的问题:

  • 最终,我也不知道为什么运行时加载xxx.resources.dll时,没有查找GAC_MSILy这个目录.
  • 以异步的方式执行powershell command.
posted @   zkwhatuwant  阅读(232)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
· 面试官:你是如何进行SQL调优的?
点击右上角即可分享
微信分享提示