Improve company productivity with a Business Account.Sign Up

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 355
  • Last Modified:

Dynamically Run Code

Okay, I've struggled with this long enough, and I was hoping that somebody can give me a direction to go in that would provide a better solution.  I am using the following code in VB.NET 2003 to dynamically compile and run some code that is loaded from a text file:

  Public Function Compile(ByVal codeExecute As String, ByVal className As String, _
      ByVal outputName As String, ByRef arrayErrors As ArrayList) As Boolean

    ' Assume success.
    Dim boolValid As Boolean = True

    arrayErrors = New ArrayList

    Dim providerVB As New VBCodeProvider
    Dim compilerVB As ICodeCompiler = providerVB.CreateCompiler

    Dim paramCompiler As New CompilerParameters

    paramCompiler.GenerateInMemory = False

    'note it saves it in a temporary folder "user temp folder"
    'also note you could enhance this by pulling the namespace info
    'and class name from the code instead of having seperate vars for them.
    paramCompiler.OutputAssembly = outputName & ".dll"
    paramCompiler.MainClass = className
    paramCompiler.IncludeDebugInformation = True

    'I believe this is to get all the assemblies of the current
    'application and add them as references to the new assembly
    For Each assemblyVB As [Assembly] In AppDomain.CurrentDomain.GetAssemblies()
      paramCompiler.ReferencedAssemblies.Add(assemblyVB.Location)
    Next

    'here we do the deed
    Dim resultsCompile As CompilerResults = compilerVB.CompileAssemblyFromSource(paramCompiler, _
      codeExecute)

    'this is just to list out all the errors if any
    For Each errorCompile As CompilerError In resultsCompile.Errors
      arrayErrors.Add(errorCompile)
    Next

    If arrayErrors.Count = 0 Then

      'now we create an instance of the assembly newly created
      'and set it to an object notice this is also a good example of late binding

      Dim vArgs() As Object

      Dim nsName As String = Me.GetType.Assembly.GetEntryAssembly().GetName().Name

      Dim fullName = nsName & "." & outputName & "." & className

      Dim objectName As String = nsName & "." & className

      Dim objectRun As Object = resultsCompile.CompiledAssembly.CreateInstance(objectName)

      Dim typeClass As Type = resultsCompile.CompiledAssembly.GetType(objectName)
      Dim methodShowDialog As MethodInfo = typeClass.GetMethod("Test")

      'now we call the sub in the object

      Dim vParams() As Object

      methodShowDialog.Invoke(objectRun, vParams)

      'Dim domainRun As AppDomain = AppDomain.CreateDomain("Run", AppDomain.CurrentDomain.Evidence)
      'domainRun.AppendPrivatePath(Path.GetDirectoryName([Assembly].GetExecutingAssembly.Location.ToString))

      'Dim assemblyRun As [Assembly] = domainRun.Load(domainRun.RelativeSearchPath & "\" & outputName & ".dll")

    Else

      boolValid = False

    End If

    resultsCompile = Nothing
    providerVB = Nothing
    paramCompiler = Nothing
    compilerVB = Nothing

  End Function 'Compile'


It is a small test code that works correctly once:

Imports System
Imports System.Windows.Forms

Namespace AutoBuild.NET
  Public Class TestClass
     Public Sub Test
       MessageBox.Show("test message")
    End Sub
  End Class

End Namespace


The problem comes when I try to make a change to the file and run it again.  I get an error that the assembly cannot be recompiled since it is still in use.  This is because the assembly is still in memory, and cannot be unloaded.  I have tried to overcome this by creating a new AppDomain (commented code towards the bottom), and Loading the compiled assembly in the new AppDomain, and then Unloading the domain when I am through.

When I tried this, I kept getting an error that the DLL or one of its dependencies could not be found (Man, I hate this error!) :(

This may not be the best approach, and I am open to any kind of suggestions, hints, links, etc., that could help me find a solution to this problem.

TIA
Bob "The Learned One"
0
Bob Learned
Asked:
Bob Learned
  • 6
  • 2
1 Solution
 
eternal_21Commented:
Change

  paramCompiler.GenerateInMemory = False

to

  paramCompiler.GenerateInMemory = True

and your problem will be solved!
0
 
Bob LearnedAuthor Commented:
That is the way that I had it before, but I was still getting the error.  

I noticed, though, that when it was True, it was still creating a file in the assembly bin folder.  The GenerateInMemory = False was an attempt to get it to load into the AppDomain.

Bob
0
 
eternal_21Commented:
> I noticed, though, that when it was True, it was still creating a file in the assembly bin folder.

The DLL will get created, but after being loaded, there will be no lock on the file.  Can you post some more code?  I was able to repeatly generate a DLL file and execute the Test method with out another AppDomain, and without any errors...
0
Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

 
Bob LearnedAuthor Commented:
Let me check this again, I'll get back to you.

Thanks,
Bob
0
 
Bob LearnedAuthor Commented:
Okay, I changed paramCompiler.GenerateInMemory = True, and now I don't get any error.  It must have been one of the 10,000 things that I was experimenting with that was causing my problem.  Now, I think I am back to the original problem that I lost track of, which is if I run the code, I get a message box, but if I change the text of the message box, and then try to recompile it, I get the old message box text.

This is the calling code:

  Private Sub RunScript()

    Dim richtextObject As RichTextBox
    Dim stringFileName As String

    Try

      richtextObject = Me.ActiveScriptControl
      stringFileName = Me.ActiveScriptFileName

      Dim errorMessage As String

      Dim compilerRunTime As New SourceCodeCompiler

      Dim boolValid As Boolean = compilerRunTime.Compile(richtextObject.Text, "TestClass", _
        "Test", errorMessage)

      If Not boolValid Then
        MsgBox(errorMessage)
      End If

      '      m_scriptEngine.SystemFont = Me.Font

      '     m_scriptEngine.RunScript(stringFileName, Me.ActiveScriptControl.Lines)

      buttonRun.Pushed = False

    Catch ex As Exception

      MsgBox(ex.ToString)

    End Try

  End Sub 'RunScript'


Thanks,
Bob
0
 
Bob LearnedAuthor Commented:
This is the reference that I used to start thinking about creating a new  AppDomain:

http://www.dotnet247.com/247reference/msgs/33/166928.aspx

Bob
0
 
Bob LearnedAuthor Commented:
Here is another that talks about AppDomains:

http://www.west-wind.com/presentations/dynamicCode/DynamicCode.htm

Bob
0
 
Bob LearnedAuthor Commented:
I think what I will do is to split the compiler code, and put it into another application, and make the calling program write a temporary file, and then pass the name of the file to the compiler application, which will get unloaded once it is done.

Thanks for your help.
Bob
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 6
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now