2010-03-02 3 views
10

У меня есть настольное приложение, написанное на C#. Я хочу сделать скриптовый скрипт на C#/VB. В идеале, пользователь должен открыть боковую панель и писать такие вещи, какКак я могу настроить приложение на C#?

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

Имея подсветку синтаксиса и завершение кода было бы удивительным, но я мог бы жить без него. Я бы не хотел, чтобы пользователи имели Visual Studio 2010.

Я думаю о вызове компилятора, загрузке и запуске сборки вывода.

Есть ли лучший способ?

Есть Microsoft.CSharp ответ?

+1

Это поможет, если вы опишете, что именно вы хотите достичь. – Perpetualcoder

+1

Подождите, вы спрашиваете: «Как я могу сделать свое приложение на C# сценарием на языке сценариев?» или «Как я могу сделать свое приложение на C# сценарием на C#?» –

+1

Приложение C#, написанное на C# –

ответ

1

Используйте язык сценариев. Tcl, LUA или даже JavaScript приходит на ум.

Использование Tcl действительно легко:

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

Затем используйте это нравится:

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

Есть ли какие-либо мосты C# для любого из этих языков сценариев, и если да, то можете ли вы предоставить ссылки? –

4

Вы думали о IronPython или IronRuby?

+0

Производительность - это то, что вам нужно для контроля. +1 для ответа, хотя – Perpetualcoder

+1

Это зависит от того, насколько логика программы находится в скриптовой части и насколько тяжелый подъем - это код .NET/C#. Python обычно используется для создания сценариев с большими, высокопроизводительными сценариями на C++ (например, видеоигры, программное обеспечение для анимации Menv Pixar и т. Д.), Но Python не является обычным способом создания новых функций, но Python позволяет использовать более высокий уровень существующие высокопроизводительные элементы вместе интересными, новыми способами. –

+1

Или IronJS? немного более доступным для C# чувака. –

1

Вы все равно вызовете компилятор, потому что C# - это скомпилированный язык. Лучший способ сделать это можно проверить в CSharpCodeProvider - класс.

+1

Вы не будете вызывать компилятор во время выполнения. Однако вы будете ссылаться на clr. –

+1

вы будете вызывать компилятор. csc.exe будет вызван в любом случае, вы можете использовать рефлектор, чтобы проверить, что – Andrey

1

Я бы использовал PowerShell или MEF. Это действительно зависит от того, что вы подразумеваете под начинкой и какого типа приложения у вас есть. Самая лучшая часть в PowerShell - это прямое перемещение и прямое использование интерфейсов .NET в сценарии.

0

На каком языке написано ваше приложение? Если C++, вы можете рассмотреть Google V8, вложенный механизм ECMAScript/JavaScript.

1

Вы можете использовать следующий открытое решение источника в качестве примера: https://bitbucket.org/jlyonsmith/coderunner/wiki/Home

+0

CodePlex скоро завершает работу, поэтому вы можете подумать о том, чтобы сделать что-то до того, как ссылка сломается. –

1

Я имел точно такую ​​же проблему и с немного прибегая к помощи и нескольких модификаций я решил его с помощью Microsoft.CSharp.CSharpCodeProvider, которая позволяет пользователю отредактируйте шаблон C#, который я представляю им, который предоставляет полную объектную модель моего приложения, и они могут даже передавать параметры из/и возвращать результат самому приложению.

Полное решение для C# можно скачать с http://qurancode.com. Но вот основной код, который делает только что:

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

Затем я определил C# интерфейс, который будет реализован с помощью кода пользователя, где они могут свободно положить anythng они, как внутри их конкретного метода Run:

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

А вот шаблон запуска пользователь может распространяется:

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

И это, как я называю это из моего 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 

Тем не менее, нужно сделать выделение синтаксиса и CodeCompletion.

Удачи вам!

+0

Re * "раскрывает полную объектную модель моего приложения" *: что такое объектная модель? Все ли (общедоступные) классы с их общедоступными методами/свойствами или что? –

 Смежные вопросы

  • Нет связанных вопросов^_^