Pull to refresh

Компиляция и безопасное выполнения кода «на лету»

Reading time 4 min
Views 7.5K

Проблема


В вашем приложении нужно выполнять код, который не известен на этапе компиляции приложения. Это могут быть разнообразные плагины, расширения, вычисления и т.д.

Решение


.NET позволяет компилировать и выполнять код. Более того, исполнять код можно безопасно, сузив круг ресурсов, которые может использовать выполняемый код.



Реализация


Для компиляции нам потребуется CodeDomProvider, с помощью которого можно инстанцировать экземпляр компилятора для любого языка .NET. Перейдем к коду.

  1. private string EvalCode(string typeName, string methodName, string sourceCode)
  2.         {
  3.             string output = ":)";
  4.             var compiler = CodeDomProvider.CreateProvider("CSharp");
  5.             var parameters = new CompilerParameters
  6.             {
  7.                 CompilerOptions = "/t:library",
  8.                 GenerateInMemory = true,
  9.                 IncludeDebugInformation = true
  10.             };
  11.             var results = compiler.CompileAssemblyFromSource(parameters, sourceCode);
  12.  
  13.             if (!results.Errors.HasErrors)
  14.             {
  15.                 var assembly = results.CompiledAssembly;
  16.                 var evaluatorType = assembly.GetType(typeName);
  17.                 var evaluator = Activator.CreateInstance(evaluatorType);
  18.  
  19.                 output = (string) InvokeMethod(evaluatorType, methodName, evaluator, new object[] { output });
  20.                 return output;
  21.             }
  22.  
  23.             output = "\r\nHouston, we have a problem at compile time!";
  24.             return results.Errors.Cast<CompilerError>().Aggregate(output, (current, ce) => current + string.Format("\r\nline {0}: {1}", ce.Line, ce.ErrorText));
  25.         }
  26.  
  27.         [FileIOPermission(SecurityAction.Deny, Unrestricted = true)]
  28.         private object InvokeMethod(Type evaluatorType, string methodName, object evaluator, object[] methodParams)
  29.         {
  30.             return evaluatorType.InvokeMember(methodName, System.Reflection.BindingFlags.InvokeMethodnull, evaluator, methodParams);
  31.         }


Как видите, ничего сверхъестественного нет. Создаем компилятор, передаем в него код, получаем DLL. С помощью reflection запускаем нужный метод.

Код в действии:


Интересна строка 27. С помощью нее можно ограничивать права, которыми обладает код (не только динамический). В .NET есть механизм Code Access Security, благодаря которому можно управлять безопасностью выполняемого кода.

Ошибка безопасности при попытки записи на диск:


Несмотря на то что выполнение кода выглядит весьма «безобидно», стоит помнить, что это весьма ресурсоемкий процесс. Например, если выполнять код в текущем домене приложения (так, как это сделано в демо), при продолжительной работе приложения память может просто закончится (.NET не может выгружать сборки из домена).

Демо:
DynDllFunWf.zip
Tags:
Hubs:
+19
Comments 7
Comments Comments 7

Articles