从已编译程序集外部存在的对象为已编译程序集中的字段设置值

问题描述 投票:0回答:1

我目前正在将旧应用程序从 .Net Framework 4.5.2 升级到 .Net 6。该应用程序当前通过 CodeDom 编译代码,然后执行。在 .Net 6 中,不再支持通过 CodeDom 进行编译,因此我一直在转换代码以改为使用 Microsoft.CodeAnalysis (Roslyn)。我已经成功地在 .Net 6 中编译了代码并消除了所有错误和警告。

我不知道如何从编译代码外部存在的对象为编译代码中的字段设置值。

在CodeDom中,您可以使用System.Reflection.FieldInfo和compiledCode.GetType("Script").GetField("app")来获取字段。然后使用.SetValue 设置值。到目前为止我还没有找到 Roslyn 的等效命令。

这里是一个有效的 CodeDom 代码示例。 “app”是要设置值的字段。

'CodeDom example
'Add referenced assemblies
Dim compileParams As System.CodeDom.Compiler.CompilerParameters = New CompilerParameters
compileParams.ReferencedAssemblies.Add("system.dll")
compileParams.ReferencedAssemblies.Add("system.xml.dll")
compileParams.ReferencedAssemblies.Add("system.data.dll")
compileParams.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll")
compileParams.ReferencedAssemblies.Add("Microsoft.VisualBasic.Compatibility.dll")
compileParams.ReferencedAssemblies.Add("mscorlib.dll")
compileParams.ReferencedAssemblies.Add(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly.Location) & "\CustomDLL.dll")
compileParams.CompilerOptions = "/t:library"
compileParams.GenerateInMemory = True
compileParams.GenerateExecutable = False

'Script example. Formatted for readability.
Dim sb As StringBuilder = New StringBuilder
sb.Append(
"Option Strict Off
 Option Explicit On
 Imports System
 Imports System.XML
 Imports System.Data
 Imports Microsoft.VisualBasic
 Imports Microsoft.VisualBasic.Compatibility
 Imports CustomDLL

 Module Script 
      Public app as Object

      Sub ProcessName()
           Call app.Execute(app.statements.executeStatement(1))
           Call app.Execute(app.statements.executeStatement(2))
           ...
      End Sub
 End Module")

'Compile
Dim codeProvider As VBCodeProvider = New VBCodeProvider
Dim compileResults As CompilerResults = codeProvider.CompileAssemblyFromSource(compileParams, sb.ToString)

'Set value for 'app' in compiled program
'objApp is a global object that exists outside the compiled program and its class is defined by the 'CustomDLL'
Dim field As System.Reflection.FieldInfo
field = compileResults.CompiledAssembly.GetType("Script").GetField("app")
field.SetValue(Nothing, CType(objApp, Object))

'Execute
Dim assemb As System.Reflection.Assembly = compileResults.CompiledAssembly
Dim method As System.Reflection.MethodInfo = assemb.GetType("Script").GetMethod(processName)
Dim returnObj As Object = method.Invoke(Nothing, Nothing)

这里是.Net 6转换后的代码示例。需要设置'app'的值。

'Roslyn example
'Add reference paths
Dim refPaths = {GetType(System.Object).GetTypeInfo().Assembly.Location,
                GetType(Console).GetTypeInfo().Assembly.Location,
                Path.Combine(Path.GetDirectoryName(GetType(System.Runtime.GCSettings).GetTypeInfo().Assembly.Location), "System.Runtime.dll"),
                Path.Combine(Path.GetDirectoryName(GetType(System.Runtime.GCSettings).GetTypeInfo().Assembly.Location), "System.Data.dll"),
                Path.Combine(Path.GetDirectoryName(GetType(System.Runtime.GCSettings).GetTypeInfo().Assembly.Location), "System.XML.dll"),
                System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly.Location) & "\CustomDLL.dll"}
Dim references As As Microsoft.CodeAnalysis.MetadataReference() = refPaths.[Select](Function(r) MetadataReference.CreateFromFile(r)).ToArray()

'Script example. Formatted for readability.
Dim sb As StringBuilder = New StringBuilder
sb.Append(
"Option Strict Off
 Option Explicit On
 Imports System
 Imports System.XML
 Imports System.Data
 Imports Microsoft.VisualBasic
 Imports Microsoft.VisualBasic.Compatibility
 Imports CustomDLL

 Module Script 
      Public app as Object

      Sub ProcessName()
           Call app.Execute(app.statements.executeStatement(1))
           Call app.Execute(app.statements.executeStatement(2))
           ...
      End Sub
 End Module")
 Dim syntax As Microsoft.CodeAnalysis.SyntaxTree = VisualBasicSyntaxTree.ParseText(sb.ToString())

'Compile options
Dim assemblyName As String = Path.GetRandomFileName()
Dim compileOpts As New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
                                                             embedVbCoreRuntime:=True)

'Compile
Dim objVBCompilation As Microsoft.CodeAnalyis.VisualBasic.VisualBasicCompilation = VisualBasicCompilation.Create(assemblyName,
                                                syntaxTrees:={syntax},
                                                references:=references,
                                                options:=compileOpts)

'How do I set a value to 'app' like in the CodeDom example?

当我搜索符号时,我可以看到“app”字段存在。

Dim sybmols As IEnumerable(Of ISymbol) = objVBCompilation.GetSymbolsWithName("app")
If symbols IsNot Nothing Then
    For Each symbol As ISymbol In symbols
        
    Next
End If

但我无法将符号或 sybmol 的子部分转换为 FieldInfo。我还没有找到设置符号值的方法。

我还可以看到“app”存在于语法树中,但我再次没有找到在那里设置值的方法。

vb.net .net-6.0 roslyn roslyn-code-analysis codedom
1个回答
0
投票

花了几天时间试图解决这个问题,在将问题发布到 StackOverflow 后我成功了......

我需要获取编译后的代码,将其加载到 System.Reflection.Assembly 中,然后找到该字段并设置它。我还能够跟踪 MethodInfo 来调用正确的流程。

Using ms As New MemoryStream
    Dim objEmitResult As EmitResult = objVBCompilation.Emit(ms)
    If objEmitResult.Success = True Then
        ms.Seek(0, SeekOrigin.Begin)

        'Set field 'app'
        objAssembly = AssemblyLoadContext.Default.LoadFromStream(ms)
        Dim type As Type = objAssembly.GetType("Script")
        Dim field As FieldInfo = type.GetField("app")
        field.SetValue(Nothing, CType(objApp, Object))
    End If
End Using
© www.soinside.com 2019 - 2024. All rights reserved.