如何使我的应用程序可以用C#编写脚本?

如何使我的应用程序可以用C#编写脚本?,c#,.net,scriptable,C#,.net,Scriptable,我有一个用C编写的桌面应用程序,我想在C/VB上编写脚本。 理想情况下,用户会打开一个侧窗格并编写如下内容 foreach (var item in myApplication.Items) item.DoSomething(); 有语法突出显示和代码完成将是了不起的,但我可以生活没有它。 我不想要求用户已经安装 我正在考虑调用编译器,加载并运行输出程序集 有更好的办法吗 答案是什么?使用脚本语言,甚至想到JavaScript 使用Tcl非常简单: using System.Runtim

我有一个用C编写的桌面应用程序,我想在C/VB上编写脚本。 理想情况下,用户会打开一个侧窗格并编写如下内容

foreach (var item in myApplication.Items)
   item.DoSomething();
有语法突出显示和代码完成将是了不起的,但我可以生活没有它。 我不想要求用户已经安装

我正在考虑调用编译器,加载并运行输出程序集

有更好的办法吗

答案是什么?

使用脚本语言,甚至想到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;
}

您是否考虑过使用IronRuby或IronRuby?

无论如何,您都会调用编译器,因为C#是一种编译语言。最好的方法是签入。

我会使用PowerShell或。这实际上取决于您所说的Scribable是什么意思,以及您有什么类型的应用程序。PowerShell最棒的地方在于它可以直接作为主机,并直接设计为以脚本方式使用.NET接口

您的应用程序是用什么语言编写的?如果C++,你可以考虑,一个可嵌入的ECMAScript/JavaScript引擎。

您可以使用以下开源解决方案作为示例:

我遇到了完全相同的问题,通过谷歌搜索和少量修改,我使用Microsoft.CSharp.CSharpCodeProvider解决了这个问题,它允许用户编辑我向他们展示的暴露完整对象的C#模板我的应用程序模型,它们甚至可以从/传递参数,并将结果返回到应用程序本身

完整的C#解决方案可从下载。 但以下是主要代码:

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;
    }
}
但仍然要做的是语法突出显示和CodeCompletion


祝你好运

如果你能准确描述你想要实现的目标,这会有所帮助。等等,你是在问“我怎样才能让我的C#应用程序能够用脚本语言编写脚本?”还是“我怎样才能让我的C#应用程序能够用C#编写脚本?”C#应用程序可在C#Related中编写脚本:这些脚本语言是否有C#桥?如果有,您能否提供链接?您将不会在运行时调用编译器。但是,您将调用clr。您将调用编译器。无论如何都会调用csc.exe,您可以使用reflector检查性能是否需要监控+1答案是,这取决于划线部分有多少程序逻辑,以及.NET/C#代码有多少繁重的内容。Python通常用于制作大型、高性能的C++程序脚本(例如视频游戏、Pixar的内部MVV动画软件等),但Python不是构建新特征的常规方式,而是Python将允许更高层次的方式将现有的高性能作品放在一起进行有趣的处理,新奇的方式,还是铁器?一个C#dude更容易接近一点。@Sky Sanders:我不知道IronJS。听起来很酷,我希望他们能把它放在一个稳定的版本中。COUPUTEX很快就关闭了,所以你可以考虑在链接断开之前做点什么。重新“暴露我的应用程序的完整对象模型”:什么是对象模型?是所有(公共)类及其公共方法/属性,还是什么?是的,这就是我的意思。类模型可能是更好的选择,但并不常见:)
/// <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;
        }
    }
}
#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