Search code examples
c#code-generation

How to write to an at runtime generated .dll file in C#?


I have to change the contents of an .dll file at runtime, but cannot do so because it is in use and get a

InvalidOperationException

instead.

I am currently in the process of working out a way to compile C# code at runtime for a game made in Unity. Using the Microsoft.CSharp.CSharpCodeProvider and System.CodeDom.Compiler.CompilerParamters classes, I've got a system working that allows me to compile code and outputs it as a .dll file, so I can use it together with other classes. If you need to know more about the way I do this, take a look at the tutorial I used (and the below mentioned changes).

However, the compiling only works once, because the next time the compiler is run, the .dll file already exists and I get the following error message:

Could not write to file `fileName'. Win32 IO returned 1224. Path: path/fileName.dll

These are the most important parts of my code:

public void Compile() {
  CSharpCodeProvider provider = new CSharpCodeProvider();
  CompilerParameters parameters = new CompilerParameters();
  //...
  parameters.GenerateInMemory = false; //generates actual file
  parameters.GenerateExecutable = false; //generated .dll instead of .exe
  //...
  parameters.OutputAssembly = Application.dataPath + className + ".dll";
  CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
  //...
  Assembly assembly = results.CompiledAssembly;
  Type program = assembly.GetType("GameLevel." + className);
  MethodInfo excecuteMethod = program.GetMethod("Excecute");

  excecuteMethod.Invoke(null, null);
}

I really don't want to give the file a different name every time, because that would make using it in other classes a pain. I am assuming this can be solved by somehow telling the game that the old .dll file isn't in use anymore, because that shouldn't even be the case after the method has been excecuted, right?

I am thankful for your answers!


Solution

  • As I said in the comments, I do this often with a twist. I have a calculus engine which compiles formulas. Every time a formula changes, it's re compiled. Every time i need to run a formula, i instantiate its class. But.... Every time I recompile, I create a NEW dll with a DIFFERENT name. So I use a timestamp for the names and a timestamp for the class names. Everytime I instantiate, I look for the latest dll

    So my class inside the dll looks like:

    public class MyGeneratedClass_20191024103000 {
    // do stuff
    }
    

    assembly creation (pseudocode):

    aseemblyManager.CreateLibrary(OUTPUT_DLLS_PATH + "\\Calculus_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".dll", refs, sourcecode) ... etc
    

    aseembly load:

    string pathNewest = ListFolderSortByDate(); //you should also get the timestamp
    assembly = Assembly.LoadFrom(pathNewest ); //register dll
    mytype =  assembly.GetType("mycalculus"); 
    

    finally, instantiation:

     myobject= Activator.CreateInstance(mytype , new object[] { some parameters });
    mytype .GetMethod("Calculate" + timestamp).Invoke(myobject, arrParam);