3

Is it event possible to import text as code, then add it in a sub in vb.net? If I have a .txt file filled with code can I import it programatically (by a button)?

What I need is to make vb.net to accept that script (txt file), and use it to declare variables and make functions/subs - that's all I need to.

user2023328
  • 125
  • 2
  • 11
  • 1
    You might be able to do it using Eval() but I don't think so. – markp3rry Feb 05 '13 at 14:11
  • 1
    It is possible to dynamically compile an assembly using the CodeDom stuff. It's somewhat easy to do, for those who have a decent grasp of .NET, but it should always be done sparingly. Typically there is a better way of doing what you need to do. Why do you think you need to do it? – Steven Doggart Feb 05 '13 at 14:12
  • For an example of what I mean by using CodeDom, take a look at my answer to this question: http://stackoverflow.com/a/10948013/1359668. It's in C#, but it should give you the idea of what I'm talking about. – Steven Doggart Feb 05 '13 at 14:23
  • I would my program to read the text file then add that text as a code into a sub. I don't think this could be that hard. Another idea - make the program "download" the code from the internet, but I still don't know how to add it to a sub – user2023328 Feb 05 '13 at 14:29
  • You can't add it to a sub that's already defined in your current assembly, but you can create the sub with the code in a new dynamically-generated assembly--a class library, and then call *that* from your current assembly. Does that interest you? If so, I could work up an example in VB.NET and post it as an answer. – Steven Doggart Feb 05 '13 at 14:33
  • Everything is good. I only need to use the code to declare variables and execute them at a specified time. No matter if the code is in another sub, but the variables must be global. If you could make me a code like that I'll be extremely gratefull – user2023328 Feb 05 '13 at 14:51
  • 2
    I'm a little confused by what you are trying to do. Could you explain it further? – Steven Doggart Feb 05 '13 at 14:54
  • 2
    As @StevenDoggart pointed out, it would be better if you described the need for such behavior. There are many issues with this approach, and there is most likely a better way to do what you want. But we cannot help you further without more details from you. – Victor Zakharov Feb 05 '13 at 14:56
  • Please don't put all the details in a comment. Please edit your original question to provide the details. It will be much easier for everyone to read that way. – Steven Doggart Feb 05 '13 at 15:00
  • I updated it. It's very hard to explain (because I explained it THREE times) – user2023328 Feb 05 '13 at 15:03
  • We're trying to help. No need to get snippy. – Steven Doggart Feb 05 '13 at 15:28

1 Answers1

3

You can do this kind of thing using the CodeDom objects. The CodeDom objects allow you to dynamically generate assemblies at run-time. For instance, if you make an interface

Public Interface IScript
    Property Variable1 As String
    Sub DoWork()
End Interface

Then, you create a method, like this:

Imports Microsoft.VisualBasic
Imports System.CodeDom.Compiler

' ...

Public Function GenerateScript(code As String) As IScript
    Using provider As New VBCodeProvider()
        Dim parameters As New CompilerParameters()
        parameters.GenerateInMemory = True
        parameters.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location)
        Dim interfaceNamespace As String = GetType(IScript).Namespace
        Dim codeArray() As String = New String() {"Imports " & interfaceNamespace & Environment.NewLine & code}
        Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, codeArray)
        If results.Errors.HasErrors Then
            Throw New Exception("Failed to compile script")
        Else
            Return CType(results.CompiledAssembly.CreateInstance("Script"), IScript)
        End If
    End Using
End Function

Now, you can call it like this:

Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim builder As New StringBuilder()
    builder.AppendLine("Public Class Script")
    builder.AppendLine("    Implements IScript")
    builder.AppendLine("    Public Property Variable1 As String Implements IScript.Variable1")
    builder.AppendLine("    Public Sub DoWork() Implements IScript.DoWork")
    builder.AppendLine("        Variable1 = ""Hello World""")
    builder.AppendLine("    End Sub")
    builder.AppendLine("End Class")
    Dim script As IScript = GenerateScript(builder.ToString())
    script.DoWork()
    MessageBox.Show(script.Variable1) ' Displays "Hello World"
End Sub

Obviously, instead of building the code in a string builder, you could load it out of a text file, like this:

Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim script As IScript = GenerateScript(File.ReadAllText("C:\script.txt")
    script.DoWork()
    MessageBox.Show(script.Variable1) ' Displays "Hello World"
End Sub
Steven Doggart
  • 43,358
  • 8
  • 68
  • 105
  • 1
    when I use script.DoWork() what happens exactly? P.S: Sorry for being "snippy", I was a bit nervous so...hope you don't care about that and reply to my question :) – user2023328 Feb 05 '13 at 15:40
  • 2
    No hard feelings, I know how it goes :) Anyway, when you call `script.DoWork`, it calls the `Script.DoWork` method that is defined in the string builder. In my example, all it does is set the value of the `Variable1` property to `"Hello World"`. But it could do anything you want--that's the beauty of it being a script :) – Steven Doggart Feb 05 '13 at 15:43
  • 1
    So it executes the code right? But how do I declare a variable (or a global one)? It's optional, but it's for my general knowledge – user2023328 Feb 05 '13 at 16:14
  • 1
    Yes, calling the `DoWork` method executes the code in the `DoWork` method, just as if it was defined locally in your own pre-compiled assembly. I just showed that `IScript` interface as an example. You could have as many methods and properties defined in that interface as you need. The `Variable1` property was my attempt at demonstrating how to declare a *variable*, as you mentioned, with a script. – Steven Doggart Feb 05 '13 at 16:20