Skriptmotor för C# och VB.NET - del 2
Förord
I denna andra och sista del av mini-artikelserien om konsten att hantera C# och VB.NET scripting skriver vi en mycket simpel kod-editor för att testa skriptkompilatorn ifrån första artikeln.Innehåll
»»
»
»
»
»
»
»
»
Relaterade artiklar
» Skriptmotor för C# och VB.NET - del 1
Inledning och uppdelning
</h3>
I denna andra delen ska vi skriva en mycket enkel kodeditor som låter oss testa vår skriptkompilator ifrån förra delen (se Skriptmotor för C# och VB.NET - del 1). Vår applikation kommer att bestå av ett Windows form med en textarea för koden, en annan för meddelande ifrån skriptkompilatorn (vid kompilering), en ”splitter” samt två knappar: En för att kompilera och en annan för att köra om kompileringen lyckades. För att göra editorn en aning mer väluppfostrad än ett vanligt fönster ska vi dessutom lyssna på fönstrets Resize event.
Då kör vi…
Kodeditorn
</h3>
I din solution (ifrån förra artikeln) skapar du ett nytt ”Windows application” projekt och döper det till något lämpligt. Lägg först till en referens till vårt första projekt. (I Solution explorer: Högerklicka på References och välj Add reference. Välj sedan fliken Projects och dubbelklicka på ”Scripting”, d v s namnet på vårt första projekt där skriptkompilatorn finns.) I det nya formulärets kodfil (Form1.cs) lägger du sedan till ”uses My.Scripting;” till dina refererade namespaces så att vi kommer åt vår skriptkompilator.
Hoppa över till formulärets design. I fönstret du får upp ändrar du formulärets Text egenskap till ”Testing script compiler”. Därefter kastar du i snabb följd in (i denna ordningen) dessa kontrollerna…
- En TextBox, döpt till ”TxtScript”; sätt egenskapen ”Dock” till ”Top”.
- En splitter; sätt egenskapen ”Dock” till ”Top”.
- En TextBox döpt till ”TxtMessages”; sätt egenskapen ”Dock” till ”Top”.
- Panel; sätt egenskapen ”Dock” till ”Top”.
- I panelen en knapp, döpt till ”BtnCompile.”
- I panelen ännu en knapp, döpt till ”BtnRun”.
Snygga till formuläret efter eget huvud (och smak).
För att formuläret ska prioritera sin yta så att våra skripter får mest plats så lägger vi in följande kod i formulärets...
Resize eventet
int oldHeight;
private void Form1_Resize(object sender, System.EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
return;
int test = this.Height-oldHeight;
txtScript.Height += this.Height-oldHeight;
oldHeight = this.Height;
}
Koden ändrar alltså storleken på script-textboxen om du drar i fönstrets kanter. Eftersom Resize-eventet även anropas om du minimerar fönstret så testar vi detta och hoppar ut i så fall.
Kompileringen
Att lägga in skripter i textboxen är förstås något som du själv (användaren) sår fixa när vi kör applikationen men koden ska ju kompileras också. Ändra texten i knappen BtnCompile till ”Compile” (eller kanske ”Kompilera” om du brinner för modersmålet :o). Dubbelklicka därefter på knappen; detta provocerar VS.NET att automatiskt bygga ett kod-skelett åt dig för Click-eventet. Skriv koden så här:
Metoden BtnCompile_Click()
IScript scriptObject;
private void BtnCompile_Click(object sender, System.EventArgs e)
{
BtnRun.Enabled = false;
ScriptCompiler compiler = new ScriptCompiler();
scriptObject = compiler.Compile(txtScript.Text);
Exception error = compiler.Error;
if (error != null)
{
scriptObject = null;
showError(error);
}
else
{
TxtMessages.Text = "Compilation succeeds without errors! :o)";
BtnRun.Enabled = true;
}
}
Observera att vi behöver ett IScript, d v s en instans av den kompilerade skript-klassen. Lägg denna någonstans i formulär-klassen som ett fält (i koden ovan ligger den precis före metoden). Event-koden instansierar helt enkelt bara en ScriptCompiler och kompilerar texten som finns i text-boxen TxtScript. Om några fel uppstår under kompileringen så finns dessa i kompilatorns Error egenskap och i så fall visar vi dessa via anrop till en ny metod som vi kallar showError() (vi kommer till den strax). Om allt gick bra så har vi nu ett skriptobjekt i fältet ”scriptObject”. Vi meddelar I så fall också användaren, d v s dig, att kompileringen lyckades och slutligen ”tänder” vi knappen ”BtnRun” så att du kan köra ditt skript.
Lägg nu till…
Metoden showError()
void showError(Exception error)
{
ArrayList al = new ArrayList();
al.AddRange( getErrorLines(error.Message) );
while (error.InnerException != null)
{
al.AddRange(getErrorLines(error.InnerException.Message));
error = error.InnerException;
}
txtMessages.Lines = (string[])al.ToArray(typeof(string));
}
Metoden behöver snygga till felmeddelandet genom att bryta ner det i enskilda text-rader som kan visas i vår textbox TxtMessages och detta gör vi med metoden getErrorLines() (kommer strax). Därefter prmoenerar vi ner genom InnerException hierarkin och lägger till alla ev. ”inre undantag” som också finns så att vi kan spåra alla fel lättare. Slutligen visar vi hela rasket i textboxen TxtMessages. Klart, d v s tunt, som korvspad ifrån Scan :o).
Metoden getErrorLines()
string[] getErrorLines(string message)
{
string[] test = message.Split('\n');
return test;
}
Ok, kompileringen är alltså klar och nu vill du köra skriptet. Hoppa tillbaka till formulärets designeditor och ändra namnet på knappen BtnRun till ”Run” (el dyl.) och dubbelklicka sedan på den. I knappens Click-event kodar du som följer…
Metoden BtnRun_Click()
private void BtnRun_Click(object sender, System.EventArgs e)
{
if (scriptObject != null)
try
{
Hashtable parameters = new Hashtable();
parameters["message"] = "Hej hopp!";
scriptObject.Parameters = parameters;
scriptObject.Execute();
string reply = scriptObject.Parameters["reply"];
MessageBox.Show(test);
}
catch (Exception error)
{
showError(error);
}
}
Att starta skriptet är ju inte konstigare än att anropa dess Execute() metod men vi vill ju också testa dess förmåga att kommunicera med moderapplikationen (vår kodeditor i detta exemplet). För att göra detta skapar vi ett IDictionary-objekt, i detta fallet väljer vi en Hashtable, och lägger till ett simpelt text-element med nyckeln ”message”. När vi testar att skriva ett skript senare ska vi alltså kolla efter en parameter som heter ”message” och i så fall visa detta på något sätt. Dessutom ska vi svara på meddelandet genom att lägga till en ny parameter som vi kallar ”reply”. Efter anropet till skriptet kollar vi därför om det finns en ”reply” och i så fall visar vi denna i en vanlig hederling MessageBox. Om några körfel uppstår så fångar vi dessa och anlitar metoden showError().
Ett testskript i C#
</h3>
Ok, dags att provköra till sist. Kompilera kodeditorn och starta den. Klistra sedan in följande testskript i kodfönstret:
#language = CSharp
using System;
using System.Windows.Forms;
namespace My.Scripting
{
// MyScriptObject inherits ScriptObject which, in turn, implements the IScript interface
class MyScriptObject : ScriptObject
{
public override object Execute()
{
string message = Parameters["message"] as string;
if (message != null)
{
MessageBox.Show(message);
Parameters["reply"] = "hej hopp själv!";
}
else
MessageBox.Show("Inget att göra :o(");
return null;
}
}
}
Skriptet kollar alltså om det finns en ”message” parameter och visar i så fall dess värde med en MessageBox samt svarar genom att lägga till en ”reply” parameter. Inga konstigheter egentligen. Observera att skriptet inleds med ett språkdirektiv (#language = CSharp) så att skriptkompilatorn ska kunna välja rätt .NET-kompilator. Om du tänker skriva alla dina skripter i C# så kan du strunta i denna men då måste du också instansiera din skriptkompilator med konstruktorn ScriptCompiler(ScriptLanguage) eller sätta dess egenskap DefaultLanguage innan du kompilerar. Missar du detta så kastar kompilatorn ett ScriptUnknownLanguageException exception (se föregående artikel).
Prova nu att skriva motsvarande skript med VB.NET som språk. Glöm bara inte att inleda det med direktivet #language=VisualBasic.
Det var allt för denna gången. Jag svarar gärna på frågor och tar emot tips på förbättringar via iMail.
/Jonas
Inledning och uppdelning
Kodeditorn
</h3>I din solution (ifrån förra artikeln) skapar du ett nytt ”Windows application” projekt och döper det till något lämpligt. Lägg först till en referens till vårt första projekt. (I Solution explorer: Högerklicka på References och välj Add reference. Välj sedan fliken Projects och dubbelklicka på ”Scripting”, d v s namnet på vårt första projekt där skriptkompilatorn finns.) I det nya formulärets kodfil (Form1.cs) lägger du sedan till ”uses My.Scripting;” till dina refererade namespaces så att vi kommer åt vår skriptkompilator.
Hoppa över till formulärets design. I fönstret du får upp ändrar du formulärets Text egenskap till ”Testing script compiler”. Därefter kastar du i snabb följd in (i denna ordningen) dessa kontrollerna…
- En TextBox, döpt till ”TxtScript”; sätt egenskapen ”Dock” till ”Top”.
- En splitter; sätt egenskapen ”Dock” till ”Top”.
- En TextBox döpt till ”TxtMessages”; sätt egenskapen ”Dock” till ”Top”.
- Panel; sätt egenskapen ”Dock” till ”Top”.
- I panelen en knapp, döpt till ”BtnCompile.”
- I panelen ännu en knapp, döpt till ”BtnRun”.
Snygga till formuläret efter eget huvud (och smak).
För att formuläret ska prioritera sin yta så att våra skripter får mest plats så lägger vi in följande kod i formulärets...
Resize eventet
int oldHeight;
private void Form1_Resize(object sender, System.EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
return;
int test = this.Height-oldHeight;
txtScript.Height += this.Height-oldHeight;
oldHeight = this.Height;
}
Koden ändrar alltså storleken på script-textboxen om du drar i fönstrets kanter. Eftersom Resize-eventet även anropas om du minimerar fönstret så testar vi detta och hoppar ut i så fall.
Kompileringen
Att lägga in skripter i textboxen är förstås något som du själv (användaren) sår fixa när vi kör applikationen men koden ska ju kompileras också. Ändra texten i knappen BtnCompile till ”Compile” (eller kanske ”Kompilera” om du brinner för modersmålet :o). Dubbelklicka därefter på knappen; detta provocerar VS.NET att automatiskt bygga ett kod-skelett åt dig för Click-eventet. Skriv koden så här:
Metoden BtnCompile_Click()
IScript scriptObject;
private void BtnCompile_Click(object sender, System.EventArgs e)
{
BtnRun.Enabled = false;
ScriptCompiler compiler = new ScriptCompiler();
scriptObject = compiler.Compile(txtScript.Text);
Exception error = compiler.Error;
if (error != null)
{
scriptObject = null;
showError(error);
}
else
{
TxtMessages.Text = "Compilation succeeds without errors! :o)";
BtnRun.Enabled = true;
}
}
Observera att vi behöver ett IScript, d v s en instans av den kompilerade skript-klassen. Lägg denna någonstans i formulär-klassen som ett fält (i koden ovan ligger den precis före metoden). Event-koden instansierar helt enkelt bara en ScriptCompiler och kompilerar texten som finns i text-boxen TxtScript. Om några fel uppstår under kompileringen så finns dessa i kompilatorns Error egenskap och i så fall visar vi dessa via anrop till en ny metod som vi kallar showError() (vi kommer till den strax). Om allt gick bra så har vi nu ett skriptobjekt i fältet ”scriptObject”. Vi meddelar I så fall också användaren, d v s dig, att kompileringen lyckades och slutligen ”tänder” vi knappen ”BtnRun” så att du kan köra ditt skript.
Lägg nu till…
Metoden showError()
void showError(Exception error)
{
ArrayList al = new ArrayList();
al.AddRange( getErrorLines(error.Message) );
while (error.InnerException != null)
{
al.AddRange(getErrorLines(error.InnerException.Message));
error = error.InnerException;
}
txtMessages.Lines = (string[])al.ToArray(typeof(string));
}
Metoden behöver snygga till felmeddelandet genom att bryta ner det i enskilda text-rader som kan visas i vår textbox TxtMessages och detta gör vi med metoden getErrorLines() (kommer strax). Därefter prmoenerar vi ner genom InnerException hierarkin och lägger till alla ev. ”inre undantag” som också finns så att vi kan spåra alla fel lättare. Slutligen visar vi hela rasket i textboxen TxtMessages. Klart, d v s tunt, som korvspad ifrån Scan :o).
Metoden getErrorLines()
string[] getErrorLines(string message)
{
string[] test = message.Split('\n');
return test;
}
Ok, kompileringen är alltså klar och nu vill du köra skriptet. Hoppa tillbaka till formulärets designeditor och ändra namnet på knappen BtnRun till ”Run” (el dyl.) och dubbelklicka sedan på den. I knappens Click-event kodar du som följer…
Metoden BtnRun_Click()
private void BtnRun_Click(object sender, System.EventArgs e)
{
if (scriptObject != null)
try
{
Hashtable parameters = new Hashtable();
parameters["message"] = "Hej hopp!";
scriptObject.Parameters = parameters;
scriptObject.Execute();
string reply = scriptObject.Parameters["reply"];
MessageBox.Show(test);
}
catch (Exception error)
{
showError(error);
}
}
Att starta skriptet är ju inte konstigare än att anropa dess Execute() metod men vi vill ju också testa dess förmåga att kommunicera med moderapplikationen (vår kodeditor i detta exemplet). För att göra detta skapar vi ett IDictionary-objekt, i detta fallet väljer vi en Hashtable, och lägger till ett simpelt text-element med nyckeln ”message”. När vi testar att skriva ett skript senare ska vi alltså kolla efter en parameter som heter ”message” och i så fall visa detta på något sätt. Dessutom ska vi svara på meddelandet genom att lägga till en ny parameter som vi kallar ”reply”. Efter anropet till skriptet kollar vi därför om det finns en ”reply” och i så fall visar vi denna i en vanlig hederling MessageBox. Om några körfel uppstår så fångar vi dessa och anlitar metoden showError().
Ett testskript i C#
</h3>
Ok, dags att provköra till sist. Kompilera kodeditorn och starta den. Klistra sedan in följande testskript i kodfönstret:
#language = CSharp
using System;
using System.Windows.Forms;
namespace My.Scripting
{
// MyScriptObject inherits ScriptObject which, in turn, implements the IScript interface
class MyScriptObject : ScriptObject
{
public override object Execute()
{
string message = Parameters["message"] as string;
if (message != null)
{
MessageBox.Show(message);
Parameters["reply"] = "hej hopp själv!";
}
else
MessageBox.Show("Inget att göra :o(");
return null;
}
}
}
Skriptet kollar alltså om det finns en ”message” parameter och visar i så fall dess värde med en MessageBox samt svarar genom att lägga till en ”reply” parameter. Inga konstigheter egentligen. Observera att skriptet inleds med ett språkdirektiv (#language = CSharp) så att skriptkompilatorn ska kunna välja rätt .NET-kompilator. Om du tänker skriva alla dina skripter i C# så kan du strunta i denna men då måste du också instansiera din skriptkompilator med konstruktorn ScriptCompiler(ScriptLanguage) eller sätta dess egenskap DefaultLanguage innan du kompilerar. Missar du detta så kastar kompilatorn ett ScriptUnknownLanguageException exception (se föregående artikel).
Prova nu att skriva motsvarande skript med VB.NET som språk. Glöm bara inte att inleda det med direktivet #language=VisualBasic.
Det var allt för denna gången. Jag svarar gärna på frågor och tar emot tips på förbättringar via iMail.
/Jonas
Ett testskript i C#
#language = CSharp
using System;
using System.Windows.Forms;
namespace My.Scripting
{
// MyScriptObject inherits ScriptObject which, in turn, implements the IScript interface
class MyScriptObject : ScriptObject
{
public override object Execute()
{
string message = Parameters["message"] as string;
if (message != null)
{
MessageBox.Show(message);
Parameters["reply"] = "hej hopp själv!";
}
else
MessageBox.Show("Inget att göra :o(");
return null;
}
}
}
0 Kommentarer