2010-03-02 18 views
10

Ho un'applicazione desktop scritta in C# Mi piacerebbe creare script su C#/VB. Idealmente, l'utente avrebbe aperto un riquadro laterale e scrivere le cose comeCome posso rendere la mia applicazione scrivibile in C#?

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

Avere evidenziazione della sintassi e completamento del codice sarebbe terribile, ma ho potuto vivere senza di essa. Vorrei non desidera richiedere agli utenti di avere Visual Studio 2010 installato.

Sto pensando di richiamare il compilatore, caricare ed eseguire l'assembly di output.

C'è un modo migliore?

Is Microsoft.CSharp la risposta?

+1

Sarebbe utile se si descrivesse esattamente ciò che si desidera ottenere. – Perpetualcoder

+1

Aspetta, stai chiedendo, "Come posso rendere la mia applicazione C# scrivibile in un linguaggio di scripting?" oppure "Come posso rendere la mia applicazione C# scrivibile in C#?" –

+1

Applicazione C# con script in C# –

risposta

1

Utilizzare un linguaggio di scripting. Tcl, LUA o anche JavaScript viene in mente.

Utilizzando Tcl è davvero semplice:

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); 
       } 
      } 
     } 
    } 
} 

quindi utilizzarlo come:

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

Esistono ponti C# per questi linguaggi di script e, in caso affermativo, è possibile fornire collegamenti? –

4

Avete pensato IronPython o IronRuby?

+0

Le prestazioni possono essere necessarie per il monitoraggio. +1 per la risposta però – Perpetualcoder

+1

Dipende da quanta logica di programma si trova nella parte di scripting e quanta parte del pesante sollevamento è il codice .NET/C#. Python è comunemente usato per programmare script C++ di grandi dimensioni e ad alte prestazioni (ad es. Videogiochi, software di animazione interno Menv di Pixar, ecc.) Ma Python non è il modo convenzionale di costruire nuove funzionalità, ma piuttosto Python consentirebbe un modo di mettere a livello più alto i pezzi esistenti ad alte prestazioni insieme in modi interessanti e nuovi. –

+1

o IronJS? un po 'più accessibile a un C#. –

1

Invocherai comunque il compilatore, perché C# è un linguaggio compilato. Il modo migliore per farlo può essere verificato in CSharpCodeProvider - класс.

+1

Non si richiamerà il compilatore in fase di esecuzione. Invocherai comunque il clr. –

+1

richiamerai il compilatore. csc.exe verrà chiamato in ogni caso, è possibile utilizzare Reflector per verificare che – Andrey

1

Vorrei utilizzare PowerShell o MEF. Dipende davvero da cosa intendi per scritable e che tipo di applicazione hai. La parte migliore di PowerShell è che è direttamente hostable e progettata direttamente per l'utilizzo delle interfacce .NET in modo scripting.

0

In quale lingua è scritta la domanda? Se C++, potresti considerare Google V8, un motore ECMAScript/JavaScript integrabile.

1

È possibile utilizzare la seguente soluzione open source come esempio: https://bitbucket.org/jlyonsmith/coderunner/wiki/Home

+0

CodePlex si arresti presto, quindi è possibile prendere in considerazione l'idea di fare qualcosa prima che il collegamento venga interrotto. –

1

Ho avuto lo stesso problema e con un po 'di googling e poche modifiche ho risolto utilizzando Microsoft.CSharp.CSharpCodeProvider che permette all'utente di modificare un modello C# che presento a loro che espone il modello di oggetto completo della mia applicazione e possono persino passare parametri da/e restituire i risultati all'applicazione stessa.

La soluzione C# completa può essere scaricata da http://qurancode.com. Ma qui è il codice principale che fa proprio questo:

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

Ho poi definito un C# interfaccia essere attuata entro il codice utente in cui sono liberi di mettere anythng loro piace all'interno della loro concreta metodo Run:

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

E qui è il modello di avvio l'utente può, si estende:

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

e questo è come lo chiamo io dai miei 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 

Tuttavia, l'evidenziazione della sintassi e il completamento del codice tuttavia non sono disponibili.

Buona fortuna!

+0

Ri * "espone il modello a oggetti completo della mia applicazione" *: che cos'è il modello a oggetti? Sono tutte le classi (pubbliche) con i loro metodi/proprietà pubblici o cosa? –

Problemi correlati