ОК, обо всем по порядку: нет никакого реального способа использовать CSharpCodeProvider для динамической компиляции исходного кода C # полностью в памяти. Существуют методы, которые поддерживают эту функциональность, но поскольку компилятор C # является собственным исполняемым файлом, который не может быть запущен внутри процесса, исходная строка сохраняется во временном файле, компилятор вызывается для этого файла, а затем результирующая сборка сохраняется на диск и затем загружается для вас с помощью Assembly.Load.
Во-вторых, как вы обнаружили, вы должны иметь возможность использовать метод Compile из AppDomain, чтобы загрузить сборку и дать ей необходимые разрешения. Я столкнулся с тем же самым необычным поведением, и после многих копаний обнаружил, что это ошибка в фреймворке. Я отправил отчет о проблеме на MS Connect .
Так как фреймворк уже записывает в файловую систему, обходной путь - записать сборку во временный файл и затем загрузить при необходимости. Однако, когда вы загрузите его, вам нужно будет временно утвердить разрешения в AppDomain, поскольку вы запретили доступ к файловой системе. Вот пример этого фрагмента:
new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, assemblyPath).Assert();
var assembly = Assembly.LoadFile(assemblyPath);
CodeAccessPermission.RevertAssert();
Оттуда вы можете использовать сборку и отражение для вызова вашего метода. Обратите внимание, что этот метод позволяет поднять процесс компиляции за пределы изолированного AppDomain, что, на мой взгляд, является плюсом.
Для справки, вот мой класс Sandbox, созданный для облегчения запуска сборок скриптов в хорошем чистом отдельном 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));
}
}
Краткое примечание: если вы используете этот метод для предоставления доказательств безопасности для нового домена приложений, вам необходимо подписать сборку, чтобы дать ей строгое имя.
Обратите внимание, что это прекрасно работает при запуске в процессе, но если вы действительно хотите использовать среду с пуленепробиваемыми сценариями, вам нужно сделать еще один шаг и изолировать сценарий в отдельном процессе, чтобы гарантировать, что сценарии являются вредоносными (или просто глупо) такие вещи, как переполнение стека, разветвление бомб и ситуации с нехваткой памяти, не разрушают весь процесс приложения. Я могу дать вам больше информации, если вам это нужно.