0

I am creating a game where you have to code to move a block around the screen in visual studio community 2015. The goal of this project is to create a game to teach people how to code. I have created a method to move the block (just up so far).

 private void move(string dir)
    {
        if(dir=="up"&& pbxBot.Location.Y != 12)
        {
            this.pbxBot.Top = this.pbxBot.Top - 30;

        }
    }

I have a textbox(textBox1) and a button (btnGo), I want to set it so when the user types in textBox1 an clicks the go button the code is executed.

For example the user types in "move(up);" in textBox1 and clicks btnGo, the block will move 1 space up(the block is 30x30 pixels hence 30 pixels=1 space)

  • @NattyMan0007 - No, it's not a duplicate of that question. This is a question about parsing a string. Calling the method can be done a variety of ways. – Enigmativity May 19 '17 at 11:48
  • I am trying to get it so ALL the code in text box is executed once the button is pressed, later on this will include loops and conditional statements etc. This text box contains multiple lines. – Robin Sanders May 19 '17 at 12:04
  • @RobinSanders - Then you're trying to compile code on the fly and execute it. It can be done, but it's not simple. – Enigmativity May 19 '17 at 13:38

3 Answers3

0

If you're checking the parameter value in your move function, why not just check for the command itself?

Take the value in the text box and split it by the "(" symbol, then take the first part of your new list, in this case "move" and perform logic according to that value:

 switch (textBoxValue.split("(")[0])
  {
     case "Move":
          move();
          break;

and so on.

0

(This post has been changed so that the text to be compiled is displayed in an editable text box) all that has to be done is create a new Windows form project, and overwrite the project files by using copy and paste from this article into the project files indicated by name)

First things first: First thing you need to do is load the Roslyn compiler package.

In Visual Studio, Tools/NuGet Package Manager/Package Manager Console

at the PM> prompt, enter Install-Package Microsoft.CodeAnalysis

When you create your project, target .NET Framework 4.6.1 in project/properties/application/Target framework

This code has been tested with Visual Studio 2015 and .NET Framework 4.6.1

Program.cs

using System;
using System.Windows.Forms;

namespace ExecuteText {
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    } 
 }

Form1.cs

using System;
using System.Drawing;
using System.Windows.Forms;

using System.Linq;
using System.IO;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using System.Reflection;

namespace ExecuteText
{

    /**************************************************************************************************
    * A form 1.
    *
    * @sa System.Windows.Forms.Form
    **************************************************************************************************/

    public partial class Form1 : Form
    {

        /**************************************************************************************************
        * Default constructor.
        **************************************************************************************************/

        public Form1()
        {
            InitializeComponent();
        }

        /**************************************************************************************************
        * Gets the assembly files in this collection.
        *
        * @param assembly The assembly.
        *
        * @return An enumerator that allows foreach to be used to process the assembly files in this
        * collection.
        **************************************************************************************************/

        IEnumerable<string> GetAssemblyFiles(Assembly assembly)
        {
            var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (var assemblyName in assembly.GetReferencedAssemblies())
                yield return loadedAssemblies.SingleOrDefault(a => a.FullName == assemblyName.FullName)?.Location;
        }

        /**************************************************************************************************
        * Creates compiler context.
        *
        * @return The new compiler context.
        **************************************************************************************************/

