Powershell - Missing System.Collections.Generic in C:\windows\assembly\GAC_MSIL - powershell

I'm trying to use this code in Powershell:
Add-Type -AssemblyName "System.Collections.Generic"
However I get this error:
Add-Type : Cannot add type. One or more required assemblies are missing.
I've looked in C:\windows\assembly\GAC_MSIL and can see that there is no folder named System.Collections.Generic.
Do I need to download this library, if so where?

There is no System.Collections.Generic assembly. That is a namespace. A large portion of the types in that namespace are in the core library assembly, mscorlib.dll which is already available in Powershell since Powershell is .Net.
Go to MSDN for the namespace, find the type you are trying to use, and you can see the assembly it is in, and remember that there is not necessarily a one to one relationship between assemblies and namespaces.
Using generic types is a bit involved in Powershell and can depend on using reflection, or formatting complex type names.
See this stackoverflow question for some more details.

Never mind,
not sure why that doesn't work, but turns out I don't need it anyway.
I had this code which for some reason wasn't working intially because it couldn't find [Collections.Generic.List[String]], but now it seems to work:
[string[]] $csvUserInfo = #([IO.File]::ReadAllLines($script:EmailListCsvFile))
[Collections.Generic.List[String]]$x = $csvUserInfo

I would answer by another question, Why do you need this assembly ?
Can you try to replace your code by :
$x = [IO.File]::ReadAllLines($script:EmailListCsvFile)
In PowerShell it should work.

Related

How can I import/load a .dll file to use in a PowerShell script without getting "TypeNotFound" error?

