#c# #powershell
#c# #powershell
Вопрос:
У меня есть следующее struct
и class
как часть модуля скрипта PS. Эта часть кода написана на C # и обрабатывает код для запуска при импорте модуля в сеанс PowerShell, хотя OnImport()
биты, которые я здесь не включил. Это не все, что делает код C #, достаточно, чтобы показать проблему, с которой я сталкиваюсь:
Add-Type -IgnoreWarnings @"
using System;
using System.Reflection;
using System.Management.Automation;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Runtime.Versioning;
namespace MyPowerShell {
public struct State {
public readonly static ICertificatePolicy OriginalSystemCertificatePolicy = ServicePointManager.CertificatePolicy;
public readonly static ICertificatePolicy SkipSSLCheckPolicy;
public readonly static Version PSVersion;
public readonly static string Framework;
static State() {
PSVersion = new Version(
$($PSVersionTable.PSVersion.Major),
$($PSVersionTable.PSVersion.Minor),
$($PSVersionTable.PSVersion.Build),
$($PSVersionTable.PSVersion.Revision)
);
OriginalSystemCertificatePolicy = ServicePointManager.CertificatePolicy;
SkipSSLCheckPolicy = new TrustAllCertsPolicy();
}
}
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
}
"@
Это работает нормально, и я могу ссылаться на struct
свойства и class
штрафовать PowerShell после Add-Type
завершения:
# Each of these returns the information expected and I can instantiate TrustAllCertsPolicy
[MyPowerShell.State]::PSVersion
[MyPowerShell.State]::OriginalSystemCertificatePolicy
[MyPowerShell.State]::SkipSSLCheckPolicy
$policy = [MyPowerShell.TrustAllCertsPolicy]::new()
Но я попытался добавить другой способ определения текущей версии PowerShell из кода, который не зависит от расширения $PSVersionTable.PSVersion
свойств в источнике here string
, немного изменив struct
определение:
public struct State {
public readonly static ICertificatePolicy OriginalSystemCertificatePolicy = ServicePointManager.CertificatePolicy;
public readonly static ICertificatePolicy SkipSSLCheckPolicy;
public readonly static Version PSVersion;
public readonly static string Framework; # Added this line
static State() {
PSVersion = new Version(
$($PSVersionTable.PSVersion.Major),
$($PSVersionTable.PSVersion.Minor),
$($PSVersionTable.PSVersion.Build),
$($PSVersionTable.PSVersion.Revision)
);
OriginalSystemCertificatePolicy = ServicePointManager.CertificatePolicy;
SkipSSLCheckPolicy = new TrustAllCertsPolicy();
# Added the following 2 lines
TargetFrameworkAttribute attribute = Attribute.GetCustomAttribute(Assembly.GetEntryAssembly(), typeof(TargetFrameworkAttribute)) as TargetFrameworkAttribute;
Framework = attribute.FrameworkName;
}
// TrustAllCertsPolicy Code
}
Как только я добавляю код для извлечения FrameworkName
атрибута из сборки записи в статическом конструкторе struct
, он все равно компилируется, но я больше не могу ссылаться ни на одно из свойств в struct
all, что приводит к ошибке конвейера:
[MyPowerShell.State]::PSVersion
An error occurred while creating the pipeline.
CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException
FullyQualifiedErrorId : RuntimeException
Однако вместо этого я могу использовать следующие вызовы для set Framework
, используя AssemblyProductAttribute
вместо этого вызывающую сборку, и у меня нет проблем:
static State() {
PSVersion = new Version(
$($PSVersionTable.PSVersion.Major),
$($PSVersionTable.PSVersion.Minor),
$($PSVersionTable.PSVersion.Build),
$($PSVersionTable.PSVersion.Revision)
);
OriginalSystemCertificatePolicy = ServicePointManager.CertificatePolicy;
SkipSSLCheckPolicy = new TrustAllCertsPolicy();
// Replaced the next two lines with this
AssemblyProductAttribute productAttr = Attribute.GetCustomAttribute(Assembly.GetCallingAssembly(), typeof(AssemblyProductAttribute)) as AssemblyProductAttribute;
Framework = productAttr.Product;
}
Почему мой struct
становится непригодным для использования из PowerShell, когда я пытаюсь получить TargetFrameworkAttribute
из сборки ввода, но отлично работает при получении AssemblyProductAttribute
из вызывающей сборки вместо этого?
Комментарии:
1.
[System.Reflection.Assembly]::GetEntryAssembly()
возвращает,null
посколькуpowershell.exe
это собственный PE, а не управляемая сборка2. Забавно. Прекрасно, что ошибка, которую я получил, была настолько описательной. Есть идеи, почему этот вызов отлично работает из ядра PowerShell?
pwsh.exe
Не является также неуправляемой сборкой?3. Nvm — похоже, в ядре PS входная сборка является DLL, а не исполняемым файлом. Думаю, это объясняет это.
4. Если вы укажете это в качестве ответа, я приму это
Ответ №1:
Если вы проверите базовое исключение, вы обнаружите, что причиной является нулевая ссылка, передаваемая Attribute.GetCustomAttribute()
в статическом конструкторе.
PS ~> $Error[0].GetBaseException() |Format-List -Force
Message : Value cannot be null.
Parameter name: element
ParamName : element
Data : {}
InnerException :
TargetSite : System.Attribute[] GetCustomAttributes(System.Reflection.Assembly, System.Type,
Boolean)
StackTrace : at System.Attribute.GetCustomAttributes(Assembly element, Type attributeType,
Boolean inherit)
at System.Attribute.GetCustomAttribute(Assembly element, Type attributeType,
Boolean inherit)
at State..cctor()
HelpLink :
Source : mscorlib
HResult : -2147467261
Вызов Assembly.GetEntryAssembly()
возвращает null
, потому что хостинговое приложение на самом деле не является управляемой сборкой — powershell.exe
это собственный Win32 PE.
Комментарии:
1. Спасибо, не подумал попробовать проверить базовое исключение. Очень раздражает, что реальная ошибка была скрыта.
2. FWIW мне все еще неясно, почему вы пытаетесь перенести проверку версии на C # в модуле сценария — не
if($PSVersionTable.PSVersion.Major -lt 6){ Add-Type $PS5VersionOfState }else{ Add-Type $PSCoreVersionOfState }
было бы проще в корневом модуле или скрипте?3. Я имею опыт работы со сценариями, но это мой первый модуль PS, который я сделал. Я понял около 30 минут назад, что неправильно подхожу к этому. Мне не нужно ничего выгружать на C #, кроме одного определения класса, которое я могу сделать с помощью класса PowerShell вместо этого.