繁体   English   中英

System.PlatformNotSupportedException 在运行时编译 C# 代码 .NET Core

[英]System.PlatformNotSupportedException Compiling C# code at runtime .NET Core

尝试在运行时在 .NET Core 上编译简单的 C# 代码,但出现此错误:

System.PlatformNotSupportedException:“此平台不支持操作。”

在这条线上:

CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);

我的代码:

using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Text;

string code = @"
    using System;

    namespace First
    {
        public class Program
        {
            public static void Main()
            {
            " +
                "Console.WriteLine(\"Hello, world!\");"
                + @"
            }
        }
    }
";


CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();

parameters.ReferencedAssemblies.Add("System.Drawing.dll");
parameters.GenerateInMemory = true;
parameters.GenerateExecutable = true;

CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);


if (results.Errors.HasErrors)
{
    StringBuilder sb = new StringBuilder();

    foreach (CompilerError error in results.Errors)
    {
        sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
    }

    throw new InvalidOperationException(sb.ToString());
}


Assembly assembly = results.CompiledAssembly;
Type program = assembly.GetType("First.Program");
MethodInfo main = program.GetMethod("Main");


main.Invoke(null, null);

我推荐使用 Roslyn 编译器。 您需要添加引用 Microsoft.CodeAnalysis 和 Microsoft.CodeAnalysis.CSharp 才能使以下示例正常工作。 请注意, RoslynCompiler class 会动态加载程序集。 如果要将编译保存到磁盘以供重用,您可以相当轻松地修改 class 以使用FileStream而不是MemoryStream

