Search code examples
c#code-generationroslyn.net-6.0codedom

Codegeneration at runtime from a string to console exe is not working in C# .NET6


I have some code which must be able to generated a console application at runtime (Codegeneration with System.CodeDom). I did this already a lot, but in NET 6 now I am struggling with that and the new API. In the code below I try to compile simply from a string. See below the static class with method Start() which then should generates the application.

The compilations seems fine, no errors at the end. But when starting the generated AppCodegenerated.exe, it shows some reference exception with System.Runtime.

Please help, any Idea? Already researched a lot but could not find any useful solution..

//-

I used the Visual Studio 2022 / NET 6 and theses Nuget's:

enter image description here

using Basic.Reference.Assemblies;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using System.Text;



namespace CompilerSimplified
{
    public static class Compiler
    {
        public static bool Start()
        {
            string FileName = "AppCodegenerated";
            string ExePath = AppDomain.CurrentDomain.BaseDirectory + @"\" + FileName + ".exe";
            string code = @"using System; Console.WriteLine(""Hello.""); Console.ReadLine(); ";


            // ------- References -------------
            // .net platform references
            List<MetadataReference> References = new List<MetadataReference>();
            foreach (var item in ReferenceAssemblies.Net60) // ReferenceAssemblies from Nuget: Basic.Reference.Assemblies;
                References.Add(item);

            // or tried this: loop manually through system platform 
            //string[] fileEntries = Directory.GetFiles(@"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.0\ref\net6.0\", "*.dll");
            //foreach (string fileName in fileEntries)
            //    references.Add(MetadataReference.CreateFromFile(fileName));MetadataReference.CreateFromFile(fileName));
            // ------- References END -------------



            // delete existing file
            if (File.Exists(ExePath))
                File.Delete(ExePath);

            // compiler options
            CSharpCompilationOptions DefaultCompilationOptions =
                new CSharpCompilationOptions(outputKind: OutputKind.ConsoleApplication, platform: Platform.AnyCpu)
                .WithOverflowChecks(true).WithOptimizationLevel(OptimizationLevel.Release);

            // encode soucre code
            string sourceCode = SourceText.From(code, Encoding.UTF8).ToString();

            // CSharp options
            var parsedSyntaxTree = Parse(sourceCode, "", CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp10));

            // compilation
            var compilation = CSharpCompilation.Create(FileName, new SyntaxTree[] { parsedSyntaxTree }, references: References, DefaultCompilationOptions);
            var result = compilation.Emit(ExePath);

            // return
            if (result.Success)
                return true;
            else
                return false;
        }

        private static SyntaxTree Parse(string text, string filename = "", CSharpParseOptions options = null)
        {
            var stringText = SourceText.From(text, Encoding.UTF8);
            return SyntaxFactory.ParseSyntaxTree(stringText, options, filename);
        }

    }
}

Above code runs fine without error and exports the AppCodegenerated.exe into the project /bin folder.

Execution of this generated AppCodegenerated.exe shows following on the output console:

Unhandled exception: System.IO.FileNotFoundException: 
The file or assembly "System.Runtime, Version = 6.0.0.0, Culture = neutral, 
PublicKeyToken = b03f5f7f11d50a3a" or a dependency on it was not found. 
The system can not find the stated file.

Solution

  • It is not possible to codegenerate directly a console application like the initial approach above. One possible solution is to generate first a dll (what I mentioned above in the example code is working fine), and from there include that .dll into a .exe, from where the functionality can run.