Rezard
Rezard

Reputation: 126

end-user defined code - how to use existing classes and objects from the ExecutingAssembly

I have a winform similar to what the OP has in How to load an internal class in end-user compiled code ? (advanced) where I have a textbox that will be the source of end-user code, and I it will be compiled on the fly when I hit the button.

Here are some parts of the code (where most was copied from How to load an internal class in end-user compiled code ? (advanced) :

Imports System
Imports System.CodeDom.Compiler
Imports System.Reflection
Imports System.Text

Public Class Form1

Private Sub ExecuteB_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ExecuteB.Click
    Dim Code As String = Me.CodeToExec.Text

    Dim CompilerResult As CompilerResults
    CompilerResult = Compile(Code)
End Sub

Public Function Compile(ByVal Code As String) As CompilerResults
    Dim CodeProvider As New VBCodeProvider
    Dim CodeCompiler As System.CodeDom.Compiler.CodeDomProvider = CodeDomProvider.CreateProvider("VisualBasic")

    Dim Parameters As New System.CodeDom.Compiler.CompilerParameters
    Parameters.GenerateExecutable = False
    Parameters.ReferencedAssemblies.Add("System.dll")
    Parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll")
    Parameters.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll")

    Dim CompilerResult As CompilerResults = CodeCompiler.CompileAssemblyFromSource(Parameters, Code)

    If CompilerResult.Errors.HasErrors Then
        CompileTB.Clear()
        For i = 0 To CompilerResult.Errors.Count - 1
            CompileTB.AppendText(CompilerResult.Errors(i).ErrorText & vbCrLf)
        Next
        Return Nothing
    Else
        Return CompilerResult
    End If
End Function

End Class

My controls are: CodeToExec is the textbox where the user puts the custom code, CompileTB is another textbox where the errors are listed, and ExecuteB is the button. There are other classes and object in my program, and here is one of the classes that I want to use:

Public Class ExceptionCriteria

Private _CriteriaName As String
Public Property CriteriaName As String
    Get
        CriteriaName = _CriteriaName
    End Get
    Set(value As String)
        _CriteriaName = value
    End Set
End Property

Private _RuleSet As List(Of String)
Public Property RuleSet As List(Of String)
    Get
        RuleSet = _RuleSet
    End Get
    Set(value As List(Of String))
        _RuleSet = value
    End Set
End Property

Private _SerializationFilePath As String
Public Property SerializationFilePath As String
    Get
        SerializationFilePath = _SerializationFilePath
    End Get
    Set(value As String)
        _SerializationFilePath = value
    End Set
End Property

Public Sub AddRule(newRule As String)
    If IsNothing(_RuleSet) Then
        _RuleSet = New List(Of String)
    End If
    _RuleSet.Add(newRule)
End Sub

End Class

And here is one of the members that I want to use/set in the Form1 class:

Public comparisondate As Date = #2/20/2015#

My problem is, I want to access/modify objects, members, properties in the executing assembly and create new objects based on the custom classes there. How can I do this?

Upvotes: 1

Views: 131

Answers (2)

Tzwenni
Tzwenni

Reputation: 139

If you want to use existing classes, you should know how to (self-)reference and how to deliver parameters. Here's a little example:

  1. Do not forget to Imports YourProjectName in the generated code.

  2. Example Class to do all necessary stuff

    Imports System.CodeDom.Compiler
    Imports System.Reflection
    
    Public Class MyHelloCompiler
        Private Shared                  _ass As Assembly
        Private Shared            _YourClass As Type
        Private Shared           _YourMethod As MethodInfo
        Private Shared    _YourClassInstance As Object
    
        Public Sub New()
            'Code to compile and instantiate the compiled class for later use:
            Dim opt As New CompilerParameters _
                                   (Nothing, String.Empty, False)
            opt.GenerateExecutable = False
            opt.GenerateInMemory = True
            ' Following is the important self reference
            opt.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location)
    
            Console.WriteLine (MyCodeToCompile)
    
            Dim res As CompilerResults = New VBCodeProvider().CompileAssemblyFromSource (opt, MyCodeToCompile)
            _ass =  res.CompiledAssembly
            _YourClass = _ass.GetType ("MyExampleClass")
            _YourMethod = _YourClass.GetMethod ("MyExampleProperty")
            _YourClassInstance =  Activator.CreateInstance ( _YourClass)
        End Sub
    
        Public Class MyExampleClass()
            Public Property MyExampleProperty as String
        End Class
    
        Public Function RunCompiledCodeWithParams( _
              instanceOfExampleClass as MyExampleClass) as String
            Dim params As Object() = New Object() {instanceOfExampleClass}
            Return CStr(_YourMethod.Invoke(_YourClassInstance, params))
        End Function
    
        Private Function MyCodeToCompile() as String
            Return _
            "Imports System" & vbCrLf & _
            "Imports YourProjectName" & vbCrLf & _
            "Public Class ClassToCompile" & vbCrLf & _
            "    Public Shared Function GetExampleProperty(prmExampleClassInstance as MyExampleClass) as String" & vbcrlf & _
            "        Return prmExampleClassInstance.MyExampleProperty" & vbcrlf & _
            "    End Function" & vbcrlf & _
            "End Class"
        End Function
    End Class
    
  3. Calling this above:

    '....
    Dim MyTestCompile As MyHelloCompiler = New MyHelloCompiler
    Dim MyDummyClass  As MyExampleClass  = New MyExampleClass
    MyDummyClass.MyExampleProperty   = "I am the property value"
    
    Dim MyResult as String = _
        MyTestCompile.RunCompiledCodeWithParams(MyDummyClass)
    '....
    

Upvotes: 0

Rezard
Rezard

Reputation: 126

I have bumped into the answer, what I lacked was

Parameters.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location)

Then I just needed to make the procedures and members shared. I hope this also helps newbies like me who happen to have the same problem and pass by this page.

Upvotes: 1

Related Questions