Question

I have this console type thing that accepts a line of C# code, wraps it up in some surrounding code and compiles it into an assembly. Then, I invoke the method from that assembly, output the result and that's it.

The problem is, the assembly needs to have a name so I can set it as a friend assembly so it could access the non-public classes. I named it "console".

Everything worked as expected but the problem is that I can't run a second script after one has finished because the file named "console" already exists in the directory and can't be overwritten.

I've tried Disposing of everything that has a Dispose method. I've tried manually deleting the file with File.Delete. Nothing helped.

So here's the code I'm using. I hope someone can help me.

CSharpCodeProvider provider = new CSharpCodeProvider();
var param = new CompilerParameters();
param.GenerateInMemory = false;
param.GenerateExecutable = false;
param.OutputAssembly = "console";
param.ReferencedAssemblies.Add("System.dll");
param.ReferencedAssemblies.Add("System.Core.dll");
param.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
var results = provider.CompileAssemblyFromSource(param,
@"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace FireflyGL
{
class DebugConsole
{
    static void Run(StringWriter writer)
    {
        var stdOut = Console.Out;
        Console.SetOut(writer);
        " + input.Text + @"
        Console.SetOut(stdOut);
    }
}
}");
            if (results.Errors.HasErrors)
            {
                for (int i = 0; i < results.Errors.Count; ++i)
                {
                    PushText(results.Errors[i].ErrorText);
                }
            }
            else
            {
                try
                {
                    var writter = new StringWriter();
                    results.CompiledAssembly.GetType("FireflyGL.DebugConsole").GetMethod("Run", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { writter });
                    PushText(writter.ToString());
                    history.Add(input.Text);
                    currentHistory = history.Count;
                    input.Text = "";
                    writter.Dispose();
                }
                catch (Exception)
                {

                }
            }
            provider.Dispose();
            File.Delete(results.CompiledAssembly.Location);
Was it helpful?

Solution

You have to unload the assembly to get rid of all the references. Unfortunately, you can't do that. You can however unload an AppDomain and provided you reference your assembly in that AppDomain, it will be unloaded as well.

If you don't care about creating a memory leak, you could also just create unique names for your assembly (console1, console2 etc.)...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top