Reputation: 13713
I would like to use a .NET Framework class inside a PowerShell script. I've searched and read that it should be possible. What I have tried is the following:
Add-Type -AssemblyName System.Resources
$resx = New-Object [System.Resources.ResXResourceWriter];
But doing so throws an exception:
New-Object : Cannot find type [[System.Resources.ResXResourceWriter]]: verify that the assembly containing this type is loaded. At C:\temp\Translation\import_translation.ps1:41 char:25 + ... $resx = New-Object [System.Resources.ResXResourceWriter]; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
The assembly does not appear to be found, even though I'm loading it using the Add-Type
command above. How can I use .NET Framework classes in PowerShell?
Upvotes: 2
Views: 512
Reputation: 438263
tl;dr:
Note: In addition to the primary problem shown below, there are two additional problems:
* It is the System.Windows.Forms.dll
assembly that contains the type of interest, so the Add-Type
command should be: Add-Type -AssemblyName System.Windows.Forms
; add -Passthru
to see what types are being loaded.
* Type [System.Resources.ResXResourceWriter]
does not have a parameter-less constructor; run [System.Resources.ResXResourceWriter]::new
(without ()
) to see the available constructor overloads; see bottom section.
Your primary problem:
Instead of:
$resx = New-Object [System.Resources.ResXResourceWriter] # WRONG, due to [...]
use:
$resx = New-Object System.Resources.ResXResourceWriter # OK - no [...]
Alternatively, in PSv5+:
$resx = [System.Resources.ResXResourceWriter]::new() # OK
When you pass [System.Resources.ResXResourceWriter]
as an argument to a command, it is treated verbatim, not as a type literal - and a type whose full name is literally [System.Resources.ResXResourceWriter]
, with the enclosing [
and ]
, doesn't exist.
The reason is that command arguments are parsed in argument mode, where [
has no special meaning as the 1st char. of an argument.
See this answer for an overview of how (unquoted) tokens are parsed in argument mode.
While you can force a token to be interpreted as an expression by enclosing it in (...)
- ([System.Resources.ResXResourceWriter])
, in this case - that just creates extra work, because the type literal is converted back to a string when the value is bound to the New-Object
's -TypeName
parameter.
PowerShell v5 introduced the static ::new()
method that you can call on type literals in expression mode in order to invoke constructors with the method syntax, analogous to how you call methods in C#, as shown above.
Calling ::new
without ()
is also a convenient way to list the available constructor overloads, i.e. to see what constructor variants with what parameters the type supports; e.g.:
Add-Type -AssemblyName System.Windows.Forms
[System.Resources.ResXResourceWriter]::new
yields:
OverloadDefinitions
-------------------
System.Resources.ResXResourceWriter new(string fileName)
System.Resources.ResXResourceWriter new(string fileName, System.Func[type,string] typeNameConverter)
System.Resources.ResXResourceWriter new(System.IO.Stream stream)
System.Resources.ResXResourceWriter new(System.IO.Stream stream, System.Func[type,string] typeNameConverter)
System.Resources.ResXResourceWriter new(System.IO.TextWriter textWriter)
System.Resources.ResXResourceWriter new(System.IO.TextWriter textWriter, System.Func[type,string] typeNameConverter)
For instance, to call the 2nd overload from the list above, you'd using something like (using bogus arguments):
[System.Resources.ResXResourceWriter]::new('c:\tmp\foo', {})
Upvotes: 1