developer82
developer82

Reputation: 13713

Using .NET framework namespaces and classes

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

Answers (1)

mklement0
mklement0

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

Related Questions