2010-03-02 27 views
10

Tengo una aplicación de escritorio escrita en C# Me gustaría hacer secuencias de comandos en C#/VB. Lo ideal sería que el usuario abra un panel lateral y escribir cosas como¿Cómo puedo hacer que mi aplicación sea secuenciable en C#?

foreach (var item in myApplication.Items) 
    item.DoSomething(); 

Tener resaltado de sintaxis y finalización de código sería impresionante, pero yo podría vivir sin ella. Quiero no quiero requerir que los usuarios tengan instalado Visual Studio 2010.

Estoy pensando en invocar el compilador, cargar y ejecutar el ensamblaje de salida.

¿Hay una manera mejor?

¿Es Microsoft.CSharp la respuesta?

+1

Ayudaría si describe qué es exactamente lo que quiere lograr. – Perpetualcoder

+1

Espera, ¿estás preguntando, "¿Cómo puedo hacer que mi aplicación C# se pueda escribir en un lenguaje de scripting?" o "¿Cómo puedo hacer que mi aplicación C# sea secuenciable en C#?" –

+1

Aplicación de C# programable en C# –

Respuesta

1

Utilice un lenguaje de scripting. Tcl, LUA o incluso JavaScript viene a la mente.

El uso de Tcl es muy fácil:

using System.Runtime.InteropServices; 
using System; 

namespace TclWrap { 
    public class TclAPI { 
     [DllImport("tcl84.DLL")] 
     public static extern IntPtr Tcl_CreateInterp(); 
     [DllImport("tcl84.Dll")] 
     public static extern int Tcl_Eval(IntPtr interp,string skript); 
     [DllImport("tcl84.Dll")] 
     public static extern IntPtr Tcl_GetObjResult(IntPtr interp); 
     [DllImport("tcl84.Dll")] 
     public static extern string Tcl_GetStringFromObj(IntPtr tclObj,IntPtr length); 
    } 
    public class TclInterpreter { 
     private IntPtr interp; 
     public TclInterpreter() { 
      interp = TclAPI.Tcl_CreateInterp(); 
      if (interp == IntPtr.Zero) { 
       throw new SystemException("can not initialize Tcl interpreter"); 
      } 
     } 
     public int evalScript(string script) { 
      return TclAPI.Tcl_Eval(interp,script);   
     } 
     public string Result { 
      get { 
       IntPtr obj = TclAPI.Tcl_GetObjResult(interp); 
       if (obj == IntPtr.Zero) { 
        return ""; 
       } else { 
        return TclAPI.Tcl_GetStringFromObj(obj,IntPtr.Zero); 
       } 
      } 
     } 
    } 
} 

luego usarlo como:

TclInterpreter interp = new TclInterpreter(); 
string result; 
if (interp.evalScript("set a 3; {exp $a + 2}")) { 
    result = interp.Result; 
} 
+0

¿Hay puentes C# para cualquiera de esos lenguajes de scripting y, de ser así, puede proporcionar enlaces? –

4

¿Ha pensado IronPython o IronRuby?

+0

El rendimiento es algo que es posible que deba supervisar. +1 para la respuesta, aunque – Perpetualcoder

+1

Depende de cuánta lógica de programa hay en la parte de scriping y de cuánto pesa el código .NET/C#. Python se usa comúnmente para hacer programas grandes y de alto rendimiento en C++ (por ejemplo, videojuegos, software de animación Menv interno de Pixar, etc.), pero Python no es la forma convencional de crear nuevas características, sino que Python permitiría una forma de poner de alto nivel. las piezas de alto rendimiento existentes juntas en formas interesantes y novedosas. –

+1

O IronJS? un poco más accesible para un tipo C#. –

1

Invocará el compilador de todos modos, porque C# es un lenguaje compilado. La mejor forma de hacerlo puede consultarse en CSharpCodeProvider - класс.

+1

No invocará el compilador en tiempo de ejecución. Sin embargo, invocarás el clr. –

+1

invocará el compilador. Se llamará a csc.exe de todos modos, puede usar el reflector para verificar que – Andrey

1

Yo usaría PowerShell o MEF. Realmente depende de lo que quiera decir con "scritable" y qué tipo de aplicación tiene. La mejor parte de PowerShell es que es directamente hospedable y está diseñado directamente para usar interfaces .NET de forma scripting.

0

¿En qué idioma está escrita su solicitud? Si C++, puede considerar Google V8, un motor ECMAScript/JavaScript incrustable.

1

que tenían el mismo problema y con un poco de google y algunas modificaciones Lo resuelto utilizando Microsoft.CSharp.CSharpCodeProvider que permite al usuario edito una plantilla C# que les presento que expone el Modelo de Objetos completo de mi aplicación e incluso pueden pasar parámetros de/y devolver el resultado a la aplicación misma.

La solución completa de C# se puede descargar desde http://qurancode.com. Pero aquí es el código principal que hace precisamente eso:

