0

I run user defined scripts in my WPF application using CS-Script library. How can I cancel a script if it runs endless? As my users write the script I can't rely on a cancel flag that is checked inside the script.

Here is a simplified code snippet showing the problem:

public partial class MainWindow : Window
{
    public string MessageFromScript
    {
        get { return (string)GetValue(MessageFromScriptProperty); }
        set { SetValue(MessageFromScriptProperty, value); }
    }
    public static readonly DependencyProperty MessageFromScriptProperty =
        DependencyProperty.Register("MessageFromScript", typeof(string), typeof(MainWindow), new PropertyMetadata(null));


    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }

    BackgroundWorker worker = null;
    private void OnStart(object sender, RoutedEventArgs e)
    {
        if(worker != null)
        {
            return;
        }
        worker = new BackgroundWorker();
        worker.DoWork += RunScript;
        worker.RunWorkerCompleted += ScriptCompleted;
        worker.WorkerSupportsCancellation = true;
        worker.RunWorkerAsync();
    }

    private void ScriptCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if(e.Cancelled)
            MessageFromScript = "Script cancelled";
        else
            MessageFromScript = e.Result.ToString();
    }

    private void RunScript(object sender, DoWorkEventArgs e)
    {
        dynamic script = CSScript.Evaluator.LoadCode(@"using System;
                                using System.Threading;
                                 public class Script
                                 {
                                     public string Test()
                                     {
                                        {int count=0; while(true) { count++; Console.WriteLine(count.ToString()); Thread.Sleep(200); }}

                                        return ""Message from script"";
                                     }
                                 }");

        e.Result = script.Test();
    }

    private void OnStop(object sender, RoutedEventArgs e)
    {
        if(worker == null)
        {
            return;
        }

        //TODO: How do I stop the script here?

        worker = null;
    }
}
MTR
  • 1,690
  • 3
  • 20
  • 47

1 Answers1

1

In your Test() method add a parameter where you pass a CancellationToken to the script. Then design the loops in your script to check the canellation token if abort has been requested and break out. To stop the script just call the Cancel() method of your CancellationTokenSource which token you passed to the script on invocation.

henon
  • 2,022
  • 21
  • 23
  • Doesn't help, if my user dosn't check this in his script. – MTR Apr 05 '17 at 06:02
  • 1
    Then don't allow while, foreach, etc in the script (i.e. by regex checking and rejecting) and provide safe abortable versions of these loop constructs that take a delegate and behave like the loop they replace. Another idea but less easy to implement would be to modify the generated IL by inserting the IL-equivalent of if(token.IsCancelled) return; or similar at the beginning of every loop. if you compile the script in DEBUG mode the beginning of a loop can be easily spotted in a debug-assembly as it starts with two consecutive no-op instructions. – henon Apr 06 '17 at 07:17
  • nice idea replacing the loops. I think I'll go this way. – MTR Apr 06 '17 at 13:27