[void] CreateSession() {
try {
# Load WinSCP .NET assembly
Add-Type -Path (Join-Path $PSScriptRoot "\winscp\WinSCPnet.dll")
# Setup session options
$this.sessionOptions = New-Object WinSCP.SessionOptions -Property @{
Protocol = [WinSCP.Protocol]::Sftp
In the above section of code I encounter the "TypeNotFound" error message regarding "[WinSCP.Protocol]".
14 | Protocol = [WinSCP.Protocol]::Sftp
| ~~~~~~~~~~~~~~~
| Unable to find type [WinSCP.Protocol].
The .dll file can load correctly, I have verified this previously. I know what is happening is that the PowerShell "parser" is throwing an error because it doesn't recognize the WinSCP library at load time. I have tried adding a module and manifest, but I cannot find a simple example of how to do this properly. Also, it doesn't matter if I'm running PowerShell 5.x or 7.x. All I am wanting is to load this DLL so I can use classes/functions from it. Why is loading a DLL so hard in PowerShell?
What do I need to do to get this WinSCP DLL to load at runtime and not throw an error?
A very similar question was asked on this site a couple years ago by someone, but there are no answers to it.
I am looking for a real example for how to load a DLL file into a script. The linked question does not appropriately do that. Why do I need to create a manifest module thing to import the DLL?
tl;dr:
The problem stems from trying to reference the just-loaded WinSCP types in a class
definition, via type literals, such as [WinSCP.Protocol]
, as explained below.
The problem can be bypassed by not using class
es at all, and using functions instead.
I am looking for a real example for how to load a DLL file into a script.
Add-Type
-Path
/ -LiteralPath
, as shown in your code does just that:
It loads the specified .NET assembly and makes its public types available in the calling session, just like the similar using assembly
statement.
However, since you're using a class
definition
attempting to reference a type from an assembly you are loading from the same script file via a type literal (e.g, [WinSCP.Protocol]
), the class definition fails:
At script-file parse time, all types being referenced by a class
definition as type literals (e.g. [WinSCP.Protocol]
) - whether as property types, in the body of methods, or as a base class / interface to implement (though in the latter case the type literal has no [...]
enclosure; e.g. class Foo : WinSCP.Protocol { ... }
) -
must already have been loaded into the session, as of PowerShell 7.3.6.[1]
using assembly
statement was green-lit in 2017, but hasn't been implemented as of this writing: see GitHub issue #3641.Workarounds:
This answer offers two solutions:
Invoke-Expression
-based solution.This answer offers a simple two-script solution:
class
definition based on the dependent assembly's types. A workaround isn't always needed, namely if you avoid use of type literals, such as [WinSCP.Protocol]
With respect to [WinSCP.SessionOptions]
, you're already doing that by using New-Object
WinSCP.SessionOptions
instead of the more modern (PSv5+) [WinSCP.SessionOptions]::new()
You can also avoid it for the [WinSCP.Protocol]::Sftp
enumeration value by simply using a string - 'Sftp'
instead - at least for the code snippet shown this would solve your problem; here's a simplified example:
class Foo {
# Note: Do NOT use [WinSCP.SessionOptions] here.
[object] $sessionOptions
[void] CreateSession() {
# Load WinSCP .NET assembly
Add-Type -LiteralPath (Join-Path $PSScriptRoot "\winscp\WinSCPnet.dll")
# Set up session options
# Note the use of *string* 'Sftp' in lieu of [WinSCP.Protocol]::Sftp
$this.sessionOptions = New-Object WinSCP.SessionOptions -Property @{
Protocol = 'Sftp'
}
}
}
Now you can instantiate [Foo]
as you normally would - either with New-Object Foo
or, preferable with [Foo]::new()
(once [Foo]
itself is successfully defined, it's fine to refer to it by a type literal, outside class
definitions).
[1] Classes were a relatively late addition to the PowerShell language, and, unfortunately, there are still many problems to be worked out - see the list of pending issues in GitHub issue #6652. Note, however, that feature parity with, say, C# classes was never the aim.