Powershell:使用PS 5类时无法找到类型 [英] Powershell: Unable to find type when using PS 5 classes
问题描述
我正在WinSCP Powershell Assembly中使用PS中的类.在其中一种方法中,我使用的是WinSCP中的各种类型.
I'm using classes in PS with WinSCP Powershell Assembly. In one of the methods I'm using various types from WinSCP.
只要我已经添加了程序集,此方法就可以正常工作-但是,由于Powershell在使用类时读取脚本的方式(我认为是?),因此在加载程序集之前会引发错误.
This works fine as long as I already have the assembly added - however, because of the way Powershell reads the script when using classes (I assume?), an error is thrown before the assembly could be loaded.
实际上,即使我将Write-Host
放在顶部,它也不会加载.
In fact, even if I put a Write-Host
at the top, it will not load.
在解析文件的其余部分之前,是否有任何方法可以强制某些内容运行?
Is there any way of forcing something to run before the rest of the file is parsed?
Transfer() {
$this.Logger = [Logger]::new()
try {
Add-Type -Path $this.Paths.WinSCP
$ConnectionType = $this.FtpSettings.Protocol.ToString()
$SessionOptions = New-Object WinSCP.SessionOptions -Property @{
Protocol = [WinSCP.Protocol]::$ConnectionType
HostName = $this.FtpSettings.Server
UserName = $this.FtpSettings.Username
Password = $this.FtpSettings.Password
}
导致这样的错误:
Protocol = [WinSCP.Protocol]::$ConnectionType
Unable to find type [WinSCP.Protocol].
推荐答案
如您所见, PowerShell拒绝运行包含引用当时不可用(尚未加载)类型的类定义的脚本 strong>-脚本解析阶段失败.
As you've discovered, PowerShell refuses to run scripts that contains class definitions that reference then-unavailable (not-yet-loaded) types - the script parsing stage fails.
- 从PSv5.1开始,即使在脚本顶部的
using assembly
语句在这种情况下也无济于事,因为在您的情况下,该类型是在 PS类定义的上下文中引用的-此可以在PowerShell Core 中修复,但是; 此GitHub问题中正在跟踪所需的工作.
- As of PSv5.1, even a
using assembly
statement at the top of a script does not help in this case, because in your case the type is referenced in the context of a PS class definition - this may get fixed in PowerShell Core, however; the required work is being tracked in this GitHub issue.
正确的解决方案是创建脚本 module (*.psm1
),该脚本的关联清单(*.psd1
)将包含引用类型的程序集声明为 prerequisite > ,通过RequiredAssemblies
键.
The proper solution is to create a script module (*.psm1
) whose associated manifest (*.psd1
) declares the assembly containing the referenced types a prerequisite, via the RequiredAssemblies
key.
如果不能使用模块,请参见底部的替代解决方案.
这是一个简化的演练:
创建测试模块tm
如下:
Create test module tm
as follows:
-
创建模块文件夹
./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
;例如:
Create file ./tm/tm.psm1
with your class definition; e.g.:
class Foo {
# Simply return the full name of the WinSCP type.
[string] Bar() {
return [WinSCP.Protocol].FullName
}
}
注意:在现实世界中,模块通常放置在$env:PSMODULEPATH
中定义的标准位置之一中,因此该模块只能由 name 引用,而无需指定(相对)路径.
Note: In the real world, modules are usually placed in one of the standard locations defined in $env:PSMODULEPATH
, so that the module can be referenced by name only, without needing to specify a (relative) path.
使用模块:
PS> using module ./tm; (New-Object Foo).Bar()
WinSCP.Protocol
using module
语句导入模块,并且-与Import-Module
不同-
还使模块中定义的 class 可用于当前会话.
The using module
statement imports the module and - unlike Import-Module
-
also makes the class defined in the module available to the current session.
由于导入模块隐式加载了WinSCP程序集,这要归功于模块清单中的RequiredAssemblies
键,实例化引用程序集类型的类Foo
成功.
Since importing the module implicitly loaded the WinSCP assembly thanks to the RequiredAssemblies
key in the module manifest, instantiating class Foo
, which references the assembly's types, succeeded.
如果您的用例不允许使用模块,则可以在紧急情况下使用Invoke-Expression
,但是请注意,通常最好避免使用Invoke-Expression
,以确保健壮性和可靠性.为了避免安全风险 [1]
If your use case doesn't allow the use of modules, you can use Invoke-Expression
in a pinch, but note that it's generally better to avoid Invoke-Expression
in the interest of robustness and so as to avoid security risks[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
可以调用存储在字符串中的 any 命令并应用它完全不受您控制的字符串可能会导致执行恶意命令.
此警告类似地适用于其他语言,例如Bash的内置eval
命令.
[1] It's not a concern in this case, but generally, given that Invoke-Expression
can invoke any command stored in a string, applying it to strings not fully under your control can result in the execution of malicious commands.
This caveat applies to other language analogously, such as to Bash's built-in eval
command.
这篇关于Powershell:使用PS 5类时无法找到类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!