Ответ 1
ОК, сначала сначала: нет реального способа использования CSharpCodeProvider для выполнения динамической компиляции источника С# полностью в памяти. Существуют методы, которые, как представляется, поддерживают эту функциональность, но поскольку компилятор С# является естественным исполняемым файлом, который не может запускаться в процессе, исходная строка сохраняется во временном файле, компилятор вызывается в этом файле, а затем результирующая сборка сохранен на диск и затем загружен для вас с помощью Assembly.Load.
Во-вторых, как вы обнаружили, вы должны иметь возможность использовать метод Compile из AppDomain для загрузки сборки и предоставления ей необходимых разрешений. Я столкнулся с таким же необычным поведением, и после того, как много копаний обнаружило, что это была ошибка в рамках. Я отправил отчет о проблеме на MS Connect.
Поскольку структура уже записывается в файловую систему в любом случае, обходной путь состоит в том, чтобы сборка была записана во временный файл и затем загружалась по мере необходимости. Однако при загрузке вам необходимо временно утверждать разрешения в AppDomain, так как вы запретили доступ к файловой системе. Вот пример этого фрагмента:
new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, assemblyPath).Assert();
var assembly = Assembly.LoadFile(assemblyPath);
CodeAccessPermission.RevertAssert();
Оттуда вы можете использовать сборку и отражение, чтобы вызвать ваш метод. Обратите внимание, что этот метод позволяет вам поднять процесс компиляции вне изолированного AppDomain, что является плюсом, на мой взгляд.
Для справки, вот мой класс Sandbox, созданный для облегчения запуска сборок script в чистом отдельном AppDomain, который имеет ограниченные разрешения и может быть легко выгружен при необходимости:
class Sandbox : MarshalByRefObject
{
const string BaseDirectory = "Untrusted";
const string DomainName = "Sandbox";
public 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 string Execute(string assemblyPath, string scriptType, string method, params object[] parameters)
{
new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, assemblyPath).Assert();
var assembly = Assembly.LoadFile(assemblyPath);
CodeAccessPermission.RevertAssert();
Type type = assembly.GetType(scriptType);
if (type == null)
return null;
var instance = Activator.CreateInstance(type);
return string.Format("{0}", type.GetMethod(method).Invoke(instance, parameters));
}
}
Быстрая заметка: если вы используете этот метод для предоставления доказательств безопасности для нового AppDomain, вам нужно подписать свою сборку, чтобы дать ей сильное имя.
Обратите внимание, что это отлично работает при запуске, но если вам действительно нужна пуленепробиваемая среда script, вам нужно сделать еще один шаг и изолировать script в отдельном процессе, чтобы убедиться, что скрипты, которые делают вредоносные (или просто глупые) вещи, такие как переполнение стека, вилочные бомбы и ситуации с памятью, не разрушают весь процесс приложения. Я могу дать вам больше информации об этом, если вам это нужно.