Запуск скомпилированного сценария C# в изолированной программной среде AppDomain
Мои приложения должны быть доступны для сценариев пользователями в C#, но сценарий пользователя должен запускаться в ограниченном AppDomain, чтобы предотвратить случайное повреждение сценариев, но я не могу заставить его работать, и, поскольку мое понимание AppDomains, к сожалению, ограничено, я не могу сказать почему.
Решение, которое я сейчас пробую, основано на этом ответе /questions/32196985/vnet-40-kak-ya-mogu-pesochnitsej-sborki-v-pamyati-i-vyipolnit-metod/32196997#32196997.
Это модель моей ситуации (все, кроме Script.cs, находящегося в сборке со строгим именем). Прошу прощения за стену кода, я не смог бы еще больше сократить проблему.
class Program
{
static void Main(string[] args)
{
// Compile the script
CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters parameters = new CompilerParameters()
{
GenerateExecutable = false,
OutputAssembly = System.IO.Path.GetTempFileName() + ".dll",
};
parameters.ReferencedAssemblies.Add(Assembly.GetEntryAssembly().Location);
CompilerResults results = codeProvider.CompileAssemblyFromFile(parameters, "Script.cs");
// ... here error checks happen ....//
var sandbox = Sandbox.Create();
var script = (IExecutable)sandbox.CreateInstance(results.PathToAssembly, "Script");
if(script != null)
script.Execute();
}
}
public interface IExecutable
{
void Execute();
}
Класс Песочница:
public class Sandbox : MarshalByRefObject
{
const string BaseDirectory = "Untrusted";
const string DomainName = "Sandbox";
public static Sandbox Create()
{
var setup = new AppDomainSetup()
{
ApplicationBase = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, BaseDirectory),
ApplicationName = DomainName,
DisallowBindingRedirects = true,
DisallowCodeDownload = true,
DisallowPublisherPolicy = true
};
var permissions = new PermissionSet(PermissionState.None);
permissions.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess));
permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
var domain = AppDomain.CreateDomain(DomainName, null, setup, permissions,
typeof(Sandbox).Assembly.Evidence.GetHostEvidence<StrongName>());
return (Sandbox)Activator.CreateInstanceFrom(domain, typeof(Sandbox).Assembly.ManifestModule.FullyQualifiedName, typeof(Sandbox).FullName).Unwrap();
}
public object CreateInstance(string assemblyPath, string typeName)
{
new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, assemblyPath).Assert();
var assembly = Assembly.LoadFile(assemblyPath);
CodeAccessPermission.RevertAssert();
Type type = assembly.GetType(typeName); // ****** I get null here
if (type == null)
return null;
return Activator.CreateInstance(type);
}
}
Загруженный скрипт:
using System;
public class Script : IExecutable
{
public void Execute()
{
Console.WriteLine("Boo");
}
}
В CreateInstance
из SandBox
Я всегда получаю null
на отмеченной линии. Я пробовал различные формы предоставления имени, включая чтение имени типа (или неверно определенного имени) из results.CompiledAssembly
используя отражение. Что я здесь не так делаю?
1 ответ
Первое, что я проверю, есть ли ошибки компиляции (у меня было несколько головных болей, вызванных этими проблемами)
Вторая идея - о разрешении собраний. Я всегда добавляю в качестве проверки безопасности обработчик событий для AppDomain.CurrentDomain.AssemblyResolve, где я ищу на своем известном пути для отсутствующих сборок. Когда не найденная сборка является той, которую я только что скомпилировал, я добавляю к ней статическую ссылку и возвращаю ее.
Что я обычно делаю, так это:
- Создать новую сборку в файловой системе с помощью компилятора
- Загрузите его содержимое с помощью File.ReadAllBytes
- Загрузите dll с Assembly.Load в AppDomain, в котором я буду использовать объект
- Добавьте событие AppDomain.CurrentDomain.AssemblyResolve.
На всякий случай (так как я часто этим пользуюсь) я создал небольшую библиотеку для выполнения подобных задач
Код и документация здесь: Kendar Expression Builder Пока пакет nuget находится здесь: Шаблон Nuget Sharp