        private CSharpCompilation CreateCompilerContext()
        {
            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(TextBox.Text);
            var assemblyName = Path.GetRandomFileName();
            Assembly a = typeof(Form1).Assembly;
            var refs = GetMetadataReferencesInAssembly(a);

            var compilation = CSharpCompilation.Create(
                                                       assemblyName,
                                                       new[] {syntaxTree},
                                                       refs.ToArray(),
                                                       new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
            return compilation;
        }

        /**************************************************************************************************
        * Console write errors.
        *
        * @param result The result.
        **************************************************************************************************/

        private void ConsoleWriteErrors(EmitResult result)
        {
            var failures = result.Diagnostics.Where(CodeHasError);
            var err = "";
            foreach (var diagnostic in failures)
               err += $"{diagnostic.Id}: {diagnostic.GetMessage()}";

            ErrorTextBox.AppendText(err);
        }

        /**************************************************************************************************
        * Executes the compiled code operation.
        *
        * @param ms The milliseconds.
        **************************************************************************************************/

        private  void ExecuteCompiledCode(MemoryStream ms)
        {
            ms.Seek(0, SeekOrigin.Begin);
            var assembly = Assembly.Load(ms.ToArray());

            var type = assembly.GetType("AStringOfCode.FunctionClass");
            var obj = Activator.CreateInstance(type);
            type.InvokeMember("Run",
                              BindingFlags.Default | BindingFlags.InvokeMethod,
                              null,
                              obj,
                              new object[] {Block});
            ShowPosition();
        }

        /**************************************************************************************************
        * Gets metadata references in assembly.
        *
        * @param assembly The assembly.
        *
        * @return The metadata references in assembly.
        **************************************************************************************************/

        private List<MetadataReference> GetMetadataReferencesInAssembly(Assembly assembly)
        {


            var assemblyFiles = GetAssemblyFiles(assembly);

            Console.WriteLine(assemblyFiles);

            List<MetadataReference> refs = new List<MetadataReference>();
            foreach (var assemblyname in assemblyFiles)
                refs.Add(MetadataReference.CreateFromFile(assemblyname));

            return refs;
        }



        /**************************************************************************************************
        * Code has error.
        *
        * @param diagnostic The diagnostic.
        *
        * @return True if it succeeds, false if it fails.
        **************************************************************************************************/

        private bool CodeHasError(Diagnostic diagnostic)
        {
            return diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error;
        }

        private void ShowPosition()
        {
            PositionTextBox.Text = Block.Location.X + "," + Block.Location.Y;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
           ShowPosition(); 
        }


        private void Run_Click(object sender, EventArgs e)
        {

            var compilation = CreateCompilerContext();
            using (var ms = new MemoryStream())
            {
                var result = compilation.Emit(ms);

                if (result.Success)
                {
                    ExecuteCompiledCode(ms);
                }
                else
                {
                    ConsoleWriteErrors(result);
                }
            }
        }

        private void TextBox_TextChanged(object sender, EventArgs e)
        {

        }
    }

}

Form1.Designer.cs

namespace ExecuteText
{
    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
            this.TextBox = new System.Windows.Forms.RichTextBox();
            this.Block = new System.Windows.Forms.PictureBox();
            this.Run = new System.Windows.Forms.Button();
            this.ErrorTextBox = new System.Windows.Forms.RichTextBox();
            this.PositionTextBox = new System.Windows.Forms.RichTextBox();
            ((System.ComponentModel.ISupportInitialize)(this.Block)).BeginInit();
            this.SuspendLayout();
            // 
            // TextBox
            // 
            this.TextBox.Location = new System.Drawing.Point(12, 43);
            this.TextBox.Name = "TextBox";
            this.TextBox.Size = new System.Drawing.Size(400, 605);
            this.TextBox.TabIndex = 0;
            this.TextBox.Text = resources.GetString("TextBox.Text");
            this.TextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
            // 
            // Block
            // 
            this.Block.BackColor = System.Drawing.Color.DodgerBlue;
            this.Block.Location = new System.Drawing.Point(591, 314);
            this.Block.Name = "Block";
            this.Block.Size = new System.Drawing.Size(52, 50);
            this.Block.TabIndex = 1;
            this.Block.TabStop = false;
            // 
            // Run
            // 
            this.Run.Location = new System.Drawing.Point(12, 12);
            this.Run.Name = "Run";
            this.Run.Size = new System.Drawing.Size(75, 23);
            this.Run.TabIndex = 2;
            this.Run.Text = "Run";
            this.Run.UseVisualStyleBackColor = true;
            this.Run.Click += new System.EventHandler(this.Run_Click);
            // 
            // ErrorTextBox
            // 
            this.ErrorTextBox.Location = new System.Drawing.Point(12, 665);
            this.ErrorTextBox.Name = "ErrorTextBox";
            this.ErrorTextBox.Size = new System.Drawing.Size(747, 96);
            this.ErrorTextBox.TabIndex = 3;
            this.ErrorTextBox.Text = "";
            // 
            // PositionTextBox
            // 
            this.PositionTextBox.Location = new System.Drawing.Point(418, 43);
            this.PositionTextBox.Name = "PositionTextBox";
            this.PositionTextBox.Size = new System.Drawing.Size(341, 44);
            this.PositionTextBox.TabIndex = 4;
            this.PositionTextBox.Text = "";
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(771, 773);
            this.Controls.Add(this.PositionTextBox);
            this.Controls.Add(this.ErrorTextBox);
            this.Controls.Add(this.Run);
            this.Controls.Add(this.Block);
            this.Controls.Add(this.TextBox);
            this.Name = "Form1";
            this.Text = "Form1";
            this.Load += new System.EventHandler(this.Form1_Load);
            ((System.ComponentModel.ISupportInitialize)(this.Block)).EndInit();
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.RichTextBox TextBox;
        private System.Windows.Forms.Button Run;
        public System.Windows.Forms.PictureBox Block;
        private System.Windows.Forms.RichTextBox ErrorTextBox;
        private System.Windows.Forms.RichTextBox PositionTextBox;
    }
}

Form1.resx