using System; 
using System.Text; 
using System.IO; 
using System.Collections.Generic; 
using System.Reflection; 
using System.CodeDom.Compiler; 
using Microsoft.CSharp; 
using System.Security; 
using Model; // this is my application Model with my own classes 


public static class ScriptRunner 
{ 
    private static string s_scripts_directory = "Scripts"; 
    static ScriptRunner() 
    { 
     if (!Directory.Exists(s_scripts_directory)) 
     { 
      Directory.CreateDirectory(s_scripts_directory); 
     } 
    } 

    /// <summary> 
    /// Load a C# script fie 
    /// </summary> 
    /// <param name="filename">file to load</param> 
    /// <returns>file content</returns> 
    public static string LoadScript(string filename) 
    { 
     StringBuilder str = new StringBuilder(); 
     string path = s_scripts_directory + "/" + filename; 
     if (File.Exists(filename)) 
     { 
      using (StreamReader reader = File.OpenText(path)) 
      { 
       string line = ""; 
       while ((line = reader.ReadLine()) != null) 
       { 
        str.AppendLine(line); 
       } 
      } 
     } 
     return str.ToString(); 
    } 

    /// <summary> 
    /// Compiles the source_code 
    /// </summary> 
    /// <param name="source_code">source_code must implements IScript interface</param> 
    /// <returns>compiled Assembly</returns> 
    public static CompilerResults CompileCode(string source_code) 
    { 
     CSharpCodeProvider provider = new CSharpCodeProvider(); 

     CompilerParameters options = new CompilerParameters(); 
     options.GenerateExecutable = false; // generate a Class Library assembly 
     options.GenerateInMemory = true;  // so we don;t have to delete it from disk 

     Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); 
     foreach (Assembly assembly in assemblies) 
     { 
      options.ReferencedAssemblies.Add(assembly.Location); 
     } 

     return provider.CompileAssemblyFromSource(options, source_code); 
    } 

    /// <summary> 
    /// Execute the IScriptRunner.Run method in the compiled_assembly 
    /// </summary> 
    /// <param name="compiled_assembly">compiled assembly</param> 
    /// <param name="args">method arguments</param> 
    /// <returns>object returned</returns> 
    public static object Run(Assembly compiled_assembly, object[] args, PermissionSet permission_set) 
    { 
     if (compiled_assembly != null) 
     { 
      // security is not implemented yet !NIY 
      // using Utilties.PrivateStorage was can save but not diaplay in Notepad 
      // plus the output is saved in C:\Users\<user>\AppData\Local\IsolatedStorage\... 
      // no contral over where to save make QuranCode unportable applicaton, which is a no no 
      //// restrict code security 
      //permission_set.PermitOnly(); 

      foreach (Type type in compiled_assembly.GetExportedTypes()) 
      { 
       foreach (Type interface_type in type.GetInterfaces()) 
       { 
        if (interface_type == typeof(IScriptRunner)) 
        { 
         ConstructorInfo constructor = type.GetConstructor(System.Type.EmptyTypes); 
         if ((constructor != null) && (constructor.IsPublic)) 
         { 
          // construct object using default constructor 
          IScriptRunner obj = constructor.Invoke(null) as IScriptRunner; 
          if (obj != null) 
          { 
           return obj.Run(args); 
          } 
          else 
          { 
           throw new Exception("Invalid C# code!"); 
          } 
         } 
         else 
         { 
          throw new Exception("No default constructor was found!"); 
         } 
        } 
        else 
        { 
         throw new Exception("IScriptRunner is not implemented!"); 
        } 
       } 
      } 

      // revert security restrictions 
      //CodeAccessPermission.RevertPermitOnly(); 
     } 
     return null; 
    } 

    /// <summary> 
    /// Execute a public static method_name(args) in compiled_assembly 
    /// </summary> 
    /// <param name="compiled_assembly">compiled assembly</param> 
    /// <param name="methode_name">method to execute</param> 
    /// <param name="args">method arguments</param> 
    /// <returns>method execution result</returns> 
    public static object ExecuteStaticMethod(Assembly compiled_assembly, string methode_name, object[] args) 
    { 
     if (compiled_assembly != null) 
     { 
      foreach (Type type in compiled_assembly.GetTypes()) 
      { 
       foreach (MethodInfo method in type.GetMethods()) 
       { 
        if (method.Name == methode_name) 
        { 
         if ((method != null) && (method.IsPublic) && (method.IsStatic)) 
         { 
          return method.Invoke(null, args); 
         } 
         else 
         { 
          throw new Exception("Cannot invoke method :" + methode_name); 
         } 
        } 
       } 
      } 
     } 
     return null; 
    } 

    /// <summary> 
    /// Execute a public method_name(args) in compiled_assembly 
    /// </summary> 
    /// <param name="compiled_assembly">compiled assembly</param> 
    /// <param name="methode_name">method to execute</param> 
    /// <param name="args">method arguments</param> 
    /// <returns>method execution result</returns> 
    public static object ExecuteInstanceMethod(Assembly compiled_assembly, string methode_name, object[] args) 
    { 
     if (compiled_assembly != null) 
     { 
      foreach (Type type in compiled_assembly.GetTypes()) 
      { 
       foreach (MethodInfo method in type.GetMethods()) 
       { 
        if (method.Name == methode_name) 
        { 
         if ((method != null) && (method.IsPublic)) 
         { 
          object obj = Activator.CreateInstance(type, null); 
          return method.Invoke(obj, args); 
         } 
         else 
         { 
          throw new Exception("Cannot invoke method :" + methode_name); 
         } 
        } 
       } 
      } 
     } 
     return null; 
    } 
} 

