我们正在编写一个复杂的桌面应用程序,需要提供灵活的报告格式,因此我们认为只需将对象模型公开给脚本语言即可。在过去,这意味着使用VBA(仍然是一个选项),但托管代码派生的VSTA似乎已经失去了生命力。
现在,在Windows.NET上嵌入脚本语言的最佳选择是什么?
我们正在编写一个复杂的桌面应用程序,需要提供灵活的报告格式,因此我们认为只需将对象模型公开给脚本语言即可。在过去,这意味着使用VBA(仍然是一个选项),但托管代码派生的VSTA似乎已经失去了生命力。
现在,在Windows.NET上嵌入脚本语言的最佳选择是什么?
个人建议使用C#作为脚本语言。.NET框架(包括Mono,感谢Matthew Scharley)实际上在框架内部为每种.NET语言都包含了编译器。
基本上,该系统的实现有两个部分。
允许用户编译代码 这相对容易,并且可以只用几行代码完成(尽管您可能需要添加一个错误对话框,这可能需要更多几十行代码,具体取决于您希望它有多易用)。
创建和使用包含在已编译程序集中的类 这比前一步骤要困难一些(需要少量反射)。 基本上,您应该将已编译的程序集视为程序的“插件”。 在C#中有很多教程可以介绍各种创建插件系统的方法(Google是您的朋友)。
我已经实现了一个“快速”应用程序来演示如何实现此系统(包括2个工作脚本!)。 这是应用程序的完整代码,只需创建一个新的应用程序,然后将代码粘贴到“ program.cs”文件中。 此时我必须为即将粘贴的大块代码道歉(我本不想它如此庞大,但在我的评论中有些过度热衷)。
using System;
using System.Windows.Forms;
using System.Reflection;
using System.CodeDom.Compiler;
namespace ScriptingInterface
{
// 定义脚本接口
public interface IScriptType1
{
string RunScript(int value);
}
}
namespace ScriptingExample
{
static class Program
{
///
/// 应用程序的主入口点。
///
[STAThread]
static void Main()
{
// 编译代码(我很懒,所以我会硬编码它,你肯定可以想出如何从文件/文本框中读取)
Assembly compiledScript = CompileCode(
"namespace SimpleScripts" +
"{" +
" public class MyScriptMul5 : ScriptingInterface.IScriptType1" +
" {" +
" public string RunScript(int value)" +
" {" +
" return this.ToString() + \" just ran! Result: \" + (value*5).ToString();" +
" }" +
" }" +
" public class MyScriptNegate : ScriptingInterface.IScriptType1" +
" {" +
" public string RunScript(int value)" +
" {" +
" return this.ToString() + \" just ran! Result: \" + (-value).ToString();" +
" }" +
" }" +
"}");
if (compiledScript != null)
{
// 执行脚本
RunScript(compiledScript);
}
}
// 编译代码
static Assembly CompileCode(string code)
{
// 创建代码提供程序
// 此类实现“CodeDomProvider”类作为其基础。所有当前的.Net语言(至少Microsoft的)都带有自己的实现,
// 因此您可以允许用户使用他们选择的语言(尽管我建议您不要允许使用C++,因为它对于脚本使用来说太不稳定了——任何内存泄漏吗?)
Microsoft.CSharp.CSharpCodeProvider csProvider = new Microsoft.CSharp.CSharpCodeProvider();
// 设置选项
CompilerParameters options = new CompilerParameters();
options.GenerateExecutable = false; // 我们需要一个DLL文件(或者在.Net中称为“Class Library”)
options.GenerateInMemory = true; // 当我们完成后节省了删除DLL的工作,虽然您可能将其设置为false并节省启动时间,因为下次无需重新编译即可启动
// 并设置您想要的任何其他选项,有相当多的选项,花点时间浏览所有这些选项,并决定哪些最适合您的应用程序!
// 添加您希望用户能够访问的任何引用,但请注意,给他们访问某些类的权限可以允许写入和执行有害代码。
// 我建议你编写自己的类库,只允许它允许的操作,因此他们只能做你想要的事情。
// (虽然像“System.Xml.dll”这样的东西可能很有用,只需要提供一种用户可以读取文件并将其传递给它的方式)
// 为了避免太多膨胀在这个例子中,我们将仅向其添加此程序作为其引用,这样我们就不需要另一个
// 项目来存储两者都使用的接口的接口。只要记住,这将公开所有公共类以
// “脚本”
options.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
// 编译我们的代码
CompilerResults result;
result = csProvider.CompileAssemblyFromSource(options, code);
if (result.Errors.HasErrors)
{
// TODO: 向用户报告脚本出错了
return null;
}
if (result.Errors.HasWarnings)
{
// TODO: 告诉用户警告,可能会
csscript.net
看起来像是对此的某种包装器。这基本上是一个最简实现。我认为一个更好的问题应该是“csscript.net 对我有什么作用,而这个代码没有”。我不知道 csscript 在做什么,但他们肯定知道这里的上述代码在做什么,他们在他们的库中使用它(或者极其相似的代码)。 - Grant PetersPowerShell引擎旨在轻松嵌入应用程序中,以使其可脚本化。实际上,PowerShell CLI只是引擎的基于文本的接口。
编辑:请参见https://devblogs.microsoft.com/powershell/making-applications-scriptable-via-powershell/
Boo language.
如上所述,IronRuby是一个很有意思的项目。对于我这样的C#程序员来说,Mono中的C# Eval支持也是一个非常吸引人的项目。但目前还不可用(将成为Mono 2.2的一部分)。
我推荐使用IronPython。它的嵌入非常简单,与.Net类的交互也很直接,而且它本身就是Python。