Compile and execute code at RunTime

JumpyNET

Centurion
Joined
Apr 4, 2005
Messages
196
I would like to compile and execute code at runtime and access the main form from the runtime code.

Visual Basic:
Public Class Form1
    Public TestVariable As Integer = 7
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ExecuteTxtCode("Form1.TestVariable = 2") 'How should I alter this????
        Me.Text = TestVariable 'Should be 2 and not the initial value of 7.
    End Sub
    Private Sub ExecuteTxtCode(ByVal Code As String)
        'What should I put here????
    End Sub
End Class
 
Are you having more trouble compiling code at runtime, or having that code access your form?

A search for "vb compile at runtime" gave this result. The code in the article contains errors, but it shows you everything you need to know to compile code at runtime.

Note this important piece of code.
Code:
For Each ASM In AppDomain.CurrentDomain.GetAssemblies()
    PM.ReferencedAssemblies.Add(ASM.Location)
Next
This adds a reference to all loaded assemblies, including your assembly containing Form1.

Now that we have a reference to the assembly that defines Form1, all we need in order to get the code to interact with a Form1 is a reference to an instance of Form1. There are a few ways to do this. The code could just look through Application.OpenForms for an instance. Form1 could store an instance of itself in a public shared property that the code could read. The method I chose was to have the code being compiled to accept a Form1 in the constructor.

Here is the code from the article, updated to work. You'll need a mulitline textbox named "TextBox1" and a button named "Button1."
Code:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim errors As New StringBuilder

        Try
            If TextBox1.Text.Trim <> "" Then
                Dim VBP As New VBCodeProvider
                Dim CVB As System.CodeDom.Compiler.ICodeCompiler
                CVB = VBP.CreateCompiler
                Dim PM As New System.CodeDom.Compiler.CompilerParameters
                PM.GenerateInMemory = True
                '  PM.GenerateExecutable = True
                PM.OutputAssembly = "Generated.dll"
                PM.MainClass = "ClassMain"
                PM.IncludeDebugInformation = True

                Dim ASM As System.Reflection.Assembly
                For Each ASM In AppDomain.CurrentDomain.GetAssemblies()
                    PM.ReferencedAssemblies.Add(ASM.Location)
                Next

                'Get compilation results
                Dim Results As System.CodeDom.Compiler.CompilerResults
                Results = CVB.CompileAssemblyFromSource(PM, TextBox1.Text)

                'Show possible compilation errors
                Dim Err As System.CodeDom.Compiler.CompilerError
                For Each Err In Results.Errors
                    errors.AppendLine("Error N. " & Err.ErrorNumber & _
                    " Message: " & Err.ErrorText & " Line " & Err.Line)
                Next

                'Use the compiled assembly
                Dim RunObj As New Object
                [COLOR="Red"]Dim vArgs As Object() = {Me}[/COLOR]

                RunObj = Results.CompiledAssembly.[COLOR="Orange"]CreateInstance[/COLOR]( _
                  "[COLOR="Blue"]Generated.ClassMain[/COLOR]", True, _
                  Reflection.BindingFlags.CreateInstance, Nothing, _
                  [COLOR="Red"]vArgs[/COLOR], Nothing, Nothing)

                If Not RunObj Is Nothing Then
                    Dim method As MethodInfo = RunObj.GetType().GetMethod("[COLOR="Magenta"]DoIt[/COLOR]")
                    method.Invoke(RunObj, _
                        BindingFlags.Static Or BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.NonPublic, _
                        Nothing, Nothing, Nothing)
                Else
                    MessageBox.Show("Compile Error:" & vbCrLf & vbCrLf & errors.ToString())
                End If

            Else
                MessageBox.Show("Write VB.Net code to compile")
            End If
        Catch err As Exception
            MessageBox.Show(err.Message & " " & err.StackTrace)
        End Try
    End Sub
As you can see, the code looks for a class named "ClassMain" in the namespace "Generated", which has a method, "DoIt", and a constructor that accepts a Form1.

Here is the code that I used that meets those criteria.
Code:
Imports System.Windows.Forms
Imports Microsoft.VisualBasic
 
namespace [COLOR="Blue"]Generated[/COLOR]
   public class [COLOR="Blue"]ClassMain[/COLOR]
      public sub [COLOR="Orange"]New[/COLOR]([COLOR="Red"]frm as WindowsApplication1.Form1[/COLOR])
          frm.Text = "EXPERIMENT"
      end sub

       public shared sub [COLOR="Magenta"]DoIt[/COLOR]()
           messagebox.show("It works!")
       end sub
 
   end class
end namespace
If you enter this code and click the button, the form's caption will change to "EXPERIMENT" and a message box will display "It works!".
 
Thank you snarfblam!

Actually I had already googled the site you suggested. I wanted to leave more choice for the one answering my question as I just wasn't sure they did everything in the best possible way. But if you vouch for the code sample I gues it's a proper way to do it.

My main problem was figuring out how to have my code access back to Form1. I'm sorry for not achieving to explain this in my original question. Anyway after seeing this piece of code
Visual Basic:
frm as WindowsApplication1.Form1
I got back on track.

Thanks!
 
I'm just going to add one thing. If you're writing a program with some kind of "interpreter" or using some other scenario where the user can frequently enter and run code, understand that each time you compile code, you're compiling and loading a whole new assembly, which can't be unloaded until the application exits. Things could pile up in memory pretty quickly.
 
Back
Top