G
Guest
I may have painted myself into a corner with GenerateInMemory=true.
My app need a custom user step. Users want to code (sort of - they are not
programmers) some refinements to a search procedure. They can concoct a
moderately complex boolean expression, and that suffices. I will take their
statements, add top and bottom text, and now I can do a CodeDom compile.
I generate an assembly via CodeDom with GenerateInMemory. I make a
delegate, and I can call my function, and all of this works fine. It runs at
good speed (no boxing of arguments), and that is what I need since I will be
looping over an array of data to be searched.
The problem is that I have a memory leak because of not unloading the
assembly. So, what I need is a new appdomain, and at this point I am stuck.
I don't know how to call CompileAssemblyFromSource() with
GenerateInMemory=True and put the generated assembly in a new appdomain. I
have no dll to load into the new appdomain.
Does anyone have any bright ideas? Working VS2005 VB source code (with the
leak) is included below. Every call to TestDynamicCode() adds two handles as
reported by the task manager. The call to CompileAssemblyFromSource() is
what produces the leak. What I think I want to do is make an appdomain, tell
codedom to put my compiler output assembly in the appdomain, do my
calculations, and then unload the appdomain. I don't see how to do it.
-------------------------
Public Module DynamicCode
Public Function SourceCode() As String
' user provides the commented lines, my code provides the rest
' the user lines coded herein are just for testing
Dim a() As String = { _
"Imports System.Math", _
"Public Class DynamicClass", _
" Public Shared Function DynamicFunction( _", _
" Byval s1 As String, _", _
" Byval s2 As String, _", _
" Byval i1 As Integer, _", _
" Byval i2 As Integer, _", _
" Byval d1 As Double, _", _
" Byval d2 As Double, _", _
" Byval d3 As Double, _", _
" Byval d4 As Double) _", _
" As Boolean", _
" Dim Qualified, b1, b2, b3, b4, b5, b6, b7, b8, b9 As Boolean", _
" Dim d As Double = Sqrt(d1)", _
" b1 = (d1>d2) ' user provides this and perhaps many others", _
" Qualified = b1 ' user provides this, the final result", _
" Return Qualified", _
" End Function", _
"End Class"}
Return Join(a, vbLf)
End Function
Public Delegate Function DynamicDelegate( _
ByVal s1 As String, _
ByVal s2 As String, _
ByVal i1 As Integer, _
ByVal i2 As Integer, _
ByVal d1 As Double, _
ByVal d2 As Double, _
ByVal d3 As Double, _
ByVal d4 As Double) _
As Boolean ' this delegate must match the signature in SourceCode() above
Public Sub TestDynamicCode() ' success is no exceptions and no assert
failures
' make params, the compiler parameters
Dim params As New CodeDom.Compiler.CompilerParameters
params.GenerateInMemory = True ' Assembly is created in memory
params.TreatWarningsAsErrors = False
params.WarningLevel = 4
params.CompilerOptions = "/optionexplicit /optionstrict /nowarn
/optimize-"
'Dim refs() As String = {"System.dll", "Microsoft.VisualBasic.dll"}
'params.ReferencedAssemblies.AddRange(refs)
' make results by compiling the source code
' CompileAssemblyFromSource leaks 2 handles and about 1k memory
' i think it is because of not unloading the assembly
' however, you can't unload an assembly, you can only unload an appdomain
' everything works ok except for this leak
Dim provider As New Microsoft.VisualBasic.VBCodeProvider
Dim results As CodeDom.Compiler.CompilerResults =
provider.CompileAssemblyFromSource(params, SourceCode)
provider.Dispose()
' report compilation errors
Dim errors As String = ""
For Each err As CodeDom.Compiler.CompilerError In results.Errors
Const f As String = "Line {0}, Col {1}: Error {2} - {3}"
errors &= [String].Format(f, err.Line, err.Column, err.ErrorNumber,
err.ErrorText) & vbLf
Next err
Debug.Assert(errors = "", errors)
' make DynamicFunctionDelegate so we can execute strongly typed and
without boxing
Dim DynamicClass As Type =
results.CompiledAssembly.GetType("DynamicClass")
Dim flags As Reflection.BindingFlags = Reflection.BindingFlags.Public Or
Reflection.BindingFlags.Static
Dim DynamicFunction As Reflection.MethodInfo =
DynamicClass.GetMethod("DynamicFunction", flags)
Dim DynamicFunctionDelegate As DynamicDelegate =
CType([Delegate].CreateDelegate(GetType(DynamicDelegate), Nothing,
DynamicFunction), DynamicDelegate)
Debug.Assert(Not DynamicFunctionDelegate Is Nothing)
' prove that it works, raise the loop limit to measure execution speed
For i As Integer = 1 To 100
Dim a As Double = Rnd(1)
Dim b As Double = Rnd(1)
Dim c As Double = 0
Dim bResult As Boolean = DynamicFunctionDelegate("", "", 1, 2, a, b,
c, c)
Debug.Assert(bResult = (a > b))
Next i
End Sub
End Module
My app need a custom user step. Users want to code (sort of - they are not
programmers) some refinements to a search procedure. They can concoct a
moderately complex boolean expression, and that suffices. I will take their
statements, add top and bottom text, and now I can do a CodeDom compile.
I generate an assembly via CodeDom with GenerateInMemory. I make a
delegate, and I can call my function, and all of this works fine. It runs at
good speed (no boxing of arguments), and that is what I need since I will be
looping over an array of data to be searched.
The problem is that I have a memory leak because of not unloading the
assembly. So, what I need is a new appdomain, and at this point I am stuck.
I don't know how to call CompileAssemblyFromSource() with
GenerateInMemory=True and put the generated assembly in a new appdomain. I
have no dll to load into the new appdomain.
Does anyone have any bright ideas? Working VS2005 VB source code (with the
leak) is included below. Every call to TestDynamicCode() adds two handles as
reported by the task manager. The call to CompileAssemblyFromSource() is
what produces the leak. What I think I want to do is make an appdomain, tell
codedom to put my compiler output assembly in the appdomain, do my
calculations, and then unload the appdomain. I don't see how to do it.
-------------------------
Public Module DynamicCode
Public Function SourceCode() As String
' user provides the commented lines, my code provides the rest
' the user lines coded herein are just for testing
Dim a() As String = { _
"Imports System.Math", _
"Public Class DynamicClass", _
" Public Shared Function DynamicFunction( _", _
" Byval s1 As String, _", _
" Byval s2 As String, _", _
" Byval i1 As Integer, _", _
" Byval i2 As Integer, _", _
" Byval d1 As Double, _", _
" Byval d2 As Double, _", _
" Byval d3 As Double, _", _
" Byval d4 As Double) _", _
" As Boolean", _
" Dim Qualified, b1, b2, b3, b4, b5, b6, b7, b8, b9 As Boolean", _
" Dim d As Double = Sqrt(d1)", _
" b1 = (d1>d2) ' user provides this and perhaps many others", _
" Qualified = b1 ' user provides this, the final result", _
" Return Qualified", _
" End Function", _
"End Class"}
Return Join(a, vbLf)
End Function
Public Delegate Function DynamicDelegate( _
ByVal s1 As String, _
ByVal s2 As String, _
ByVal i1 As Integer, _
ByVal i2 As Integer, _
ByVal d1 As Double, _
ByVal d2 As Double, _
ByVal d3 As Double, _
ByVal d4 As Double) _
As Boolean ' this delegate must match the signature in SourceCode() above
Public Sub TestDynamicCode() ' success is no exceptions and no assert
failures
' make params, the compiler parameters
Dim params As New CodeDom.Compiler.CompilerParameters
params.GenerateInMemory = True ' Assembly is created in memory
params.TreatWarningsAsErrors = False
params.WarningLevel = 4
params.CompilerOptions = "/optionexplicit /optionstrict /nowarn
/optimize-"
'Dim refs() As String = {"System.dll", "Microsoft.VisualBasic.dll"}
'params.ReferencedAssemblies.AddRange(refs)
' make results by compiling the source code
' CompileAssemblyFromSource leaks 2 handles and about 1k memory
' i think it is because of not unloading the assembly
' however, you can't unload an assembly, you can only unload an appdomain
' everything works ok except for this leak
Dim provider As New Microsoft.VisualBasic.VBCodeProvider
Dim results As CodeDom.Compiler.CompilerResults =
provider.CompileAssemblyFromSource(params, SourceCode)
provider.Dispose()
' report compilation errors
Dim errors As String = ""
For Each err As CodeDom.Compiler.CompilerError In results.Errors
Const f As String = "Line {0}, Col {1}: Error {2} - {3}"
errors &= [String].Format(f, err.Line, err.Column, err.ErrorNumber,
err.ErrorText) & vbLf
Next err
Debug.Assert(errors = "", errors)
' make DynamicFunctionDelegate so we can execute strongly typed and
without boxing
Dim DynamicClass As Type =
results.CompiledAssembly.GetType("DynamicClass")
Dim flags As Reflection.BindingFlags = Reflection.BindingFlags.Public Or
Reflection.BindingFlags.Static
Dim DynamicFunction As Reflection.MethodInfo =
DynamicClass.GetMethod("DynamicFunction", flags)
Dim DynamicFunctionDelegate As DynamicDelegate =
CType([Delegate].CreateDelegate(GetType(DynamicDelegate), Nothing,
DynamicFunction), DynamicDelegate)
Debug.Assert(Not DynamicFunctionDelegate Is Nothing)
' prove that it works, raise the loop limit to measure execution speed
For i As Integer = 1 To 100
Dim a As Double = Rnd(1)
Dim b As Double = Rnd(1)
Dim c As Double = 0
Dim bResult As Boolean = DynamicFunctionDelegate("", "", 1, 2, a, b,
c, c)
Debug.Assert(bResult = (a > b))
Next i
End Sub
End Module