I'm working on a project in C# that get scripts (VBScript, Jscript and JavaScript) from the EA database and executes certain functions at specific moments.
To be able to do this I use the Microsoft ScriptControl.
First I add all script code to the ScriptControl using ScriptControl.AddCode and then I functions with a specific name.
The code that adds the code to the script control looks like this:
//create new scriptcontroller
this.scriptController = new ScriptControl();
this.scriptController.Language = this.language.name;
this.scriptController.AddObject("Repository", model.getWrappedModel());
//Add the actual code. This must be done in a try/catch because a syntax error in the script will result in an exception from AddCode
try
{
//first add the included code
string includedCode = this.IncludeScripts(this._code);
//then remove any statements that execute a function or procedure because scriptControl.AddCode actually executes those statements
string cleanedCode = this.language.removeExecutingStatements(includedCode);
//then add the cleaned code to the scriptcontroller
this.scriptController.AddCode(cleanedCode);
The problem is that apparently AddCode also executes the script code in some way, which is not what I want.
Say I have following VBScript:
sub main
MsgBox("main executed")
end sub
main
As soon as I add the code of this script to the ScriptControl using AddCode the main sub is executed and I see the messagebox appear.
Does anyone know an easy way to avoid executing the main sub in cases like this?
My current workaround (currently only implemented for VBScript) involves parsing the script code and stripping the line that calls functions or procedures, but that is pretty tedious and error prone.
/// <summary>
/// removes the statements that execute a function/procedure from the code
/// </summary>
/// <param name="code">the code with executing statements</param>
/// <returns>the code without executing statements</returns>
public override string removeExecutingStatements(string code)
{
StringReader reader = new StringReader(code);
string cleanedCode = code;
string line;
bool functionStarted = false;
bool subStarted = false;
while (null != (line = reader.ReadLine()))
{
if (line != string.Empty)
{
if (line.StartsWith(this.functionStart))
{
functionStarted = true;
}else if (line.StartsWith(this.subStart))
{
subStarted = true;
}else if (functionStarted && line.StartsWith(this.functionEnd))
{
functionStarted = false;
}
else if (subStarted && line.StartsWith(this.subEnd))
{
subStarted = false;
}else if (!functionStarted && !subStarted)
{
//code outside of a function or sub, figure out if this code calls another sub or function
foreach (string linepart in line.Split(new char[] {' ' , '(' },StringSplitOptions.RemoveEmptyEntries))
{
if (cleanedCode.Contains(this.functionStart + linepart)
||cleanedCode.Contains(this.subStart + linepart))
{
//found a line that calls an existing function or sub, replace it by an empty string.
cleanedCode = cleanedCode.Replace(Environment.NewLine +line + Environment.NewLine,
Environment.NewLine + string.Empty + Environment.NewLine);
}
}
}
}
}
return cleanedCode;
}
Any better idea is welcome.
If you want the script code modules to act as code libraries without any static initializers then you can enforce it by declaring it in coding conventions
If you want the static initializers and other side-effects not to appear until the run-time then you can postpone the script activation through delay loading the script when it is required for the first time
If you want to have more fine-grained control over the scripting environment than you can implement a scripting host and interact with the script engines more directly (e.g. using the IActiveScriptParse
interface will not probably trigger any unexpected side-effects).
MSDN: Windows Script Interfaces
...To make implementation of the host as flexible as possible, an OLE Automation wrapper for Windows Script is provided. However, a host that uses this wrapper object to instantiate the scripting engine does not have the degree of control over the run-time name space, the persistence model, and so on, that it would if it used Windows Script directly.
The Windows Script design isolates the interface elements required only in an authoring environment so that nonauthoring hosts (such as browsers and viewers) and script engines (for example, VBScript) can be kept lightweight...