我们正在编写一个复杂的富桌面应用程序,需要在报告格式方面提供灵活性,因此我们认为我们只是将对象模型暴露给脚本语言。时间到了那时候意味着VBA(仍然是一种选择),但是托管代码派生的VSTA(我认为)似乎已经消亡了。
Windows .NET上的嵌入式脚本语言的最佳选择是什么?
我们正在编写一个复杂的富桌面应用程序,需要在报告格式方面提供灵活性,因此我们认为我们只是将对象模型暴露给脚本语言。时间到了那时候意味着VBA(仍然是一种选择),但是托管代码派生的VSTA(我认为)似乎已经消亡了。
Windows .NET上的嵌入式脚本语言的最佳选择是什么?
Answers:
我使用CSScript取得了惊人的效果。它确实减少了在我的可编写脚本的应用程序中进行绑定和其他低级工作的麻烦。
就个人而言,我将使用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
{
///
/// The main entry point for the application.
///
[STAThread]
static void Main()
{
// Lets compile some code (I'm lazy, so I'll just hardcode it all, i'm sure you can work out how to read from a file/text box instead
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)
{
// Create a code provider
// This class implements the 'CodeDomProvider' class as its base. All of the current .Net languages (at least Microsoft ones)
// come with thier own implemtation, thus you can allow the user to use the language of thier choice (though i recommend that
// you don't allow the use of c++, which is too volatile for scripting use - memory leaks anyone?)
Microsoft.CSharp.CSharpCodeProvider csProvider = new Microsoft.CSharp.CSharpCodeProvider();
// Setup our options
CompilerParameters options = new CompilerParameters();
options.GenerateExecutable = false; // we want a Dll (or "Class Library" as its called in .Net)
options.GenerateInMemory = true; // Saves us from deleting the Dll when we are done with it, though you could set this to false and save start-up time by next time by not having to re-compile
// And set any others you want, there a quite a few, take some time to look through them all and decide which fit your application best!
// Add any references you want the users to be able to access, be warned that giving them access to some classes can allow
// harmful code to be written and executed. I recommend that you write your own Class library that is the only reference it allows
// thus they can only do the things you want them to.
// (though things like "System.Xml.dll" can be useful, just need to provide a way users can read a file to pass in to it)
// Just to avoid bloatin this example to much, we will just add THIS program to its references, that way we don't need another
// project to store the interfaces that both this class and the other uses. Just remember, this will expose ALL public classes to
// the "script"
options.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
// Compile our code
CompilerResults result;
result = csProvider.CompileAssemblyFromSource(options, code);
if (result.Errors.HasErrors)
{
// TODO: report back to the user that the script has errored
return null;
}
if (result.Errors.HasWarnings)
{
// TODO: tell the user about the warnings, might want to prompt them if they want to continue
// runnning the "script"
}
return result.CompiledAssembly;
}
static void RunScript(Assembly script)
{
// Now that we have a compiled script, lets run them
foreach (Type type in script.GetExportedTypes())
{
foreach (Type iface in type.GetInterfaces())
{
if (iface == typeof(ScriptingInterface.IScriptType1))
{
// yay, we found a script interface, lets create it and run it!
// Get the constructor for the current type
// you can also specify what creation parameter types you want to pass to it,
// so you could possibly pass in data it might need, or a class that it can use to query the host application
ConstructorInfo constructor = type.GetConstructor(System.Type.EmptyTypes);
if (constructor != null && constructor.IsPublic)
{
// lets be friendly and only do things legitimitely by only using valid constructors
// we specified that we wanted a constructor that doesn't take parameters, so don't pass parameters
ScriptingInterface.IScriptType1 scriptObject = constructor.Invoke(null) as ScriptingInterface.IScriptType1;
if (scriptObject != null)
{
//Lets run our script and display its results
MessageBox.Show(scriptObject.RunScript(50));
}
else
{
// hmmm, for some reason it didn't create the object
// this shouldn't happen, as we have been doing checks all along, but we should
// inform the user something bad has happened, and possibly request them to send
// you the script so you can debug this problem
}
}
else
{
// and even more friendly and explain that there was no valid constructor
// found and thats why this script object wasn't run
}
}
}
}
}
}
}
csscript.net
看起来像是某种包装。这基本上是一个准系统的实现。我认为一个更好的问题是“ csscript.net对我有什么作用,而事实并非如此”。我不知道csscript在做什么,但是他们肯定知道上面的代码在做什么,他们在库中有(或非常相似的东西)。
IronPython。这是如何嵌入的概述。
PowerShell引擎被设计为易于嵌入到应用程序中以使其可编写脚本。实际上,PowerShell CLI只是引擎的基于文本的界面。
编辑:请参阅https://devblogs.microsoft.com/powershell/making-applications-scriptable-via-powershell/
为什么不尝试C#?Mono有一个很棒的新项目,特别是用于动态评估C#的项目:
如上所述的IronRuby。作为C#程序员,对我来说一个有趣的项目是Mono中的C#Eval支持。但是它尚不可用(将成为Mono 2.2的一部分)。
我没有尝试过,但是看起来很酷:
我刚刚为客户端创建了一个插件,从而允许他们在类似于VBA for Office的模块中编写C#代码。
我以前用过Lua;在Delphi应用中,但是它可以嵌入很多东西中。它用于Adobe的Photoshop Lightroom中。