Jump to content
  • Advertisement
Sign in to follow this  
Shinkage

[.net] Easy Scripting in .NET

This topic is 3859 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

For all those who haven't experimented with it, the Microsoft.CSharp namespace in the .NET BCL provides access to a fully functioning C# compiler. Using this it only takes a few simple steps to add powerful scripting support to any project. For those interested, I'll demonstrate the simplest way to get such a system up and running with only a few lines of code. First we'll want access to the namespaces where all this functionality resides.
using Microsoft.CSharp;
using System.CodeDom;
using System.CodeDom.Compiler;
Next up we need to create the objects that will actually do the work of compiling our scripts for us.
CSharpCodeProvider	codeProvider = new CSharpCodeProvider();
ICodeCompiler		codeCompiler = codeProvider->CreateCompiler();
With that ready, the compilation parameters need to be set up so the compile knows exactly what we want made. Most of these options are pretty straightforward and obvious. Pay close attention to the ReferencedAssemblies property of the compiler parameters however. This is where we tell the compiler which assemblies we want our script to have access to. For example, if the script is going to be doing any works with forms, it'll need to reference "System.Windows.Forms.dll."
CompilerParameters	params = new CompilerParameters();
params.GenerateExecutable = false;
params.GenerateInMemory = true;
params.IncludeDebugInformation = false;
params.TreatWarningsAsErrors = false;
params.ReferencedAssemblies.Add( "System.dll" );
params.ReferencedAssemblies.Add( "System.Windows.Forms.dll" );
CompilerResults		results = codeCompiler.CompileAssemblyFromFile( params, "MyScript.cs" );
At this point if the compiler met with any errors they can be found in the results.Errors member. Otherwise, we now have a new assembly loaded into memory and ready for use. Using the System.Reflection namespace, any types or methods declared in the source file can be discovered and used. Let's assume that the source file looks like the following:
// MyScript.cs

using System;
using System.Windows.Forms;

class Script
{
	public static void Main()
	{
		MessageBox.Show( "This is a script!" );
	}
}
Given that source after compilation by the preceding steps, the following will be sufficient to call the static Main method.
results.CompiledAssembly.GetType( "Script").GetMethod( "Main" ).Invoke( null, null );
Of course that only covers the basics, but there you have it.

Share this post


Link to post
Share on other sites
Advertisement
An excellent example.

For those who are going to implement such a setup however, I would sugest using another AppDomain and giving it restricted security settings, so that it's harder for malicious scripts to be used.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Great.

So how do we do that? ;)

Share this post


Link to post
Share on other sites
The managed DX scripting example uses exactly this technique.


Here a code snippet how to setup a "sandbox"-AppDomain

public class ScriptRunner : MarshalByRefObject // make class remotable
{
ICodeCompiler compiler;
CompilerParameters param;

public static ScriptRunner CreateInSeparateDomain()
{
AppDomain dom = AppDomain.CreateDomain( "sandbox" );
return dom.CreateInstanceAndUnwrap( typeof( ScriptRunner ).Assembly.GetName().Name,
typeof( ScriptRunner ).FullName ) as ScriptRunner;
}

public ScriptRunner()
{
string parameters = "";
// C# compiler
compiler = new CSharpCodeProvider().CreateCompiler();
// parameters = "/debug+"; // uncomment for debugging

// uncomment for JScript.NET
//compiler = new JScriptCodeProvider().CreateCompiler();
//parameters ="/debug+ /versionsafe+ ";

param = new CompilerParameters();
param.CompilerOptions = parameters;
param.GenerateExecutable = false;
param.GenerateInMemory = true;
param.IncludeDebugInformation = true;
param.ReferencedAssemblies.Add( "Scripting.dll" ); // whatever you need here

// set policy
PolicyLevel level = PolicyLevel.CreateAppDomainLevel();
PermissionSet permissions = new PermissionSet( PermissionState.None );
// uncomment all permissions you need
// (never allow "Assertion"...)
// which flags are required minimally also depends
// on .NET runtime Version
SecurityPermissionFlag permissionFlags =
// SecurityPermissionFlag.Assertion |
// SecurityPermissionFlag.BindingRedirects |
// SecurityPermissionFlag.ControlAppDomain |
// SecurityPermissionFlag.ControlDomainPolicy |
// SecurityPermissionFlag.ControlEvidence |
// SecurityPermissionFlag.ControlPolicy |
// SecurityPermissionFlag.ControlThread |
// SecurityPermissionFlag.ControlPrincipal |
// SecurityPermissionFlag.Infrastructure |
// SecurityPermissionFlag.RemotingConfiguration |
// SecurityPermissionFlag.SerializationFormatter |
// SecurityPermissionFlag.Infrastructure |
SecurityPermissionFlag.SkipVerification |
SecurityPermissionFlag.UnmanagedCode|
SecurityPermissionFlag.Execution;

permissions.AddPermission( new SecurityPermission( permissionFlags ) );

// allow reflection
permissions.AddPermission( new ReflectionPermission( ReflectionPermissionFlag.AllFlags ) );

PolicyStatement policy = new PolicyStatement( permissions, PolicyStatementAttribute.Exclusive );
CodeGroup group = new UnionCodeGroup( new AllMembershipCondition(), policy );
level.RootCodeGroup = group;
AppDomain.CurrentDomain.SetAppDomainPolicy( level );
}
}

// add code for compiling and running scripts
}





Regards
Andre

Share this post


Link to post
Share on other sites
A note: it is important to create the compiler in the sandbox domain. Else the assembly created would automatically bleed into the main domain. This also means that you must set the permissions AFTER the compiler was created, as the compiler requires some special permissions.

Regards,
Andre

Share this post


Link to post
Share on other sites
this is interesting...

I'm just wondering tho...

how might you use this within an html browser?


<html>
<head>
<title>c#</title>
</head>
<body>
<script language="c#">
// MyScript.cs

using System;
using System.Windows.Forms;

class Script
{
public static void Main()
{
MessageBox.Show( "This is a script!" );
}
}
</script>
</body>
</html>




or


<html>
<head>
<title>c#</title>
</head>
<body>
<script language="c#" src="MyScript.cs">
</script>
</body>
</html>



I find this cool as a matter of fact...
I might like to create an enhanced version of vbscript using visual basic .net that easily implements directx coponents.

of course you could achieve directx by using com in a script refering to directAnimation, but that part of directx is dead and why use the simple scripting languages when you can be proud of making your own? Who doesn't want to feel accomplished?

Share this post


Link to post
Share on other sites
wait a sec...

your not talking a bout the scripting I was thinking of...

dang... that c# code is not meant for the "script" box I guess...

you could of course go make .net apps by means of asp pages, but it adds some overhead if youre trying to create one from scratch

ie: without visual studio.net

any possible way to have c# go into the script tag with simplicity... aka the standard html+script approach.

thanx...

Share this post


Link to post
Share on other sites
C# scripts in web pages are parsed by the web server and execute through ASP.NET before the web page data is sent to the user. They don't act the same as javascript blocks, which are interpreted and run on the user's web client. So no, you can't use C# inside a web page without using ASP.NET.

This is a very good introductory walktrough. I'd read about the CSharp namespace, but never looked into using it. I'd better look again!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!