12

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?

zingwing
  • 317
  • 2
  • 3
  • 12
  • 1
    I would suggest using VS and ex. a console application to develop and debug assemblies. PowerShell can use managed code, but it's not designed to be used as a replacement for VS(althought it's a great supplement) – Frode F. Sep 08 '14 at 18:54
  • Could this help? Of course if your type definition changes it won't help, but it might be a workaround if you are not planning to redefine the type: http://stackoverflow.com/questions/16552801/how-do-i-conditionally-add-a-class-with-add-type-typedefinition-if-it-isnt-add – oatsoda Nov 03 '15 at 08:54

6 Answers6

17

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.

Marc
  • 13,011
  • 11
  • 78
  • 98
  • 2
    I found a general solution. If you add "-namespace system" at the end of the add-type statement then you can always check the existence of the type via "[type]myTypeName". Even if the added type has no class. Without this the new type ends up as [Microsoft.PowerShell.Commands.AddType.AutoGeneratedTypes.myTypeName]. – Carsten Jul 29 '22 at 07:37
11

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.

briantist
  • 45,546
  • 6
  • 82
  • 127
  • 1
    From my understanding, if I close my ISE session I won't run into this error. Is that correct? – zingwing Sep 08 '14 at 19:06
  • While running ISE changes are not lost. So a variable `$wazzup` will persist after running a particular piece of code or script. – Matt Sep 08 '14 at 19:30
  • You can use `Remove-Variable` to get rid of a defiend variable, and you can clear other run-time objects. But this is not true of types. – briantist Sep 08 '14 at 19:33
  • 3
    This issue isn't a PowerShell limitation so much as it is a .NET limitation. There's no supported way to remove assemblies once they have been loaded. – Keith Hill Sep 08 '14 at 21:22
4

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" )
Jeff D
  • 41
  • 1
3

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()"
Sam Tran
  • 148
  • 1
  • 3
  • 1
    This will lead to your code unnecessarily recompiling every time with the old results piling up in memory. – c z Nov 14 '22 at 13:24
1

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.

David Morrow
  • 262
  • 4
  • 9
0

Instead of

./YourScript.ps1

use this command

powershell.exe -ExecutionPolicy ByPass -Command "./YourScript.ps1"
Guest
  • 1
  • 1
  • Might you please [edit] your answer and explain a little how it resolves the problem in the question? As discussed in [Explaining entirely code-based answers](https://meta.stackoverflow.com/q/392712/3744182), it's a lot more helpful to people if your answer also explain why its code works. Thanks! – dbc Dec 10 '22 at 19:49