entonces he definido un # Interfaz C a ser ejecutado por el código de usuario donde son libres de poner anythng que les gusta el interior de su método de ejecución concreta:

/// <summary> 
/// Generic method runner takes any number and type of args and return any type 
/// </summary> 
public interface IScriptRunner 
{ 
    object Run(object[] args); 
} 

Y aquí es la plantilla de inicio de la lata de usuario se extiende:

using System; 
using System.Collections.Generic; 
using System.Windows.Forms; 
using System.Text; 
using System.IO; 
using Model; 

public class MyScript : IScriptRunner 
{ 
    private string m_scripts_directory = "Scripts"; 

    /// <summary> 
    /// Run implements IScriptRunner interface 
    /// to be invoked by QuranCode application 
    /// with Client, current Selection.Verses, and extra data 
    /// </summary> 
    /// <param name="args">any number and type of arguments</param> 
    /// <returns>return any type</returns> 
    public object Run(object[] args) 
    { 
     try 
     { 
      if (args.Length == 3) // ScriptMethod(Client, List<Verse>, string) 
      { 
       Client client = args[0] as Client; 
       List<Verse> verses = args[1] as List<Verse>; 
       string extra = args[2].ToString(); 
       if ((client != null) && (verses != null)) 
       { 
        return MyMethod(client, verses, extra); 
       } 
      } 
      return null; 
     } 
     catch (Exception ex) 
     { 
      MessageBox.Show(ex.Message, Application.ProductName); 
      return null; 
     } 
    } 

    /// <summary> 
    /// Write your C# script insde this method. 
    /// Don't change its name or parameters 
    /// </summary> 
    /// <param name="client">Client object holding a reference to the currently selected Book object in TextMode (eg Simplified29)</param> 
    /// <param name="verses">Verses of the currently selected Chapter/Page/Station/Part/Group/Quarter/Bowing part of the Book</param> 
    /// <param name="extra">any user parameter in the TextBox next to the EXE button (ex Frequency, LettersToJump, DigitSum target, etc)</param> 
    /// <returns>true to disply back in QuranCode matching verses. false to keep script window open</returns> 
    private long MyMethod(Client client, List<Verse> verses, string extra) 
    { 
     if (client == null) return false; 
     if (verses == null) return false; 
     if (verses.Count == 0) return false; 

     int target; 
     if (extra == "") 
     { 
      target = 0; 
     } 
     else 
     { 
      if (!int.TryParse(extra, out target)) 
      { 
       return false; 
      } 
     } 

     try 
     { 
      long total_value = 0L; 
      foreach (Verse verse in verses) 
      { 
       total_value += Client.CalculateValue(verse.Text); 
      } 
      return total_value; 
     } 
     catch (Exception ex) 
     { 
      MessageBox.Show(ex.Message, Application.ProductName); 
      return 0L; 
     } 
    } 
} 

y así es como yo lo llamo de mis MainForm.cs

#region Usage from MainForm 
if (!ScriptTextBox.Visible) 
{ 
    ScriptTextBox.Text = ScriptRunner.LoadScript(@"Scripts\Template.cs"); 
    ScriptTextBox.Visible = true; 
} 
else // if visible 
{ 
    string source_code = ScriptTextBox.Text; 
    if (source_code.Length > 0) 
    { 
     Assembly compiled_assembly = ScriptRunner.CompileCode(source_code); 
     if (compiled_assembly != null) 
     { 
      object[] args = new object[] { m_client, m_client.Selection.Verses, "19" }; 
      object result = ScriptRunner.Run(compiled_assembly, args); 
      // process result here 
     } 
    } 
    ScriptTextBox.Visible = false; 
} 
#endregion 

Sin embargo, todavía no está claro el Sintaxis resaltado y CodeCompletion.

¡Buena suerte!

+0

Re * "expone el Modelo de Objeto completo de mi aplicación" *: ¿Cuál es el modelo de objeto? ¿Son todas las clases (públicas) con sus métodos/propiedades públicas, o qué? –

Cuestiones relacionadas