【IT168 专稿】在过去的几天中,我一直在思考将.NET的部分应用程序脚本化这种可能性。
虽然我查看了现有的解决方案,但是最终,在基于内置.NET编译器的基础上,我以一种非常简单的方式实现了这种功能。
Nanoscript 就是我工作的成果。
它允许我实现下面的功能:
• 编写能够从应用程序调用的脚本函数。
• 在脚本内部定义新类使其能在脚本内部能够实例化并使用。
• 定义能够在脚本内部和外部使用的全局变量。
• 为脚本上下文导出静态程序员函数并从脚本中调用它们。
我已经为该方案添加了两个简单的示例应用程序。第一个示例是基于命令行的小型应用程序。这些仅是做一些基本的测试。使你可以对整个方案有个更好的了解。第二个示例是一个基于窗口的应用程序,用来画一个曲线:



我的脚本引擎使用起来非常简单,因为我不需要比现在更多的功能。基本上它仅仅用于编译生成的一个CS文件并生一个包含该类的程序集,该程序集封装了所有的脚本函数。NanoScript 不会使用自己的应用程序域。生成的代码在当前的应用程序域中将会执行。NanoScript不能执行外壳CS文件。它仅仅是一个简单的类库。我的目的是将其实现为一个简单的脚本游戏成为可能。如果使用谷歌搜索,你将会找到带有更多更能的优秀解决方案。
使用代码
使用Nanoscript非常简单。仅仅需要添加一个NanoScript.dll程序集合的引用并在代码中加入命名空间NanoScript即可。该示例程序使用下面的方法来编译代码,该代码被存储在一个文本框里面:
private void compileAndRunToolStripMenuItem_Click(object sender, EventArgs e)
{
isCodeCompiled = false;
// create a new scripting context
script = new ScriptEngine();
// reference the required assemblies
script.References("System.Drawing.dll");
script.References("System.Windows.Forms.dll");
// make namespaces visible
script.Using("System");
script.Using("System.Drawing");
script.Using("System.Windows");
script.Using("System.Windows.Forms");
// export the functions DrawSprite and DrawSprites to the scripting context
script.SetMemberFunction( this, "DrawSprite");
script.SetMemberFunction( this, "DrawSpriteS");
// set the code stored in textBox1
script.SetCode(this.textBox1.Text);
try
{
// try to compile
script.Compile();
}
catch (NanoScript.ScriptEngine.ScriptingContextException ex)
{ // present the compiling errors in a MessageBox
StringBuilder b = new StringBuilder();
List cc = ex.errors;
b.AppendLine("There were errors:");
foreach (ScriptEngine.ScriptingContextException.CompileError err in cc)
{
b.AppendLine("Error " + err.errNumber + " at line " +
err.line + " col " + err.column +
" : \"" + err.text+"\"" );
b.AppendLine("code: " + err.codeSnippet);
b.AppendLine();
}
MessageBox.Show(b.ToString(),"Compiling Error(s)");
return;
}
isCodeCompiled = true;
}
The functions DrawSprite and DrawSpriteS look like this:
public void DrawSprite(String name, double posx, double posy)
{
if (name == "sprite1")
{
g_mgnd.DrawImage(sprite1, new Point((int)posx, (int)posy));
}
}
public void DrawSpriteS(String name, double posx, double posy, double w, double h)
{
if (name == "sprite1")
{
g_mgnd.DrawImage(sprite1, (float)posx, (float)posy, (float)w, (float)h);
}
}
{
isCodeCompiled = false;
// create a new scripting context
script = new ScriptEngine();
// reference the required assemblies
script.References("System.Drawing.dll");
script.References("System.Windows.Forms.dll");
// make namespaces visible
script.Using("System");
script.Using("System.Drawing");
script.Using("System.Windows");
script.Using("System.Windows.Forms");
// export the functions DrawSprite and DrawSprites to the scripting context
script.SetMemberFunction( this, "DrawSprite");
script.SetMemberFunction( this, "DrawSpriteS");
// set the code stored in textBox1
script.SetCode(this.textBox1.Text);
try
{
// try to compile
script.Compile();
}
catch (NanoScript.ScriptEngine.ScriptingContextException ex)
{ // present the compiling errors in a MessageBox
StringBuilder b = new StringBuilder();
List cc = ex.errors;
b.AppendLine("There were errors:");
foreach (ScriptEngine.ScriptingContextException.CompileError err in cc)
{
b.AppendLine("Error " + err.errNumber + " at line " +
err.line + " col " + err.column +
" : \"" + err.text+"\"" );
b.AppendLine("code: " + err.codeSnippet);
b.AppendLine();
}
MessageBox.Show(b.ToString(),"Compiling Error(s)");
return;
}
isCodeCompiled = true;
}
The functions DrawSprite and DrawSpriteS look like this:
public void DrawSprite(String name, double posx, double posy)
{
if (name == "sprite1")
{
g_mgnd.DrawImage(sprite1, new Point((int)posx, (int)posy));
}
}
public void DrawSpriteS(String name, double posx, double posy, double w, double h)
{
if (name == "sprite1")
{
g_mgnd.DrawImage(sprite1, (float)posx, (float)posy, (float)w, (float)h);
}
}
现在,我们可以在我们的应用程序中编写脚本来实现绘画。我添加了7个示例函数来呈现不同的利萨如曲线。选择示例1到7填充适当代码到代码文本框中。
点击“编译并运行”编译脚本,并在应用程序的主循环中运行它。该应用程序在脚本上下文中调用“main”函数并每秒执行50次。
private void MainFunc()
{
while(true)
{
if (isCodeCompiled)
{
g_mgnd.FillRectangle(Brushes.Black, mgndBounds);
script.Execute("main");
UpdatePicBox();
}
Thread.Sleep(20);
}
}
{
while(true)
{
if (isCodeCompiled)
{
g_mgnd.FillRectangle(Brushes.Black, mgndBounds);
script.Execute("main");
UpdatePicBox();
}
Thread.Sleep(20);
}
}
当然,这仅仅是展示如何使用脚本的一个简单示例。我仅仅是想创建一个可视化的示例来展示如何以一种非常简单的方式使用脚本。它展示在应用程序中如何定义函数并在脚本中使用它。理论上,对于使用脚本引擎实现你想要的功能没有限制。我的下一步将集成该引擎到我的呈现器中,添加这些功能使其在不用重新编译的情况下添加新的类型。