3

I need to run code from a text file, in the context of the current form (code). One of the requirements is to have the code create and add a new control to the current form.

For example, in Form1.cs:

using System.Windows.Forms;
...

public int[] someCoords = { 20, 10 };
public string someImportantString = "Hello";

public void SayHello() {
    MessageBox.Show("Hello world.");
}

private void runCodeInForm() {
    // theCode will be read from a text file
    string theCode = @"
        // Has System.Windows.Forms already added in form
        Button newButton = new Button();
        newButton.Text = someImportantString; // Hello
        newButton.Location = new Point(someCoords[0], someCoords[1]); // 20, 10

        // Add this button to the current form
        this.Controls.Add(newButton);

        this.SayHello(); // Says hello. Just an example function.
    ";

    // Execute theCode in the current form
    CodeRunner.Execute(theCode, this);
}

I have tried using CSharpCodeProvider, but it seems like this can only compile the code as a separate program.

I would like this because I want the user to be able to change this code (text file) to what they would like. It is not specifically just for creating controls, but that functionality will be needed.

I am not worried about the security of the program.

6
  • 1
    This is difficult for a good reason, it is dangerous to allow abstract code to run in the same security context as the application that started it. If you really want to do that, you'll have to compile it to an assembly and load it into the app domain and then call into that code. Commented Jun 18, 2018 at 3:04
  • You can't really add code to the current program as its image has already been loaded. But you could compile a separate DLL and pass the form instance to one of its functions, where it could then add the control. Commented Jun 18, 2018 at 3:05
  • You can run C# code as a script... Commented Jun 18, 2018 at 3:13
  • This may be an XY Problem. Perhaps you could explain why C# code rather than tuples that provide a control type, parameters and coordinates is necessary in your application. Commented Jun 18, 2018 at 3:18
  • @RezaAghaei I need to run code in the form that the user can modify (via the text file) if they choose to do so. I need it in the context of the current form, because I will have certain variables and functions that I want them to be able to use in such code, and so they can add controls to the form. I am not worried about the security of the program. Commented Jun 18, 2018 at 3:41

2 Answers 2

4

Consider these points to solve the problem:

  • You should create your dynamic code as a class in a dll.
  • Your class should implement a specific interface or have a known method like Run. So you can call the method later when the code compiles.
  • Your class or the known method should accept some parameters to receive the context variables. These context variables can include a Form as parameter. You can also encapsulate context parameters in a class/interface or to keep it simple you can rely on dynamic for passing parameters.

Then to run the dynamic code, first compile it, then pass context parameters to the class or the known method and call the known method.

Example

Here is a quick and dirty example of how you can compile and run a code at run-time and let the code use your context:

public string SomePublicField = "Hello!";
private void button1_Click(object sender, EventArgs e) {
    var csc = new CSharpCodeProvider();
    var parameters = new CompilerParameters(new[] {
        "mscorlib.dll",
        "System.Windows.Forms.dll",
        "System.dll",
        "System.Drawing.dll",
        "System.Core.dll",
        "Microsoft.CSharp.dll"});
    var results = csc.CompileAssemblyFromSource(parameters,
    @"
    using System.Windows.Forms;
    using System.Drawing;
    public class Sample 
    {
        public void DoSomething (dynamic form) 
        {
            var b = new Button();
            b.Text = form.Text;
            b.Click += (s,e)=>{MessageBox.Show(form.SomePublicField);};
            form.Controls.Add(b);
        }
    }");
    //Check if compilation is successful, run the code
    if (!results.Errors.HasErrors) {
        var t = results.CompiledAssembly.GetType("Sample");
        dynamic o = Activator.CreateInstance(t);
        o.DoSomething(this);
    }
    else {
        var errors = string.Join(Environment.NewLine,
            results.Errors.Cast<CompilerError>().Select(x => x.ErrorText));
        MessageBox.Show(errors);
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Just a note that this won't work if the string contains c#6 or later (or is it 5+?). You'd need to add the appropriate nuget packages
@pinkfloydx33 The example has been written in .NET 4.7.1 in a standard Windows Forms project template which is not relying on packages.
What I mean is that the CSharpCodeProvider included in the framework can't compile c#6 code. If the code (within the string passed to CompileAssemblyFromSource) contained a c#6 construct, this would fail without the additional nuget package. It doesn't in this case because there is no such code- I was simply calling it out for the OP or anyone else that stumbled upon this answer (which I upvoted) in the event they modify it or apply it for other scenarios. [this is my understanding, if I'm wrong, I'll gladly be corrected]
Thanks for the feedback @pinkfloydx33 Original poster and future readers should be careful about the way which they are going to compile the code at run-time. This is not the main focus of this question and the sample code in this post is just a quick and dirty but working example of sharing the context with dynamically built assembly.
3

Basically what it seems you want to do, is dynamically compile and add code to your current program. This is not impossible and there are various ways to do it.

The most common uses for this type of functionality are plugins and scripting systems. However there are caveats with the both of these.

One of the of the biggest draw backs is that to run code that you have compiled (however it is you are doing that), you need to literally load it as an assembly into you app domain using the standard, Load methods. Once a library is loaded you actually cant unload it with out unloading the app domain, so this creates problems in certain situations .

If this is a scripting thing you are after, i would seriously consider using a pre-built scripting library (and there are many). Some use nifty-tricks to make this work well and have done a lot of the hard work for you... As an example http://csscript.net/

However be prepared! plugin and scripting systems start off easy, yet they are incredibly fiddly to make stable and workable. I would suggest exploring the domain of your problem first, and make sure you are not trying to reinvent the wheel... There are many options for serialisation and loading object parameters at run-time safely without fuss

Good luck

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.