Нужно сделать, чтобы пользователь мог выполнять C#-код на моем сервере (что-то вроде dotnetfiddle.net). Приложение Asp.net Core.
Есть такой метод, который вполне нормально работает:
public static async Task<ScriptState<object>> ExecuteScriptAsync(string code, IEnumerable<Assembly> references,
IEnumerable<string> usings)
{
var options = ScriptOptions.Default.WithReferences(references).WithImports(usings);
return await CSharpScript.RunAsync(code, options);
}
Проблема метода ExecuteScript
в том, что такой код он уже не будет выполнять:
class Program
{
public static void Main()
{
throw new System.Exception();
}
}
Как сделать чтобы код выполнялся как в консольном приложении, т.е. метод Main
был точкой входа?
Update
Нашел такой код. Срабатывает, но если в скрипт дописать Console.WriteLine("hi")
, то он ломается с ошибкой "Имя Console
не существует в текущем контексте".
var script = @"using System;
public static class Program
{
public static int Main(string[] args)
{
var x = 7 * 8;
return x;
}
}";
var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
var refs = new List<PortableExecutableReference>
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "mscorlib.dll")),
MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.dll")),
MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Core.dll")),
MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Runtime.dll")),
MetadataReference.CreateFromFile(Assembly.GetEntryAssembly().Location)
};
// Parse the script to a SyntaxTree
var syntaxTree = CSharpSyntaxTree.ParseText(script);
var options = new CSharpCompilationOptions(OutputKind.ConsoleApplication);
// Compile the SyntaxTree to a CSharpCompilation
var compilation = CSharpCompilation.Create("Script",
new[] { syntaxTree },
refs,
new CSharpCompilationOptions(
OutputKind.ConsoleApplication,
optimizationLevel: OptimizationLevel.Release,
assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default));
//CodeDomProvider codeDomProvider = new CodeDomProvider();
using (var outputStream = new MemoryStream())
using (var pdbStream = new MemoryStream())
{
// Emit assembly to streams.
var result = compilation.Emit(outputStream, pdbStream);
if (!result.Success)
{
return;
}
// Load the emitted assembly.
var assembly = Assembly.Load(outputStream.ToArray(), pdbStream.ToArray());
// Invoke the entry point.
var x = assembly.EntryPoint.Invoke(null, null);
Assembly.Load, тем более из массива байт - очень плохая идея, ведь она не дает возможности впоследствии выгрузить сборку из памяти, т.е. при работе в серверном приложении память рано или поздно исчерпается и сервер придется перезапускать. Кроме того, как ограничить права для запускаемого кода, чтобы он не разрушил вам систему? Если вы ориентируетесь под .NET Core, то домены приложений недоступны. Правильнее скомпилировать код в файл и запускать его в новом процессе под пользователем с ограниченными правами и перехватывать его вывод:
using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
namespace RoslynTest
{
class Program
{
static void RunScript()
{
var script = @"using System;
public static class Program
{
public static int Main(string[] args)
{
var x = 7 * 8;
Console.WriteLine(x.ToString());
return x;
}
}";
var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
var refs = new List<PortableExecutableReference>
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "mscorlib.dll")),
MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.dll")),
MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Core.dll")),
MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Runtime.dll")),
MetadataReference.CreateFromFile(Assembly.GetEntryAssembly().Location)
};
// Parse the script to a SyntaxTree
var syntaxTree = CSharpSyntaxTree.ParseText(script);
var options = new CSharpCompilationOptions(OutputKind.ConsoleApplication);
// Compile the SyntaxTree to a CSharpCompilation
var compilation = CSharpCompilation.Create("Script",
new[] { syntaxTree },
refs,
new CSharpCompilationOptions(
OutputKind.ConsoleApplication,
optimizationLevel: OptimizationLevel.Release,
assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default)
);
var result = compilation.Emit("script.exe");
if (!result.Success)
{
throw new ApplicationException("Cannot compile script");
}
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "script.exe";
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
psi.UserName = "Vasya";
psi.Password = "123";
var process = new Process();
using (process)
{
process.StartInfo = psi;
process.Start();
while (!process.StandardOutput.EndOfStream)
{
string res = process.StandardOutput.ReadLine();
Console.WriteLine(res);
}
}
}
}
}
Код заточен под .NET Framework / Windows, но думаю, не составит труда переделать под .NET Core, так как все используемые библиотеки есть в .NET Standard. Запуск процессов от имени другого пользователя должен работать в .NET Core на всех ОС, начиная с .NET Core 2.1.
Примечание. В .NET Core 3.0 появилась возможность выгрузки сборок из памяти, но это все еще не решает проблему обеспечения безопасности.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Недавно начал изучать c#, и не могу сделать вот такое задание: Создать программу которая позволяет рисовать в формате графических примитив:...
Имеется код html страницы, в котором есть такие теги как "span itemprop=\"name\">" Я без труда нахожу значение, но проблема в том, что находит значение самого...
Хочу начать использовать Sphinx, так как есть сайт с большой базой и медленными запросамиВсякая оптимизация не помогла достичь нужной скорости,...