Reputation: 357
I'm using PowerShell script to run C# code directly in the script. I've run in to an error a particular error a few times. If I make any changes to the C# code in the PowerShell ISE and try to run it again I get the following error.
Add-Type : Cannot add type. The type name 'AlertsOnOff10.onOff' already exists.
At C:\Users\testUser\Desktop\test.ps1:80 char:1
+ Add-Type -TypeDefinition $Source -ReferencedAssemblies $Assem
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (AlertsOnOff10.onOff:String) [Add-Type], Exception
+ FullyQualifiedErrorId : TYPE_ALREADY_EXISTS,Microsoft.PowerShell.Commands.AddTypeCommand
The way I have been resolving this error is by changing the namespace and the command to call the C# method [AlertsOnOff10.onOff]::Main("off")
. I there a way I can prevent this error from happening without having to change namespace and method call?
Upvotes: 15
Views: 28863
Reputation: 16910
An update is in order here.
Any tests on a non-existing namespace, results in an exception and output, that at least AFAIK, cannot be removed even by redirecting errors with *> $null
.
The only way around this, is to wrap the loader in a try{} catch {}
, and assign the result to $null
, like this.
try { $null = ([System.YourClass]).Name; } catch { Add-Type -TypeDefinition $code; }
Upvotes: 0
Reputation: 360
No sure if this meets your requirements but here goes anyway. You can check to see if an assembly is loaded or not and then take action, in the example below I am checking to see if WinSCPnet.dll is already loaded or not:
If ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object Location -Match 'WinSCPnet.dll$')
{
Write-Host 'Loaded'
} Else {
Write-Host 'Not Loaded'
}
Bonus:
If you want to list the types available in the assembly:
([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object Location -Match 'WinSCPnet.dll$').GetTypes()
If you want to list members of a specific type in the assembly:
(([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object Location -Match 'WinSCPnet.dll$').GetTypes() | Where-Object Name -EQ 'SessionOptions').GetMembers() #| Format-Table Name, MemberType
Upvotes: 1
Reputation: 11
Instead of
./YourScript.ps1
use this command
powershell.exe -ExecutionPolicy ByPass -Command "./YourScript.ps1"
Upvotes: 1
Reputation: 294
The simple solution is close Powershell and reopen it. Types you've added with Add-Type will be removed on closing, then run code again.
Upvotes: 1
Reputation: 47792
To my knowledge there is no way to remove a type from a PowerShell session once it has been added.
The (annoying) workaround I would suggest is to write your code in one ISE session, and execute it in a completely different session (separate console window or separate ISE if you want to be able to debug).
This only matters if you're changing $Source
though (actively developing the type definition). If that's not the part that's changing, then ignore the errors, or if it's a terminating error use -ErrorAction
to change it.
Upvotes: 11
Reputation: 158
Adam Furmanek's blog has the simplest and best work around. This goes something like this below. If you want to see how to pass in parameters for that, you can see that https://samtran.me/2020/02/09/execute-c-code-with-parameters-using-powershell/
$id = get-random
$code = @"
using System;
namespace HelloWorld
{
public class Program$id
{
public static void Main(){
Console.WriteLine("Hello world again!");
}
}
}
"@
Add-Type -TypeDefinition $code -Language CSharp
Invoke-Expression "[HelloWorld.Program$id]::Main()"
Upvotes: 4
Reputation: 14295
For those who want to avoid the error or avoid loading the type if it's already been loaded use the following check:
if ("TrustAllCertsPolicy" -as [type]) {} else {
Add-Type "using System.Net;using System.Security.Cryptography.X509Certificates;public class TrustAllCertsPolicy : ICertificatePolicy {public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) {return true;}}"
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
}
I post this because you get the error OP posted if you make even superficial (e.g. formatting) changes to the C# code.
Upvotes: 18
Reputation: 51
You can execute it as a job:
$cmd = {
$code = @'
using System;
namespace MyCode
{
public class Helper
{
public static string FormatText(string message)
{
return "Version 1: " + message;
}
}
}
'@
Add-Type -TypeDefinition $code -PassThru | Out-Null
Write-Output $( [MyCode.Helper]::FormatText("It Works!") )
}
$j = Start-Job -ScriptBlock $cmd
do
{
Receive-Job -Job $j
} while ( $j.State -eq "Running" )
Upvotes: 5