Search code examples
vb.net.net-assemblybinary-deserialization

Error Deserializing Assembly for Dynamically Compiled codes


Am attempting to Serialize a dynamically generated/compiled assembly to store into SQL and then subsequently extract the assembly for use. However, I am hitting an error that is driving me nuts.

"Could not load file or assembly '<random code>, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified."

When I try to De-Serialize the Assembly. Will appreciate any pointers. Partial codes:

    '--------------------------------------------------------------
    'This section SUCCESSFULLY Compile & Execute the Dynamic Code
    '  vsResult returns ok.
    '  **Note: This is partial code to create the compiler.
    '--------------------------------------------------------------
    Dim voCompileResults As System.CodeDom.Compiler.CompilerResults = voCompiler.CompileAssemblyFromSource(voCompilerParams, sb.ToString)
    Dim voAssembly As Reflection.Assembly = voCompileResults.CompiledAssembly
    Dim voInstance As Object = Activator.CreateInstance(voAssembly.GetType("libARules.clsMyRoutine"), {New libARules.Sys.clsInterProcessData(New System.Xml.XmlDocument), New libArrowOps.clsDBAccess})
    Dim vsResult As String = voInstance.GetType().InvokeMember("Run", Reflection.BindingFlags.InvokeMethod, Nothing, voInstance, Nothing)

    '----------------------------------------------------
    'Attempt to Serialize the Assembly for store/forward
    '----------------------------------------------------
    Dim voMemStream As New IO.MemoryStream
    Dim voBF As New Runtime.Serialization.Formatters.Binary.BinaryFormatter
    voBF.AssemblyFormat = Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
    voBF.Serialize(voMemStream, voCompileResults.CompiledAssembly)

    '--------------------------------------
    'Reset the MemoryStream position to begining
    '--------------------------------------
    voMemStream.Position = 0

    '--------------------------------------
    'Attempt to De-Serialize the MemoryStream back to an assembly
    '--------------------------------------
    voBF = New Runtime.Serialization.Formatters.Binary.BinaryFormatter
    voBF.AssemblyFormat = Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
    voAssembly = voBF.Deserialize(voMemStream)
    '----- **Error Here** ---------------------------------

ERROR At this point: at the line voAssembly = voBF.Deserialize(voMemStream)

Could not load file or assembly '...<random code>..., Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.


Solution

  • Ok, I finally figured it out after a nap ... Documenting it here for "future generations" :P

    Apparently, I should not be Serializing/De-Serializing the CompiledAssembly.

        '-----------------------------------------------------
        'Instead of Generating in Memory, generate it into a temporary Assembly file (I don't need the CompiledAssembly from the compiler).
        ' Note: Again, this is partial code, with only the important parts here.
        '-----------------------------------------------------
        voCompilerParams.ReferencedAssemblies.Add("mscorlib.dll")
        voCompilerParams.GenerateInMemory = False
        voCompilerParams.OutputAssembly = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "deleteNow_" & Guid.NewGuid().ToString() & ".dll")
    
        Dim voCompileResults As System.CodeDom.Compiler.CompilerResults = voCompiler.CompileAssemblyFromSource(voCompilerParams, sb.ToString)
    
        '--------------------------------------------------------------------------
        'After the Assembly is generated, open the Assembly file and read the entire content into a byte buffer.
        'Note that this byte buffer **CAN BE STORED into an SQL BLOB field.**
        '--------------------------------------------------------------------------
        Dim voInstance As Object = Nothing
        Dim vsResult As String = ""
        Dim voAssembly As Reflection.Assembly = Nothing
        If Not voCompileResults.Errors.HasErrors Then
            '----------------------------------------------------------------
            'No compilation error, open and read the generated assembly file
            '  into a Byte array.
            '----------------------------------------------------------------
            Dim voBuffer() As Byte = Nothing
            Dim voFileStream As IO.FileStream = IO.File.OpenRead(voCompilerParams.OutputAssembly)
            If voFileStream.CanRead Then
                ReDim voBuffer(voFileStream.Length - 1)
                voFileStream.Read(voBuffer, 0, voFileStream.Length)
            End If
            voFileStream.Close()
            IO.File.Delete(voCompilerParams.OutputAssembly) 'Clean-up after ourselves.
    
            '----------------------------------------------------------------
            'Now, re-create the CompiledAssembly from the Byte array.
            '----------------------------------------------------------------
            If Not IsNothing(voBuffer) Then
                voAssembly = Reflection.Assembly.Load(voBuffer)
            End If
            If Not IsNothing(voAssembly) Then
                '--------------------------------------------
                'Instantiate my dynamically compiled class
                '--------------------------------------------
                voInstance = Activator.CreateInstance(voAssembly.GetType("libARules.clsMyRoutine"), {New libARules.Sys.clsInterProcessData(New System.Xml.XmlDocument), New libMyLib.clsMyClass})
                If Not IsNothing(voInstance) Then
                   '------------------------------------------------
                   'Run my dynamic code to proof that it is working
                   '------------------------------------------------
                    vsResult = voInstance.GetType().InvokeMember("Run", Reflection.BindingFlags.InvokeMethod, Nothing, voInstance, Nothing)
                End If
            End If
        End If
    

    And whola... the vsResult is populated. This is a proof of concept that the CompiledAssembly can be stored and reused later. NOTING that the actual temporary assembly file has been deleted before the voAssembly object is recreated from voBuffer().