Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSharpCodeProvider, CompilerParameters.GenerateInMemory and CompilerResults.PathToAssembly: odd behaviour

Tags:

c#

.net

I have the following code running in a Windows Service that's been working for years without problems even under heavy concurrency:

CSharpCodeProvider codeProvider = new CSharpCodeProvider();           

CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = true;
parameters.OutputAssembly = outputAssemblyFile;    

CompilerResults results = codeProvider.CompileAssemblyFromFile(parameters, "file.cs");

if (results.Errors.Count > 0)
{
    Console.WriteLine("Compile ERROR");
}
else
{
    Console.WriteLine("Compile OK");        
    Console.WriteLine("Assembly Path:" + results.PathToAssembly);
    Console.WriteLine("Assembly Name:" + results.CompiledAssembly.FullName);        
}

Usually, when the code is successfully compiled, since parameters.GenerateInMemory is set to true, the results.PathToAssembly is null (as specified in the MSDN)

One of these days I started having an issue where sometimes the code was being successfully compiled but results.PathToAssembly was NOT null. Additionally, CompiledAssembly was returning a FileNotFoundException but when I checked the path indicated by results.PathToAssembly the assembly was actually there. I could not, however, make sure it was there at that specific moment.

I restarted the service and everything was back on track.

Is there any obvious reason for this to happen?

Thinking this could be some permission issue I tried to reproduce the issue by having the assembly already created and setting the file read-only, but that caused the compilation to fail.

like image 793
Francisco Silva Avatar asked Dec 29 '25 02:12

Francisco Silva


1 Answers

The way I read the documentation when GenerateInMemory==true the result of the compilation should be produced in memory, without generating a EXE or DLL file - hence result.PathToAssembly==null.

In this case setting OutputAssembly to something would still make sense: an assembly has a name even if it just lives in memory. GenerateExecutable tells the system to generate an EXE or a DLL, and so it should be ignored if the result is in memory.

This is the way I understand the theory - what happens in practice is that a DLL o EXE file is generated regardless of the value of GenerateInMemory, with the name specified by OutputAssembly and placed in the current directory.

Furthermore, it appears that the compiler does multiple operations on this file - delete/create/open/close.

This means that if the compiler code is called concurrently by multiple threads it can fail with either 'file not found' or 'file in use' errors, because the file operation on different thread will interfere with each other.

If the purpose of the code is to generate an assembly file on disk the solution would be:

parameters.GenerateExecutable = false; 
parameters.GenerateInMemory = false; 

and NOT specify OutputAssembly - in this case the system will auto-generate an unique name for the assembly (and the assembly DLL), avoiding conflicts between different threads. The path of the file is accessible in result.PathToAssembly.

like image 139
MiMo Avatar answered Dec 31 '25 16:12

MiMo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!