2017-03-16 43 views
3

我在PS中使用WinSCP Powershell Assembly中的類。在我使用WinSCP的各種類型的方法之一中。Powershell:使用PS 5類時找不到類型

只要我已經添加了程序集,這個工作正常 - 但是,由於Powershell在使用類(我假設?)時讀取腳本的方式,在程序集加載之前會拋出一個錯誤。

事實上,即使我把一個寫主機放在頂端,它也不會加載。

解析文件的其餘部分之前是否有強制某些東西運行的方法?

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 
     } 

協議= [WinSCP.Protocol] :: $ ConnectionType

找不到類型[WinSCP.Protocol]。

+1

你在哪裏加載你的WinSCP DLL? –

+0

它是try塊中的第一行。但它沒有關係,我加載它。即使我將cmdlet放在最上面的一行,並且有WinSCPnet.dll的直接路徑,它也不會加載 - 在運行任何東西之前,它會檢測到丟失的類型。 – dbso

回答

5

正如您發現的那樣,PowerShell拒絕運行包含引用then-unavailable(尚未加載)類型的類定義的腳本 - 腳本解析階段失敗。

  • 作爲PSv5.1的,即使在一個腳本在這種情況下沒有幫助,因爲在你的情況下,該類型是在一個PS類定義的背景下引用的頂部using assembly聲明 - 這may get fixed in v6, however

適當的解決方案是創建腳本模塊*.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定義的標準位置之一,從而使模塊可以通過名稱僅被引用,而無需指定(相對)路徑。

使用模塊

> using module ./tm; (New-Object Foo).Bar() 
WinSCP.Protocol 

using module語句導入模塊和 - 與Import-Module - 也使得可用於當前會話的模塊中定義。

由於導入模塊隱式加載WinSCP程序集,這要歸功於模塊清單中的RequiredAssemblies鍵,實例化引用程序集類型的類Foo成功。


如果你的使用情況不允許使用的模塊的,你可以在緊要關頭使用Invoke-Expression,但要注意,它通常是更好地避免在穩健的利益Invoke-Expression等,以避免安全風險[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命令。

1

儘管它本身並不是解決方案,但我還是圍繞這個解決方案工作的。但是,由於它仍然存在,所以我將讓問題保持開放狀態。

而不是使用WinSCP類型,我只是使用字符串。看到,因爲我已經有相同的那些WinSCP.Protocol

Enum Protocols { 
    Sftp 
    Ftp 
    Ftps 
} 

並在FtpSettings

$FtpSettings.Protocol = [Protocols]::Sftp 

我可以設置協議這樣

$SessionOptions = New-Object WinSCP.SessionOptions -Property @{ 
      Protocol = $this.FtpSettings.Protocol.ToString() 
      HostName = $this.FtpSettings.Server 
      UserName = $this.FtpSettings.Username 
      Password = $this.FtpSettings.Password 
     } 

我使用了類似的設置協議enumerals上[WinSCP.TransferMode]

$TransferOptions.TransferMode = "Binary" #[WinSCP.TransferMode]::Binary