Title: Run user-entered code in C#
This example shows how to compile and run user-entered code at run time. The form includes the following using statements.
using System.CodeDom.Compiler;
using System.Reflection;
The following code executes when you click the Run button.
// Compile and execute the code.
private void btnRun_Click(object sender, EventArgs e)
{
txtResults.Clear();
CodeDomProvider code_provider =
CodeDomProvider.CreateProvider("C#");
// Generate a non-executable assembly in memory.
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
parameters.GenerateExecutable = false;
// Add references used by the code. (This one is used by
// MessageBox.)
parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");
// Compile the code.
CompilerResults results =
code_provider.CompileAssemblyFromSource(parameters,
txtCode.Text);
// If there are errors, display them.
if (results.Errors.Count > 0)
{
foreach (CompilerError compiler_error in results.Errors)
{
txtResults.AppendText(
"Line: " + compiler_error.Line + ", " +
"Error Number: " + compiler_error.ErrorNumber +
", " + compiler_error.ErrorText + "\n");
}
}
else
{
// There were no errors.
txtResults.Text = "Success!";
// Get the compiled method and execute it.
foreach (Type a_type in results.CompiledAssembly.GetTypes())
{
if (!a_type.IsClass) continue;
if (a_type.IsNotPublic) continue;
// Get a MethodInfo object describing the SayHi method.
MethodInfo method_info = a_type.GetMethod("SayHi");
if (method_info != null)
{
// Make the parameter list.
object[] method_params = new object[]
{
"This is the parameter string. Isn't it great?"
};
// Execute the method.
DialogResult method_result =
(DialogResult)method_info.Invoke(null,
method_params);
// Display the returned result.
MessageBox.Show(method_result.ToString());
}
}
}
}
The code first clears any old results. It then creates a CodeDomProvider to work with C# code. (My next post will show how to figure out what other languages you can use.)
Next the code creates a parameters object. It sets the GenerateInMemory property to true to indicate that the compiler should compile into memory (as opposed to creating a compiled file). It sets the GenerateExecutable property to false to indicate that it should create an assembly that this program can invoke (as opposed to a standalone executable).
The program then adds a reference to the System.Windows.Forms library that the code needs to use MessageBox.Show. If the user-entered code needs other libraries, you should include them here.
Now the program compiles the user-entered code. It calls the code provider's CompileAssemblyFromSource method passing it the parameters selected by the code and the user-entered code. When the CompileAssemblyFromSource method returns, the program examines the results returned by the method.
If the results indicate errors, the program loops through the errors and displays their line numbers, error codes, and error messages. (You should try introducing an error into the code to see what happens. In a real application, you might list the errors in a ListBox and jump to the appropriate line when the user clicks on one.
If there are no errors in the user-entered code, then the code has been compiled successfully so the program displays a success message. It then uses reflection to examine the object types created by the compiled code. It looks for a class that is public and tries to create a MethodInfo object describing that class's SayHi method. That is just the convention that I decided to use. You can look for whatever class and method you find appropriate for your program.
If the program succeeds in creating the MethodInfo object, then the SayHi method exists for this class and the program invokes it. The code creates an array to hold parameters for the method, in this case a single string. It then calls the MethodInfo object's Invoke method, passing it the array of parameters. The null used as the first parameter would represent the object for which the method should be invoked, but the SayHi method defined in this example is a static method so it doesn't need an object. This example's SayHi method returns a DialogResult (to show which button the user clicks on the MessageBox) so the program casts the return result into this data type and displays it.
The following code shows the text that the program initially uses for its code entered at run time.
using System.Windows.Forms;
public static class MyScriptClass
{
public static DialogResult SayHi(string msg)
{
return MessageBox.Show(msg, "Message",
MessageBoxButtons.YesNoCancel);
}
}
This code defines a static class named MyScriptClass. That class defines the single static method SayHi. The SayHi method displays whatever parameter it is passed and returns the DialogResult returned by the call to MessageBox.Show.
Note: You should be very wary of user-entered code. The user-entered code could potentially perform any actions that the user could. For example, the code might be able to download and install a virus, erase a hard drive, or perform other destructive actions. It might make sense to let the program execute scripts that you have written or that a power user writes, but you probably should not execute any old code that you find floating around on the Internet without carefully examining it first. |
Download the example to experiment with it and to see additional details.
|