[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?
Note on possible duplicates
A very similar question was asked on this site a couple years ago by someone, but there are no answers to it.
Note on suggested duplicate
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 classes 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 must already have been loaded into the session, as of PowerShell 7.3.1.[1]
Removing this counterintuitive requirement for the using assembly statement was green-lit in 2017, but hasn't been implemented as of this writing: see GitHub issue #3641.
This applies equally to referencing types via type literals in the class body (e.g. [WinSCP.Protocol]) and deriving a class from another type, including implementing an interface (e.g. class Foo : WinSCP.Protocol { ... }).
Workarounds:
This answer offers two solutions:
A (multi-file) module-based solution.
A (suboptimal) Invoke-Expression-based solution.
This answer offers a simple two-script solution:
One script that loads the dependent assembly first, and then dot-sources another that contains the 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.
The easiest solution, and the one I have resolved to use, is to just use functions and not classes in PowerShell.
Just use functions, not classes in PowerShell.

How can I load a dll in PowerShell using two script approach? [duplicate]

[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?
Note on possible duplicates
A very similar question was asked on this site a couple years ago by someone, but there are no answers to it.
Note on suggested duplicate
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 classes 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 must already have been loaded into the session, as of PowerShell 7.3.1.[1]
Removing this counterintuitive requirement for the using assembly statement was green-lit in 2017, but hasn't been implemented as of this writing: see GitHub issue #3641.
This applies equally to referencing types via type literals in the class body (e.g. [WinSCP.Protocol]) and deriving a class from another type, including implementing an interface (e.g. class Foo : WinSCP.Protocol { ... }).
Workarounds:
This answer offers two solutions:
A (multi-file) module-based solution.
A (suboptimal) Invoke-Expression-based solution.
This answer offers a simple two-script solution:
One script that loads the dependent assembly first, and then dot-sources another that contains the 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.
The easiest solution, and the one I have resolved to use, is to just use functions and not classes in PowerShell.
Just use functions, not classes in PowerShell.

Documenting Powershell modules and scripts

With Powershell 5 introducing OOP Classes support, the traditional comment-based Powershell documentation methods for functions, scripts and modules are no longer a good fit. Get-Help is not presenting any help for classes, methods or properties and it looks like it will be staying that way. Other than that, Get-Help is not much of help when trying to find information on a specific function without actually having the module or powershell script in question.
As classes are especially useful for more complex Powershell projects, the need for an up-to-date documentation is more pressing than ever. Projects like Doxygen and the Sandcastle Help File Builder do support help generation for a number of OO-languages, but do not seem to be able to handle Powershell code. A quick look at the PoshBuild project reveals that it is targeted at .NET language projects, too and needs to be integrated into the Visual Studio build process, which pure-Powershell code does not have.
There is also PSDoc capable of generating documentation for modules in HTML or markdown formats based on Get-Help output, which would have been pretty much what I want if it supported classes.
So how do I auto-generate sensible documentation if I have
.ps1 scripts
.psm1 modules
classes in my Powershell code
using the comment-based help documentation syntax?
#trebleCode still deserves the answer, I'm just posting this for anyone interested.
I started trying to answer this question a while ago but got distracted and never finished. If I recall correctly, there was some discussion I found on Github where they said they didn't plan on supporting comment annotated classes, which is sad because I like Powershell Comments.
My thought here was that by calling the builtin help methods you could create a helper function that would detect these non-standard comments above the class keyword and convert them to comment objects without invoking get-help. These comments could also be stored in external files.
Below I found the code for parsing comments into objects and creating comment objects in code.
# References:
# https://learn-powershell.net/2015/08/07/invoking-private-static-methods-using-powershell/
# https://stackoverflow.com/questions/1259222/how-to-access-internal-class-using-reflection
# https://stackoverflow.com/questions/15652656/get-return-value-after-invoking-a-method-from-dll-using-reflection
# https://github.com/PowerShell/PowerShell/blob/a8627b83e5cea71c3576871eacad7f2b19826d53/src/System.Management.Automation/help/HelpCommentsParser.cs
$ExampleComment = #"
<#
.SYNOPSIS
This was a triumph
#>
"#
$CommentLines = [Collections.Generic.List`1[String]]::new()
$InvokeArgs = #($ExampleComment, $CommentLines)
# GetMethod Filter
$BindingFlags = 'static','nonpublic','instance'
# GetMethod Filter: We need to specify overloaded methods by their parameters
$ParamTypes = [Type]::GetTypeArray($InvokeArgs)
$ParamCount = [System.Reflection.ParameterModifier]::new(2)
$HelpParser = [psobject].Assembly.GetType('System.Management.Automation.HelpCommentsParser')
$CollectCommentText = $HelpParser.GetMethod('CollectCommentText', $BindingFlags, $null, $ParamTypes, $ParamCount)
# Extension methods aren't part of the class so null gets called first.
# TODO: Figure out return value
$CollectCommentText.Invoke($Null,$InvokeArgs)
$InvokeArgs
# Comment object but properties are read only.
$CommentHelp = [System.Management.Automation.Language.CommentHelpInfo]::new()
$CommentHelp.Synopsis
$CommentHelp.Description
$CommentHelp.Examples
$CommentHelp

Types in PowerShell 2 modules

I've implemented a small PowerShell module which brings a custom type with it. I defined the type in the .psm1 file as a C# class and added it with Add-Type. Now, when I add the module and remove it again, the type is still there which probably isn't quite right (it prevents re-adding the module, for example). The documentation for Remove-Module states that types defined in assemblies loaded by the module are unloaded as well. But my module doesn't bring in an assembly, just a tiny-ish single type in source code form.
I could just put the type into its own DLL and mark it as an assembly to load in the module manifest, but I like how currently all the source code is readily visible. Distributing a DLL with the module might just raise suspicion why it needs an executable file.
Is there something I can hook onto to remove the type somehow when unloading the module? Or should I just ignore potential errors with Add-Type to at least being able to re-add the module once removed from a session? I'd rather avoid putting a DLL in there (probably overkill anyway for that tiny module).
The docs on Remove-Module also say that the assembly is not unloaded. This is a fundamental issue with .NET and the CLR. Once an assembly is loaded into an AppDomain it can't be unloaded. So creating your own DLL (managed assembly) isn't going to help.
I'm not sure there is much you can do here short of avoiding Add-Type and creating your custom type using new-object psobject -prop #{...} and $obj.psobject.typenames.insert(0, 'newtypename').

Powershell in SQLCLR?

In the past I've been able to embed a sripting languate (like JScript) inside the SQLCLR, so scripts can be passed as parameters of functions, to perform certain calculations. Here is a simplistic example (the function ssScriptExecute returns a concatenation of all the print's in the script):
select dbo.ssScriptExecute( 'print("Calculation: "+(1+2/3) );' )
-- Calculation: 1.6666666666666665
I'd love to be able to embed a Powershell runtime in the same way. But I've had all sort of problems because the runtime tries to find assemblies by path, and there are no paths inside the SQlCLR. I'm happy to provide more information on the errors I get, but I was wondering if anybody has tried this!
Thanks!
I use il code injection to modified System.Automation.Management.
make variable version in GetPSVersionTable() be "2.0"
then i can run Powershell Code in SQL Server.
Be sure reference this modified dll in your visual studio project.
http://www.box.net/shared/57122v6erv9ss3aopq7p
btw, automated registering all dll you needed with running powershell in SQL
you can use this ps1 code
http://www.box.net/shared/tdlpu1875clsu8azxq4b
I think the only way to do this is to create a WCF service hosting powershell, and let SQLCLR send the request dbo.ssScriptExecute(...) to that service for execution.
Besides from that, I've also successfully embedded paxScript.net in the SQLCLR (an interpreter that does not have the memory leak problems of the DLR languages).
I thought SQLCLR was restricted to just a certain set of assemblies and PS Automation is not one of them.