Как вы обнаружили, PowerShell отказывается запускать сценарии, содержащие определения классов, которые ссылаются на недоступные на тот момент (еще не загруженные) типы - этап синтаксического анализа сценария не выполняется.
Правильным решением является создание скрипта module (*.psm1
), связанный манифест (*.psd1
) объявляет сборку, содержащую указанные типы, как предварительное условие через клавишу RequiredAssemblies
.
См. альтернативное решение внизу, если использование модулей не вариант.
Вот упрощенное пошаговое руководство:
Создайте тестовый модуль tm
следующим образом:
Создайте папку модуля ./tm
и в ней манифест (*.psd1
):
# Create module folder
mkdir ./tm
# Create manifest file that declares the WinSCP assembly a prerequisite.
# Modify the path to the assembly as needed; you may specify a relative path, but
# note that the path must not contain variable references (e.g., $HOME).
New-ModuleManifest ./tm/tm.psd1 -RootModule tm.psm1 `
-RequiredAssemblies C:\path\to\WinSCPnet.dll
Создайте файл модуля скрипта (*.psm1
) в папке модуля:
Создайте файл ./tm/tm.psm1
с определением вашего класса; например.:
class Foo {
# Simply return the full name of the WinSCP type.
[string] Bar() {
return [WinSCP.Protocol].FullName
}
}
Примечание. В реальном мире модули обычно размещаются в одном из стандартных мест, определенных в $env:PSMODULEPATH
, так что на модуль можно ссылаться только по имени, без необходимости указывать (относительный) путь.
Используйте модуль:
PS> using module ./tm; (New-Object Foo).Bar()
WinSCP.Protocol
Оператор using module
импортирует модуль и, в отличие от Import-Module
, также делает класс, определенный в модуле, доступным для текущего сеанса.
Поскольку при импорте модуля сборка WinSCP неявно загружалась благодаря ключу RequiredAssemblies
в манифесте модуля, создание экземпляра класса Foo
, который ссылается на типы сборки, завершилось успешно.
Если ваш вариант использования не позволяет использовать модули, вы можете использовать Invoke-Expression
в крайнем случае, но учтите, что обычно лучше избегать Invoke-Expression
в интересах надежности и во избежание рисков для безопасности < sup> [1].
# Adjust this path as needed.
Add-Type -LiteralPath C:\path\to\WinSCPnet.dll
# By placing the class definition in a string that is invoked at *runtime*
# via Invoke-Expression, *after* the WinSCP assembly has been loaded, the
# class definition succeeds.
Invoke-Expression @'
class Foo {
# Simply return the full name of the WinSCP type.
[string] Bar() {
return [WinSCP.Protocol].FullName
}
}
'@
(New-Object Foo).Bar()
[1] Это не проблема в этом случае, но в целом, учитывая, что Invoke-Expression
может вызывать любую команду, хранящуюся в строке, применяя ее к строкам не полностью под вашим контролем может привести к выполнению вредоносных команд. Это предостережение аналогично применимо и к другим языкам, например к встроенной в Bash команде eval
.
person
mklement0
schedule
16.03.2017