2008-12-06 11 views
18

Estoy usando C# con .NET 3.5. ¿Es posible serializar un bloque de código, transmitirlo a otro lugar, deserializarlo y luego ejecutarlo?¿Es posible serializar un bloque de código C#?

Un ejemplo de uso de esto sería:

Action<object> pauxPublish = delegate(object o) 
{ 
    if (!(o is string)) 
    { 
     return; 
    } 
    Console.WriteLine(o.ToString()); 
}; 
Transmitter.Send(pauxPublish); 

con la realización de un programa a distancia:

var action = Transmitter.Recieve(); 
action("hello world"); 

Mi objetivo final es ser capaz de ejecutar código arbitrario en un proceso diferente (que tiene sin conocimiento previo del código).

+0

Cuando trabajaba para un cliente que tenía que manejar miles de importaciones de archivos cada día desde un par de cientos de fuentes, creé una herramienta de importación genérica donde los usuarios avanzados podían definir columnas de archivos e ingresar una línea de C# para transformar la entrada valor al tipo/formato apropiado. Una vez que se haya guardado la nueva definición, compilaremos dinámicamente la clase de importación para esta fuente y luego la ejecutaremos cuando lleguen nuevos archivos. Con 50 o más casos comunes auto-completables, funcionó sorprendentemente bien y ahorró una gran cantidad de solicitudes de cambio, así como también mejoró la velocidad de importación en un orden de magnitud. – Basic

Respuesta

15

YES !!!

Hemos hecho esto para un caso muy real de rendimiento. Hacer esto en tiempo de ejecución o usar una DSL no era una opción debido al rendimiento.

Compilamos el código en un ensamblado y extraemos el IL del método.Luego obtenemos todos los metadatos asociados con este método y serializamos todo el problema a través de XML, lo comprimimos y lo ponemos en nuestra base de datos.

En el momento de la rehidratación, volvemos a constituir el IL con los metadatos utilizando la clase DynamicMethod y lo ejecutamos.

Hacemos esto debido a la velocidad. Tenemos miles de pequeños bloques de código. Desafortunadamente, compilar un bloque de código y ejecutarlo sobre la marcha requiere al menos 250 ms, lo cual es demasiado lento para nosotros. Tomamos este enfoque, y está FUNCIONANDO REALMENTE bien. En tiempo de ejecución, se necesita una cantidad de tiempo inmedible para reconstituir el método y ejecutarlo.

Lo único a tener en cuenta ... Los ensambles firmados y los ensamblajes sin firmar no pueden mezclar los datos del método serializado.

+0

Este fue el método que ideé y en el que he estado trabajando. El mayor problema parece ser la conversión de la matriz de bytes que MethodBody da en OpCodes para utilizar con Reflection.Emit. Estoy viendo http://weblogs.asp.net/rosherove/archive/2006/04/25/methodsvisualizer.aspx que podría ayudar. – ICR

+0

o0o esto suena como el enfoque que estoy buscando. Si tienes más recursos relacionados con el pensamiento, los enlaces serían muy apreciados :) – NoizWaves

+3

No puedo decidir si lo que has hecho es increíble o horrible. Posiblemente ambos. – Anthony

1

Compilarlo en un conjunto por separado, enviar el conjunto, hacer que el otro proceso lo cargue.

Es posible que desee considerar las implicaciones de seguridad.

Actualización: otra idea sería generar un árbol de expresión y el uso de esta biblioteca para serializarlo:

http://www.codeplex.com/metalinq/

3

También podría enviarlo como una cadena a continuación, utilizar el CodeDomProvider compilarlo, mismo resultado. Tengo un poco ejemplo de código de este modo:

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; 
     } 
    } 
} 
+0

¡Eso sería una gran ayuda, Tarks, gracias! Continuando con este tema, ¿es posible convertir un bloque de código C# en una cadena? – NoizWaves

0

Otra opción es usar el DLR, y limitando el código para ejecutar ...

4

En términos generales eso suena como una muy mala idea y un gran agujero de seguridad .

No desea que otro proceso ejecute ningún código. Comprenda qué necesita realmente otro proceso y cree un pequeño DSL a su alrededor.

+0

Absolutamente, no puede confiar en que la persona que llama realmente tiene que encontrar una DSL. Echa un vistazo a MGrammar y Oslo. –

+0

Ejecutar código arbitrario y/o almacenarlo en una base de datos en alguna parte ... ¿qué podría salir mal? No me parece un antipatrón ;-) –

7

Puede intentar usar IronPython en su proyecto. Es trivial para hacer lo que está pidiendo en Python. El código Python podría llamar a sus métodos C#. En cuanto a la seguridad, puede ejecutar el código en un entorno restringido de algún tipo (un ejemplo es RestrictedPython).

1

Es un desafío interesante, pero probablemente deberías describir por qué quieres hacerlo, ya que hay muchos enfoques diferentes según tu objetivo. Como señala Humpohl, también hay algunos problemas de seguridad bastante serios.

El "código serializado" podría ser simplemente un código fuente o un ensamblado compilado, según sus requisitos. Probablemente no necesite usar un formato de serialización de código separado.

Si desea generar código dinámicamente y pasarlo, puede generar código utilizando CodeDOM y compilarlo. Sin embargo, lo más probable es que no necesites generar un código completamente arbitrario.

Cuestiones relacionadas