在ILMerge之后,BinaryFormatter.Deserialize"无法找到程序集"

Mik*_*ark 12 c# ilmerge binary-serialization

我有一个带有引用的dll的C#解决方案(也是具有相同.Net版本的C#).当我构建解决方案并运行生成的exe,而不合并exe和引用的DLL时,一切正常.

现在我想将这些合并到一个exe中.我运行ILMerge,一切似乎都正常.我尝试执行exe,它似乎运行正常,直到它尝试反序列化引用的DLL中定义的对象.

using (Stream fstream = new FileStream(file_path, FileMode.Open))
{
    BinaryFormatter bf = new BinaryFormatter();
    return bf.Deserialize(fstream) as ControlledRuleCollection; 
    // throws unable to find assembly exception
}
Run Code Online (Sandbox Code Playgroud)

可能有一些ILMerge选项我在这里丢失了吗?

小智 38

您可以通过创建和添加SerializationBinder子类来完成此操作,该子类将在反序列化发生之前更改程序集名称.

sealed class PreMergeToMergedDeserializationBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        Type typeToDeserialize = null;

        // For each assemblyName/typeName that you want to deserialize to
        // a different type, set typeToDeserialize to the desired type.
        String exeAssembly = Assembly.GetExecutingAssembly().FullName;


        // The following line of code returns the type.
        typeToDeserialize = Type.GetType(String.Format("{0}, {1}",
            typeName, exeAssembly));

        return typeToDeserialize;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在反序列化时将其添加到BinaryFormatter:

BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new PreMergeToMergedDeserializationBinder();
object obj = bf.Deserialize(ms);
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案!这为我节省了很多时间!谢谢! (2认同)

Jar*_*Par 7

听起来你已经在DLL中序列化了一个对象,然后将所有程序集与ILMerge合并,现在正在尝试反序列化该对象.这根本行不通.二进制序列化的反序列化过程将尝试从原始DLL加载对象的类型.ILMerge后不存在此DLL,因此反序列化将失败.

序列化和反序列化过程需要在合并前或合并后进行操作.它不能混合


Sup*_*nus 5

SerializationBinder也是我的解决方案。但是我在被引用的DLL中有该类。因此,我必须在所有负载组件中进行搜索。如果活页夹应该在dll中搜索,我已经修改了带有参数的答案。

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace ibKastl.Helper
{
   public static class BinaryFormatterHelper
   {
      public static T Read<T>(string filename, Assembly currentAssembly)
      {
         T retunValue;
         FileStream fileStream = new FileStream(filename, FileMode.Open);

         try
         {
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            binaryFormatter.Binder = new SearchAssembliesBinder(currentAssembly,true);            
            retunValue = (T)binaryFormatter.Deserialize(fileStream);
         }
         finally
         {
            fileStream.Close();
         }

         return retunValue;
      }

      public static void Write<T>(T obj, string filename)
      {
         FileStream fileStream = new FileStream(filename, FileMode.Create);
         BinaryFormatter formatter = new BinaryFormatter();
         try
         {
            formatter.Serialize(fileStream, obj);
         }
         finally
         {
            fileStream.Close();
         }
      }
   }

   sealed class SearchAssembliesBinder : SerializationBinder
   {
      private readonly bool _searchInDlls;
      private readonly Assembly _currentAssembly;

      public SearchAssembliesBinder(Assembly currentAssembly, bool searchInDlls)
      {
         _currentAssembly = currentAssembly;
         _searchInDlls = searchInDlls;
      }

      public override Type BindToType(string assemblyName, string typeName)
      {
         List<AssemblyName> assemblyNames = new List<AssemblyName>();
         assemblyNames.Add(_currentAssembly.GetName()); // EXE

         if (_searchInDlls)
         {
            assemblyNames.AddRange(_currentAssembly.GetReferencedAssemblies()); // DLLs
         }

         foreach (AssemblyName an in assemblyNames)
         {
            var typeToDeserialize = GetTypeToDeserialize(typeName, an);
            if (typeToDeserialize != null)
            {
               return typeToDeserialize; // found
            }
         }

         return null; // not found
      }

      private static Type GetTypeToDeserialize(string typeName, AssemblyName an)
      {
         string fullTypeName = string.Format("{0}, {1}", typeName, an.FullName);
         var typeToDeserialize = Type.GetType(fullTypeName);
         return typeToDeserialize;
      }
   }

}
Run Code Online (Sandbox Code Playgroud)

用法:

const string FILENAME = @"MyObject.dat";

// Serialize
BinaryFormatterHelper.Write(myObject1,FILENAME);

// Deserialize
MyObject myObject2 = BinaryFormatterHelper.Read<MyObject>(FILENAME, Assembly.GetExecutingAssembly()); // Current Assembly where the dll is referenced
Run Code Online (Sandbox Code Playgroud)