在脚本模块导入期间从 C# class 评估当前 PowerShell 会话版本
Evaluate current PowerShell session version from C# class during script module import
首先,介绍一下我正在尝试做的事情的一些背景知识。我正在编写一个 PowerShell 脚本模块,需要在导入模块时设置一些变量和 运行 一些代码。最终需要做什么取决于要导入模块的 PowerShell 版本。例如,这需要支持 5.1 及更高版本,但是 Invoke-RestMethod
在 PowerShell 6 之前不支持
-SkipCertificateCheck
参数,这意味着我需要检查版本,创建一个 dummy ICertificatePolicy 在验证时基本上总是 returns true
,并将其存储在某个全局位置,我可以引用它并设置该策略,如果用户指示他们希望在调用某些 cmdlet 时跳过安全检查。到目前为止我有以下代码:
Add-Type @"
using System.Management.Automation;
using System.Net;
using System.Security.Cryptography.X509Certificates;
namespace MyModuleNamespace {
public struct State {
public static ICertificatePolicy SystemCertificatePolicy;
public static ICertificatePolicy SkipSSLCheckPolicy;
}
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
}
namespace MyModuleNamespace.SubNamespace {
public class ModuleAssemblyInitializer : IModuleAssemblyInitializer {
public void OnImport() {
// pseudocode of what I'm struggling to define in C#
if( CurrentPSSession.PSVersion.Major -lt 6 ) {
MyModuleNamespace.State.SystemCertificatePolicy = ServicePointManager.CertificatePolicy;
MyModuleNamespace.State.SkipSSLCheckPolicy = new MyModuleNamespace.TrustAllCertsPolicy();
}
// end pseudocode
}
}
}
"@
但是...我正在努力评估来自当前 PowerShell 会话的信息,例如评估 $PSVersionTable.PSVersion
。我知道如何检查安装了哪些版本的 PowerShell,但我需要能够评估添加了此 class 的当前 运行ning 版本的 PowerShell。此外,我不希望 entire 模块用 C# 编写,即使这在技术上是一个选项。我团队中的其他人需要维护该模块,虽然他们非常了解 PowerShell,但 C# 不在他们的技能范围内。
如果有一种 PowerShell 本地方法来处理导入模块时 运行ning 代码的情况,我会洗耳恭听。这是我的第一个 PowerShell 模块,如果我用错误的解决方案来解决这个问题也不会感到惊讶。
一般来说,我强烈建议不要这样做。盲目跳过证书信任验证是个坏主意。
正如我(不好地)试图在评论中解释的那样,如果您解析调用程序集(即 System.Management.Automation.dll
),您可以使用它来使用反射在初始化程序中发现版本信息:
using System;
using System.Management.Automation;
using System.Reflection;
namespace CrossVersionModule
{
public class ModuleAssemblyInitializer : IModuleAssemblyInitializer
{
public void OnImport()
{
// obtain reference to calling assembly
var caller = Assembly.GetCallingAssembly();
// locate the PSVersionInfo type
var psversionType = caller.GetType("System.Management.Automation.PSVersionInfo");
if (null != psversionType)
{
// locate the PSVersion property
PropertyInfo versionProp = psversionType.GetProperty("PSVersion");
if (null == versionProp) {
// In .NET framework (ie. Windows PowerShell 5.1) we have to explicitly pass binding flags for non-public members
versionProp = psversionType.GetProperty("PSVersion", BindingFlags.NonPublic | BindingFlags.Static);
}
// Invoke the getter to get the value
var getter = versionProp.GetGetMethod(true);
var version = getter.Invoke(null, new object[] { });
Console.WriteLine(version); // here's where you'd do your version check
}
}
}
}
如果我针对 PowerShell Standard 编译以上内容,我在分别导入 5.1 和 7 时得到正确的 PowerShell 版本
除了 Mathias 的 .
之外,我发现了一些能够确定 .NET 版本的方法
一个是扩展字符串中的表达式,并将以下代码添加到我的 struct State
以将 $PSVersionTable.PSVersion
中的值插入到 C# 代码中,然后可以在模块代码:
public struct State {
public static ICertificatePolicy SystemCertificatePolicy = ServicePointManager.CertificatePolicy;
public static ICertificatePolicy SkipSSLCheckPolicy;
// Lines below were added
public readonly static System.Version PSVersion;
static State() {
PSVersion = new Version(
$($PSVersionTable.PSVersion.Major),
$($PSVersionTable.PSVersion.Minor),
$($PSVersionTable.PSVersion.Build),
$($PSVersionTable.PSVersion.Revision)
);
}
}
另一种可行的方法是检查调用程序集的产品属性的值,这可以 return 调用程序集上 AssemblyProductAttribute
的一些事情之一:
- 如果直接从 PowerShell 调用
Microsoft® .NET Framework
如果使用 Windows PowerShell
Microsoft® .NET Core
如果使用 PowerShell Core
- 如果使用
Add-Type
从在 PowerShell 脚本中编译的 C# 调用
Microsoft (R) Windows (R) Operating System
如果在 .NET Framework 上
PowerShell
如果使用 .NET Core
- 当针对 .NET Core SDK 编译时,不确定这 return 来自正确编译的 C# 项目的内容
在我的例子中,我可以检查属性是否等于 PowerShell
以查看这是否是 .NET Core:
// New using directives
using System;
using System.Reflection;
// Check if .NET Core
AssemblyProductAttribute attribute = Attribute.GetCustomAttribute(Assembly.GetCallingAssembly(), typeof(AssemblyProductAttribute)) as AssemblyProductAttribute;
if(attribute.Product == "PowerShell")) {
// do .NET Core things
}
似乎第三种方法在运行时有一些问题,我已将其从这个答案中删除,直到我弄清楚发生了什么。
首先,介绍一下我正在尝试做的事情的一些背景知识。我正在编写一个 PowerShell 脚本模块,需要在导入模块时设置一些变量和 运行 一些代码。最终需要做什么取决于要导入模块的 PowerShell 版本。例如,这需要支持 5.1 及更高版本,但是 Invoke-RestMethod
在 PowerShell 6 之前不支持 -SkipCertificateCheck
参数,这意味着我需要检查版本,创建一个 dummy ICertificatePolicy 在验证时基本上总是 returns true
,并将其存储在某个全局位置,我可以引用它并设置该策略,如果用户指示他们希望在调用某些 cmdlet 时跳过安全检查。到目前为止我有以下代码:
Add-Type @"
using System.Management.Automation;
using System.Net;
using System.Security.Cryptography.X509Certificates;
namespace MyModuleNamespace {
public struct State {
public static ICertificatePolicy SystemCertificatePolicy;
public static ICertificatePolicy SkipSSLCheckPolicy;
}
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
}
namespace MyModuleNamespace.SubNamespace {
public class ModuleAssemblyInitializer : IModuleAssemblyInitializer {
public void OnImport() {
// pseudocode of what I'm struggling to define in C#
if( CurrentPSSession.PSVersion.Major -lt 6 ) {
MyModuleNamespace.State.SystemCertificatePolicy = ServicePointManager.CertificatePolicy;
MyModuleNamespace.State.SkipSSLCheckPolicy = new MyModuleNamespace.TrustAllCertsPolicy();
}
// end pseudocode
}
}
}
"@
但是...我正在努力评估来自当前 PowerShell 会话的信息,例如评估 $PSVersionTable.PSVersion
。我知道如何检查安装了哪些版本的 PowerShell,但我需要能够评估添加了此 class 的当前 运行ning 版本的 PowerShell。此外,我不希望 entire 模块用 C# 编写,即使这在技术上是一个选项。我团队中的其他人需要维护该模块,虽然他们非常了解 PowerShell,但 C# 不在他们的技能范围内。
如果有一种 PowerShell 本地方法来处理导入模块时 运行ning 代码的情况,我会洗耳恭听。这是我的第一个 PowerShell 模块,如果我用错误的解决方案来解决这个问题也不会感到惊讶。
一般来说,我强烈建议不要这样做。盲目跳过证书信任验证是个坏主意。
正如我(不好地)试图在评论中解释的那样,如果您解析调用程序集(即 System.Management.Automation.dll
),您可以使用它来使用反射在初始化程序中发现版本信息:
using System;
using System.Management.Automation;
using System.Reflection;
namespace CrossVersionModule
{
public class ModuleAssemblyInitializer : IModuleAssemblyInitializer
{
public void OnImport()
{
// obtain reference to calling assembly
var caller = Assembly.GetCallingAssembly();
// locate the PSVersionInfo type
var psversionType = caller.GetType("System.Management.Automation.PSVersionInfo");
if (null != psversionType)
{
// locate the PSVersion property
PropertyInfo versionProp = psversionType.GetProperty("PSVersion");
if (null == versionProp) {
// In .NET framework (ie. Windows PowerShell 5.1) we have to explicitly pass binding flags for non-public members
versionProp = psversionType.GetProperty("PSVersion", BindingFlags.NonPublic | BindingFlags.Static);
}
// Invoke the getter to get the value
var getter = versionProp.GetGetMethod(true);
var version = getter.Invoke(null, new object[] { });
Console.WriteLine(version); // here's where you'd do your version check
}
}
}
}
如果我针对 PowerShell Standard 编译以上内容,我在分别导入 5.1 和 7 时得到正确的 PowerShell 版本
除了 Mathias 的
一个是扩展字符串中的表达式,并将以下代码添加到我的 struct State
以将 $PSVersionTable.PSVersion
中的值插入到 C# 代码中,然后可以在模块代码:
public struct State {
public static ICertificatePolicy SystemCertificatePolicy = ServicePointManager.CertificatePolicy;
public static ICertificatePolicy SkipSSLCheckPolicy;
// Lines below were added
public readonly static System.Version PSVersion;
static State() {
PSVersion = new Version(
$($PSVersionTable.PSVersion.Major),
$($PSVersionTable.PSVersion.Minor),
$($PSVersionTable.PSVersion.Build),
$($PSVersionTable.PSVersion.Revision)
);
}
}
另一种可行的方法是检查调用程序集的产品属性的值,这可以 return 调用程序集上 AssemblyProductAttribute
的一些事情之一:
- 如果直接从 PowerShell 调用
Microsoft® .NET Framework
如果使用 Windows PowerShellMicrosoft® .NET Core
如果使用 PowerShell Core
- 如果使用
Add-Type
从在 PowerShell 脚本中编译的 C# 调用Microsoft (R) Windows (R) Operating System
如果在 .NET Framework 上PowerShell
如果使用 .NET Core
- 当针对 .NET Core SDK 编译时,不确定这 return 来自正确编译的 C# 项目的内容
在我的例子中,我可以检查属性是否等于 PowerShell
以查看这是否是 .NET Core:
// New using directives
using System;
using System.Reflection;
// Check if .NET Core
AssemblyProductAttribute attribute = Attribute.GetCustomAttribute(Assembly.GetCallingAssembly(), typeof(AssemblyProductAttribute)) as AssemblyProductAttribute;
if(attribute.Product == "PowerShell")) {
// do .NET Core things
}
似乎第三种方法在运行时有一些问题,我已将其从这个答案中删除,直到我弄清楚发生了什么。