<?xml version="1.0" encoding="utf-8"?>
<root>
  <!-- 
    Microsoft ResX Schema 

    Version 2.0

    The primary goals of this format is to allow a simple XML format 
    that is mostly human readable. The generation and parsing of the 
    various data types are done through the TypeConverter classes 
    associated with the data types.

    Example:

    ... ado.net/XML headers & schema ...
    <resheader name="resmimetype">text/microsoft-resx</resheader>
    <resheader name="version">2.0</resheader>
    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>

    There are any number of "resheader" rows that contain simple 
    name/value pairs.

    Each data row contains a name, and value. The row also contains a 
    type or mimetype. Type corresponds to a .NET class that support 
    text/value conversion through the TypeConverter architecture. 
    Classes that don't support this are serialized and stored with the 
    mimetype set.

    The mimetype is used for serialized objects, and tells the 
    ResXResourceReader how to depersist the object. This is currently not 
    extensible. For a given mimetype the value must be set accordingly:

    Note - application/x-microsoft.net.object.binary.base64 is the format 
    that the ResXResourceWriter will generate, however the reader can 
    read any of the formats listed below.

    mimetype: application/x-microsoft.net.object.binary.base64
    value   : The object must be serialized with 
            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.

    mimetype: application/x-microsoft.net.object.soap.base64
    value   : The object must be serialized with 
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.

    mimetype: application/x-microsoft.net.object.bytearray.base64
    value   : The object must be serialized into a byte array 
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" use="required" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="TextBox.Text" xml:space="preserve">
    <value>using System;
using System.Drawing;
using System.Windows.Forms;

namespace AStringOfCode
{

 public   class FunctionClass
    {
        enum Directions
        {
            e_Up,
            e_Down,
            e_Left,
            e_Right
        };

        private void Move(PictureBox p, Directions direction)
        {
            var fromPoint = p.Location;
            var toPoint = p.Location;
            switch (direction)
            {
                case Directions.e_Up:
                    toPoint = new Point(fromPoint.X, fromPoint.Y - 1);
                    break;
                case Directions.e_Down:
                    toPoint = new Point(fromPoint.X, fromPoint.Y + 1);
                    break;
                case Directions.e_Left:
                    toPoint = new Point(fromPoint.X - 1, fromPoint.Y);
                    break;
                case Directions.e_Right:
                    toPoint = new Point(fromPoint.X + 1, fromPoint.Y);
                    break;
            }

            p.Location = toPoint;
        }

        public void Run(PictureBox p)
        {
            Move(p, Directions.e_Up);
        }
    }

}</value>
  </data>
</root>

This should be what you're looking for.click here for screen image

    When you run the application, you will see three windows and a button 
     1. the code window
     2. the position window
     3. the error window

     and the "Run" Button.
     ...oh yes, let's not forget the "Block" we're moving!

you can modify the call to "move" by changing the second parameter to one of the 4 defined directions. When the "run" button is clicked, the code in the code window will be compiled and run (if there are no errors), the blue block will move one pixel in the direction indicated by the parameter to Move(), and the new position will be displayed in the Position window.

If there are errors in the compilation, they will be displayed in the Error window.

Hope this helps

Max Euwe
  • 431
  • 3
  • 9
  • so what code do I put in the buttons event handler to run the code in the textbox? – Robin Sanders May 20 '17 at 11:56
  • what I essentialy want to do is CSharpSyntaxTree.ParseText(textBox1.Text) instead of the sample string you provided but it is an invalid parameter. – Robin Sanders May 20 '17 at 18:05
  • so what code do I put in the buttons event handler to run the code in the textbox? – Robin Sanders yesterday The button handler is Demo: private void Demo(object sender, EventArgs e) – Max Euwe May 22 '17 at 02:32
  • "instead of the sample string" the code string is turned into a syntax tree with -> CSharpSyntaxTree.ParseText() and used in creation of the compiler context... here-> assemblyName, new[] {syntaxTree}, refs.ToArray(), new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); return compilation; – Max Euwe May 22 '17 at 02:39
  • What I want to know is where exactly I type in "texBox1.Text" so it runs the code from the textbox in the UI after you click the button. – Robin Sanders May 22 '17 at 11:02
  • Robin Sanders yesterday - I don't usually do this, but I'll go ahead and write your program for you. You did say you wanted to teach coding - perhaps this example will help someone who needs a similar solution. Lord knows I've benefited by the help of others, but perhaps you should reconsider being a teacher, and spend more time learning how to program - being that the only thing missing is the text box itself. :) That might have sounded snarky... if so, I didn't mean anything by it. – Max Euwe May 23 '17 at 16:44
  • Cheers for the help, I'm not planning to become a teacher. The reason for this project is for my final year dissertation at university, I want to use my summer break to start coding on it since I am a noob and have so much to learn. tbh before I started thought it would be something as simple as convert.tocode(textbox1.text) – Robin Sanders May 24 '17 at 10:54
  • If this solves the problem you were asking, please select it as the answer. Compiling a program as text within a program is not easy, as another person mentioned. I learned how to do it in order to write a run-time script editor with error-checking. – Max Euwe May 24 '17 at 22:52
-1

Start by Extracting the method name from the string using string.Split or regex.

Then you can use reflection to invoke the method, something like this:

Type thisType = this.GetType();
MethodInfo theMethod = thisType.GetMethod("MethodName");
theMethod.Invoke(this, parameters, ...);

This requires a using System.Reflection; Reference.

You can get the parameters just by using regex or string.Split to get what's inside the brackets. Chuck it in a try catch statement to handle when the function does not exist.

NattyMan0007
  • 103
  • 13