JumpyNET Posted July 3, 2012 Posted July 3, 2012 I would like to compile and execute code at runtime and access the main form from the runtime code. 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 Quote
Leaders snarfblam Posted July 4, 2012 Leaders Posted July 4, 2012 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. 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." 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. 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!". Quote [sIGPIC]e[/sIGPIC]
JumpyNET Posted July 4, 2012 Author Posted July 4, 2012 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 frm as WindowsApplication1.Form1 I got back on track. Thanks! Quote
Leaders snarfblam Posted July 5, 2012 Leaders Posted July 5, 2012 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. Quote [sIGPIC]e[/sIGPIC]
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.