RoslynCompiler Class 的示例用法(下)

    string code = @"
    using System;

    namespace First
    {
        public class Program
        {
            public static void Main()
            {
                Console.WriteLine(\"Hello, world!\");
            }
            public static void WithParams(string message)
            {
                Console.WriteLine(message);
            }
        }
    }
";

var compiler = new RoslynCompiler("First.Program", code, new[] {typeof(Console)});
var type = compiler.Compile();
    
type.GetMethod("Main").Invoke(null, null);
//result: Hellow World!

// pass an object array to the second null parameter to pass arguments
type.GetMethod("WithParams").Invoke(null, new object[] {"Hi there from invoke!"});
    //result: Hi from invoke

Roslyn 编译器 Class(快速而肮脏的示例)

public class RoslynCompiler
{
    readonly CSharpCompilation _compilation;
    Assembly _generatedAssembly;
    Type? _proxyType;
    string _assemblyName;
    string _typeName;
    
    public RoslynCompiler(string typeName, string code, Type[] typesToReference)
    {
        _typeName = typeName;
        var refs = typesToReference.Select(h => MetadataReference.CreateFromFile(h.Assembly.Location) as MetadataReference).ToList();
        
        //some default refeerences
        refs.Add(MetadataReference.CreateFromFile(Path.Combine(Path.GetDirectoryName(typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly.Location), "System.Runtime.dll")));
        refs.Add(MetadataReference.CreateFromFile(typeof(Object).Assembly.Location));

       //generate syntax tree from code and config compilation options
        var syntax = CSharpSyntaxTree.ParseText(code);
        var options = new CSharpCompilationOptions(
            OutputKind.DynamicallyLinkedLibrary, 
            allowUnsafe: true,
            optimizationLevel: OptimizationLevel.Release);

        _compilation = CSharpCompilation.Create(_assemblyName = Guid.NewGuid().ToString(), new List<SyntaxTree> { syntax }, refs, options);
    }

    public Type Compile()
    {
        
        if (_proxyType != null) return _proxyType;
        
        using (var ms = new MemoryStream())
        {
            var result = _compilation.Emit(ms);
            if (!result.Success)
            {
                var compilationErrors = result.Diagnostics.Where(diagnostic =>
                        diagnostic.IsWarningAsError ||
                        diagnostic.Severity == DiagnosticSeverity.Error)
                    .ToList();
                if (compilationErrors.Any())
                {
                    var firstError = compilationErrors.First();
                    var errorNumber = firstError.Id;
                    var errorDescription = firstError.GetMessage();
                    var firstErrorMessage = $"{errorNumber}: {errorDescription};";
                    var exception = new Exception($"Compilation failed, first error is: {firstErrorMessage}");
                    compilationErrors.ForEach(e => { if (!exception.Data.Contains(e.Id)) exception.Data.Add(e.Id, e.GetMessage()); });
                    throw exception;
                }
            }
            ms.Seek(0, SeekOrigin.Begin);

            _generatedAssembly = AssemblyLoadContext.Default.LoadFromStream(ms);

            _proxyType = _generatedAssembly.GetType(_typeName);
            return _proxyType;
        }
    }
}

性能提示

如果性能很重要,请使用委托而不是Invoke ,如下所示以实现接近预编译的吞吐量:

void Main()
{
    string code = @"OMITTED EXAMPLE CODE FROM SAMPLE ABOVE";

    var compiler = new RoslynCompiler("First.Program", code, new[] { typeof(Console) });
    var type = compiler.Compile();  
    
    // If perf matters used delegates to get near pre-compiled througput vs Invoke()
    var cachedDelegate = new DynamicDelegateCacheExample(type); 
    
    cachedDelegate.Main();
    //result: Hellow world!
    
    cachedDelegate.Main("Hi there from cached delegate!");
    //result: Hi there from cached delegate!


}
public class DynamicDelegateCacheExample
{
    delegate void methodNoParams();
    delegate void methodWithParamas(string message);
    private static methodNoParams cachedDelegate;
    private static methodWithParamas cachedDelegateWeithParams;

    public DynamicDelegateCacheExample(Type myDynamicType)
    {
        cachedDelegate = myDynamicType.GetMethod("Main").CreateDelegate<methodNoParams>();
        cachedDelegateWeithParams = myDynamicType.GetMethod("WithParams").CreateDelegate<methodWithParamas>();
    }
    
    public void Main() => cachedDelegate();

    public void Main(string message) => cachedDelegateWeithParams(message);
}

使用 .net 核心网络标准并发布到一个独立的 exe,您还需要一些技巧;

public static ModuleMetadata GetMetadata(this Assembly assembly)
{
    // based on https://github.com/dotnet/runtime/issues/36590#issuecomment-689883856
    unsafe
    {
        return assembly.TryGetRawMetadata(out var blob, out var len)
            ? ModuleMetadata.CreateFromMetadata((IntPtr)blob, len)
            : throw new InvalidOperationException($"Could not get metadata from {assembly.FullName}");
    }
}

#pragma warning disable IL3000
public static MetadataReference GetReference(this Assembly assembly)
    => (assembly.Location == "")
        ? AssemblyMetadata.Create(assembly.GetMetadata()).GetReference()
        : MetadataReference.CreateFromFile(assembly.Location);
#pragma warning restore IL3000

public static Assembly Compile(string source, IEnumerable<Type> references)
{
    var refs = new HashSet<Assembly>(){
        typeof(object).Assembly
    };
    foreach (var t in references)
        refs.Add(t.Assembly);

    foreach (var a in AppDomain.CurrentDomain.GetAssemblies()
        .Where(a => !a.IsDynamic
            && a.ExportedTypes.Count() == 0
            && (a.FullName.Contains("netstandard") || a.FullName.Contains("System.Runtime,"))))
        refs.Add(a);

    var options = CSharpParseOptions.Default
        .WithLanguageVersion(LanguageVersion.Latest);

    var compileOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
        .WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default);

    var compilation = CSharpCompilation.Create("Dynamic",
        new[] { SyntaxFactory.ParseSyntaxTree(source, options) },
        refs.Select(a => a.GetReference()),
        compileOptions
    );

    using var ms = new MemoryStream();
    var e = compilation.Emit(ms);
    if (!e.Success)
        throw new Exception("Compilation failed");
    ms.Seek(0, SeekOrigin.Begin);

    var context = new AssemblyLoadContext(null, true);
    return context.LoadFromStream(ms);
}

// for dynamically implementing some interface;
public static C CompileInstance<C>(string source, IEnumerable<Type> references)
{
    var assembly = Compile(source, references);
    var modelType = assembly.DefinedTypes.Where(t => typeof(C).IsAssignableFrom(t)).Single();

    return (C)Activator.CreateInstance(modelType);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM