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

Set a value to a field in a compiled assembly from an object that exists outside the compiled assembly

提问人:andrew 提问时间:10/17/2023 最后编辑:andrew 更新时间:10/17/2023 访问量:57

问:

我目前正在将旧应用程序从 .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”存在于 SyntaxTree 中,但我仍然没有找到在那里设置值的方法。

vb.net .net-6.0 roslyn roslyn-code-analysis codedom

评论


答:

1赞 andrew 10/17/2023 #1

在花了几天时间试图弄清楚这一点后,我在将问题发布到 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