Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

runtime code compilation gives error - process cannot access the file

I hava small windows app where user enter code and on button click event code is compiled at runtime.

When i click button 1st time it works fine but if click same button more than once it gives error "The process cannot access the Exmaple.pdb file because it is being used by another process.". Below is the example sample code

using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
using System.IO;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {

   var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
          var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "Example" + ".exe", true); //iloop.ToString() +
          parameters.GenerateExecutable = true;
          CompilerResults results = csc.CompileAssemblyFromSource(parameters,
          @"using System.Linq;
            class Program {
              public static void Main(string[] args) {}

              public static string Main1(int abc) {" + textBox1.Text.ToString()

                   + @"
              }
            }");
          results.Errors.Cast<CompilerError>().ToList().ForEach(error => Error = error.ErrorText.ToString());


var scriptClass = results.CompiledAssembly.GetType("Program");
                      var scriptMethod1 = scriptClass.GetMethod("Main1", BindingFlags.Static | BindingFlags.Public);


              StringBuilder st = new StringBuilder(scriptMethod1.Invoke(null, new object[] { 10 }).ToString());
              result = Convert.ToBoolean(st.ToString());
        }
    }
}

how do i solve this issue so that if i click same button more than once.. it should work properly.

thanks,

like image 411
sia Avatar asked Oct 30 '14 11:10

sia


People also ask

How do you fix the process Cannot access the file?

Process cannot access file because it is being used by another process error message. To resolve this error: Press Ctrl + Alt + Delete, then click Task Manager. Ensure you're on the Processes tab.

How do you handle the process Cannot access the file because it is being used by another process in C#?

1) share the same FileStream with proper synchronization functions (because it is not thread-safe). See this and this posts for an example. 2) use FileShare enumeration to instruct OS to allow other processes (or other parts of your own process) to access same file concurrently.


1 Answers

  var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" },
                       "Example" + ".exe", true);

You are explicitly naming the output file to Example.exe. You'll also get Example.pdb, the debug info file generated for the code, you asked for it with the 3rd true argument. As soon as you use results.CompiledAssembly.GetType(), you'll load the generated assembly and that puts a lock on Example.exe. Since you have the debugger attached, the debugger will locate the matching .pdb file for the assembly and load and lock it.

The locks will not be released until the assembly is unloaded. And by standard .NET Framework rules, this will not happen until your primary appdomain is unloaded. Usually at the end of your program.

So trying to compile code again is going to be a fail whale. The compiler cannot create the .pdb file, it is locked by the debugger. Omitting the true argument isn't going to help, it will now fail to create the output file, Example.exe.

You will need to tackle this differently of course. By far the simplest solution is to not name the output assembly. Default behavior is for CSharpCodeProvider to generate an assembly with a unique random name, you'll always avoid a collision that way. A more advanced approach is to create a secondary AppDomain to load the assembly, now allowing to unload it again before you recompile.

Going for the simple solution:

  var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" });
  parameters.IncludeDebugInformation = true;
  parameters.GenerateExecutable = true;
  // etc..
like image 131
Hans Passant Avatar answered Sep 30 '22 05:09

Hans Passant