Я использую C# с .СЕТЬ 3.5. Можно ли сериализовать блок кода, передать его куда-нибудь, десериализовать, а затем выполнить?
Пример использования этого:
Action<object> pauxPublish = delegate(object o)
{
if (!(o is string))
{
return;
}
Console.WriteLine(o.ToString());
};
Transmitter.Send(pauxPublish);
С какой-то удаленной программой, выполняющей:
var action = Transmitter.Recieve();
action("hello world");
Моя конечная цель - иметь возможность выполнять произвольный код в другом процессе (который не знает код заранее).





Скомпилируйте его в отдельную сборку, отправьте сборку, пусть другой процесс загрузит ее.
Вы можете подумать о последствиях для безопасности.
Обновлять: другая идея - создать дерево выражений и использовать эту библиотеку для его сериализации:
Вы также можете отправить его в виде строки, а затем использовать CodeDomProvider для ее компиляции, результат тот же. У меня есть пример кода:
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.CSharp;
namespace DynamicCodeApplication
{
class azCodeCompiler
{
private List<string> assemblies;
public azCodeCompiler()
{
assemblies = new List<string>();
scanAndCacheAssemblies();
}
public Assembly BuildAssembly(string code)
{
CodeDomProvider prov = CodeDomProvider.CreateProvider("CSharp");
string[] references = new string[] { }; // Intentionally empty, using csc.rsp
CompilerParameters cp = new CompilerParameters(references)
{
GenerateExecutable = false,
GenerateInMemory = true
};
string path = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
cp.CompilerOptions = "@" + path + @"\csc.rsp";
CompilerResults cr = prov.CompileAssemblyFromSource(cp, code);
foreach (CompilerError err in cr.Errors)
{
Console.WriteLine(err.ToString());
}
return cr.CompiledAssembly;
}
public object ExecuteCode(string code,
string namespacename, string classname,
string functionname, bool isstatic, params object[] args)
{
object returnval = null;
Assembly asm = BuildAssembly(code);
object instance = null;
Type type = null;
if (isstatic)
{
type = asm.GetType(namespacename + "." + classname);
}
else
{
instance = asm.CreateInstance(namespacename + "." + classname);
type = instance.GetType();
}
MethodInfo method = type.GetMethod(functionname);
returnval = method.Invoke(instance, args);
return returnval;
}
private void scanAndCacheAssemblies()
{
/*
foreach (string str in Directory.GetFiles(@"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727"))
{
if (str.Contains(".dll"))
{
foreach (string st in str.Split(new char[] { '\\' }))
{
if (st.Contains(".dll"))
{
assemblies.Add(st);
}
}
}
}
* */
assemblies.Add("Accessibility.dll");
assemblies.Add("AspNetMMCExt.dll");
assemblies.Add("cscompmgd.dll");
assemblies.Add("CustomMarshalers.dll");
assemblies.Add("IEExecRemote.dll");
assemblies.Add("IEHost.dll");
assemblies.Add("IIEHost.dll");
assemblies.Add("Microsoft.Build.Conversion.dll");
assemblies.Add("Microsoft.Build.Engine.dll");
assemblies.Add("Microsoft.Build.Framework.dll");
assemblies.Add("Microsoft.Build.Tasks.dll");
assemblies.Add("Microsoft.Build.Utilities.dll");
assemblies.Add("Microsoft.Build.VisualJSharp.dll");
assemblies.Add("Microsoft.CompactFramework.Build.Tasks.dll");
assemblies.Add("Microsoft.JScript.dll");
assemblies.Add("Microsoft.VisualBasic.Compatibility.Data.dll");
assemblies.Add("Microsoft.VisualBasic.Compatibility.dll");
assemblies.Add("Microsoft.VisualBasic.dll");
assemblies.Add("Microsoft.VisualBasic.Vsa.dll");
assemblies.Add("Microsoft.Vsa.dll");
assemblies.Add("Microsoft.Vsa.Vb.CodeDOMProcessor.dll");
assemblies.Add("Microsoft_VsaVb.dll");
assemblies.Add("mscorlib.dll");
assemblies.Add("sysglobl.dll");
assemblies.Add("System.configuration.dll");
assemblies.Add("System.Configuration.Install.dll");
assemblies.Add("System.Data.dll");
assemblies.Add("System.Data.OracleClient.dll");
assemblies.Add("System.Data.SqlXml.dll");
assemblies.Add("System.Deployment.dll");
assemblies.Add("System.Design.dll");
assemblies.Add("System.DirectoryServices.dll");
assemblies.Add("System.DirectoryServices.Protocols.dll");
assemblies.Add("System.dll");
assemblies.Add("System.Drawing.Design.dll");
assemblies.Add("System.Drawing.dll");
assemblies.Add("System.EnterpriseServices.dll");
assemblies.Add("System.Management.dll");
assemblies.Add("System.Messaging.dll");
assemblies.Add("System.Runtime.Remoting.dll");
assemblies.Add("System.Runtime.Serialization.Formatters.Soap.dll");
assemblies.Add("System.Security.dll");
assemblies.Add("System.ServiceProcess.dll");
assemblies.Add("System.Transactions.dll");
assemblies.Add("System.Web.dll");
assemblies.Add("System.Web.Mobile.dll");
assemblies.Add("System.Web.RegularExpressions.dll");
assemblies.Add("System.Web.Services.dll");
assemblies.Add("System.Windows.Forms.dll");
assemblies.Add("System.XML.dll");
assemblies.Add("vjscor.dll");
assemblies.Add("vjsjbc.dll");
assemblies.Add("vjslib.dll");
assemblies.Add("vjslibcw.dll");
assemblies.Add("vjssupuilib.dll");
assemblies.Add("vjsvwaux.dll");
assemblies.Add("vjswfc.dll");
assemblies.Add("VJSWfcBrowserStubLib.dll");
assemblies.Add("vjswfccw.dll");
assemblies.Add("vjswfchtml.dll");
assemblies.Add("Accessibility.dll");
assemblies.Add("AspNetMMCExt.dll");
assemblies.Add("cscompmgd.dll");
assemblies.Add("CustomMarshalers.dll");
assemblies.Add("IEExecRemote.dll");
assemblies.Add("IEHost.dll");
assemblies.Add("IIEHost.dll");
assemblies.Add("Microsoft.Build.Conversion.dll");
assemblies.Add("Microsoft.Build.Engine.dll");
assemblies.Add("Microsoft.Build.Framework.dll");
assemblies.Add("Microsoft.Build.Tasks.dll");
assemblies.Add("Microsoft.Build.Utilities.dll");
assemblies.Add("Microsoft.Build.VisualJSharp.dll");
assemblies.Add("Microsoft.CompactFramework.Build.Tasks.dll");
assemblies.Add("Microsoft.JScript.dll");
assemblies.Add("Microsoft.VisualBasic.Compatibility.Data.dll");
assemblies.Add("Microsoft.VisualBasic.Compatibility.dll");
assemblies.Add("Microsoft.VisualBasic.dll");
assemblies.Add("Microsoft.VisualBasic.Vsa.dll");
assemblies.Add("Microsoft.Vsa.dll");
assemblies.Add("Microsoft.Vsa.Vb.CodeDOMProcessor.dll");
assemblies.Add("Microsoft_VsaVb.dll");
assemblies.Add("mscorlib.dll");
assemblies.Add("sysglobl.dll");
assemblies.Add("System.configuration.dll");
assemblies.Add("System.Configuration.Install.dll");
assemblies.Add("System.Data.dll");
assemblies.Add("System.Data.OracleClient.dll");
assemblies.Add("System.Data.SqlXml.dll");
assemblies.Add("System.Deployment.dll");
assemblies.Add("System.Design.dll");
assemblies.Add("System.DirectoryServices.dll");
assemblies.Add("System.DirectoryServices.Protocols.dll");
assemblies.Add("System.dll");
assemblies.Add("System.Drawing.Design.dll");
assemblies.Add("System.Drawing.dll");
assemblies.Add("System.EnterpriseServices.dll");
assemblies.Add("System.Management.dll");
assemblies.Add("System.Messaging.dll");
assemblies.Add("System.Runtime.Remoting.dll");
assemblies.Add("System.Runtime.Serialization.Formatters.Soap.dll");
assemblies.Add("System.Security.dll");
assemblies.Add("System.ServiceProcess.dll");
assemblies.Add("System.Transactions.dll");
assemblies.Add("System.Web.dll");
assemblies.Add("System.Web.Mobile.dll");
assemblies.Add("System.Web.RegularExpressions.dll");
assemblies.Add("System.Web.Services.dll");
assemblies.Add("System.Windows.Forms.dll");
assemblies.Add("System.XML.dll");
assemblies.Add("vjscor.dll");
assemblies.Add("vjsjbc.dll");
assemblies.Add("vjslib.dll");
assemblies.Add("vjslibcw.dll");
assemblies.Add("vjssupuilib.dll");
assemblies.Add("vjsvwaux.dll");
assemblies.Add("vjswfc.dll");
assemblies.Add("VJSWfcBrowserStubLib.dll");
assemblies.Add("vjswfccw.dll");
assemblies.Add("vjswfchtml.dll");
return;
}
}
}
Это было бы большим подспорьем, Таркс, спасибо! Продолжая эту тему, можно ли превратить блок кода C# в строку?
Другой вариант - использовать DLR и ограничить выполнение кода ...
Вообще говоря, это звучит как действительно плохая идея и большая дыра в безопасности.
Вы не хотите, чтобы другой процесс выполнял какой-либо код. Поймите, для чего вам действительно нужен другой процесс, и создайте вокруг него небольшой DSL.
Безусловно, вы не можете доверять вызывающему абоненту, который действительно может предложить DSL. Взгляните на MGrammar и Осло.
Выполнение произвольного кода и / или сохранение его где-нибудь в базе данных ... что может пойти не так? Мне это не кажется антипаттерном ;-)
Вы можете попробовать использовать IronPython в своем проекте. Это банальный, чтобы делать то, что вы просите в Python. Код Python может вызывать ваши методы C#. Что касается безопасности, вы можете выполнить код в какой-либо ограниченной среде (один из примеров - RestrictedPython).
Это интересная задача, но вы, вероятно, должны описать, почему вы хотите это сделать, поскольку существует множество разных подходов в зависимости от вашей цели. Как отмечает Хампол, есть также довольно серьезные проблемы с безопасностью.
«Сериализованный код» может быть просто исходным кодом или скомпилированной сборкой, в зависимости от ваших требований. Вам, вероятно, не нужно использовать отдельный формат сериализации кода.
Если вы хотите динамически генерировать код и передавать его, вы можете сгенерировать код с помощью CodeDOM и скомпилировать его. Однако вам, скорее всего, не нужно генерировать полностью произвольный код.
ДА!!!
Мы сделали это для очень реального случая производительности. Делать это во время выполнения или использовать DSL было невозможно из-за производительности.
Мы компилируем код в сборку и извлекаем IL из метода. Затем мы получаем все метаданные, связанные с этим методом, и сериализуем весь беспорядок через XML, сжимаем его и помещаем в нашу базу данных.
Во время регидратации мы заново составляем IL с метаданными, используя класс DynamicMethod, и выполняем его.
Мы делаем это из-за скорости. У нас есть тысячи маленьких блоков кода. К сожалению, для компиляции блока кода и его запуска «на лету» требуется не менее 250 мс, что для нас слишком медленно. Мы выбрали этот подход, и он ДЕЙСТВИТЕЛЬНО работает. Во время выполнения требуется неизмеримое количество времени, чтобы воссоздать метод и запустить его.
Единственное, за чем следует следить ... Подписанные сборки и неподписанные сборки не могут смешивать сериализованные данные метода.
Это был метод, который я придумал и над которым работаю. Самая большая проблема, по-видимому, заключается в преобразовании байтового массива, который MethodBody передает, в OpCodes для использования с Reflection.Emit. Я смотрю на weblogs.asp.net/rosherove/archive/2006/04/25/…, который может помочь.
o0o это похоже на подход, который я ищу. Если у вас есть еще ресурсы, относящиеся к Think, мы будем очень благодарны за ссылки :)
Я не могу решить, было ли то, что вы сделали, потрясающим или ужасным. Возможно и то, и другое.
Удивительно или ужасно? Я думаю, и то и другое. Я действительно ничего не могу сделать, но думаю, что это грандиозный взлом ... Я был поражен, когда он наконец сработал ... потрясен тем, насколько злобным / сложным было решение.
Мы основали наш подход на этой статье: codeproject.com/KB/cs/ExpressionEval.aspx. Я должен добавить, что, поскольку мы внедрили его (с ТОННОЙ модульных тестов), у нас не было ни одной проблемы с этим подходом.
Работая с клиентом, которому приходилось обрабатывать тысячи файлов импорта каждый день из пары сотен источников, я создал универсальный инструмент импорта, в котором опытным пользователям разрешалось определять столбцы файлов и вводить строку C# для преобразования входного значения в соответствующий тип / формат. После сохранения нового определения мы динамически компилируем класс импорта для этого источника, а затем выполняем его при поступлении новых файлов. С 50 или около того типичными случаями с автозаполнением, он работал на удивление хорошо и сохранил огромное количество запросов на изменение, а также на порядок повысил скорость импорта.