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

时间:2011-03-02 16:21:32

标签: 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
}

我可能缺少一些ILMerge选项吗?

10 个答案:

答案 0 :(得分:36)

您可以通过创建和添加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;
    }
}

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

BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new PreMergeToMergedDeserializationBinder();
object obj = bf.Deserialize(ms);

答案 1 :(得分:7)

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

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

答案 2 :(得分:5)

SerializationBinder也是我的解决方案。但我在引用的DLL中有类。所以我必须搜索所有加载程序集。如果活页夹应在dll中搜索,我已使用参数修改了答案bevor。

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;
      }
   }

}

用法:

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

答案 3 :(得分:3)

您可能已从单独的程序集序列化了该程序集,然后尝试使用其他程序集(或同一程序集的较新版本)对其进行反序列化。

一些讨论here

答案 4 :(得分:2)

对于遇到此问题的任何人,尝试从其他程序集反序列化,我发现此解决方案对我来说非常有用,它使用一个小的“ BindChanger”类和一个有问题的Object类型的共享名称空间。 https://www.daniweb.com/programming/software-development/threads/339638/deserializing-in-a-different-assembly

答案 5 :(得分:2)

我找到了解决该问题的另一种方法。我的问题也许有点不同...

我试图从序列化的同一库中反序列化,但是找不到正确的程序集来进行序列化。我尝试了上面所有的解决方案,但没有一个起作用。

我在另一个网站(Here)上找到了解决方案

简而言之,请在+ tor中重写AppDomain.CurrentDomain.AssemblyResolve的ResolveEventHandler

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

然后实现“ CurrentDomain_AssemblyResolve”的方法,如下所示:

System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
  System.Reflection.Assembly ayResult = null;
  string sShortAssemblyName = args.Name.Split(',')[0];
  System.Reflection.Assembly[] ayAssemblies = AppDomain.CurrentDomain.GetAssemblies();
  foreach (System.Reflection.Assembly ayAssembly in ayAssemblies)
  {
      if (sShortAssemblyName == ayAssembly.FullName.Split(',')[0])
      {
          ayResult = ayAssembly;
          break;
      }
  }
  return ayResult;
}

这为我解决了允许我反序列化的错误。

答案 6 :(得分:0)

如果您将程序集合并到现有程序集(例如,所有DLL到EXE),您可以使用this answer中提出的解决方案:

// AssemblyInfo.cs for My.exe
[assembly: TypeForwardedTo(typeof(SomeTypeFromMergedDLL))]

这至少适用于反合并反序列化。 IL-Merge还会通过;即使您无法使用类型转发编译到同一程序集的类型...

我还没试过,如果序列化工作合并后还没有。但我会更新我的答案。

答案 7 :(得分:0)

我遇到过这样一种情况:序列化数据由一个已经存在多年的旧.NET服务存储在SQL服务器中。我需要从SQL中获取数据并遇到这种情况。我能够引用.exe并使其工作,直到我使用上面提到的解决方案。但是我的集会名称不同。

sealed class Version1ToVersion2DeserializationBinder : 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 assemVer1 = assemblyName;
            String typeVer1 = typeName;

            if (assemblyName == assemVer1 && typeName == typeVer1)
            {
                // To use a type from a different assembly version, change the version number.
                assemblyName = Assembly.GetExecutingAssembly().FullName;
                // To use a different type from the same assembly, change the type name.
                typeName = "projectname.typename";
            }

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

答案 8 :(得分:0)

我得到了解决方案

   sealed class VersionDeserializationBinder : SerializationBinder
  {
     public override Type BindToType(string assemblyName, string typeName)
    {
    Type typeToDeserialize = null;
    string currentAssemblyInfo = Assembly.GetExecutingAssembly().FullName;

    //my modification
    string currentAssemblyName = currentAssemblyInfo.Split(',')[0];
    if (assemblyName.StartsWith(currentAssemblyName))assemblyName = currentAssemblyInfo;

    typeToDeserialize = Type.GetType(string.Format("{0}, {1}", typeName, assemblyName));
    return typeToDeserialize;
}

}

Deserialization problem: Error when deserializing from a different program version

答案 9 :(得分:0)

    public sealed class DeserializationBinder : SerializationBinder
{
    private readonly string _typeName;
    private readonly Assembly _assembly;
    public DeserializationBinder(Assembly assembly, string typeName)
    {
        _typeName = typeName;
        _assembly = assembly;
    }

    public override Type BindToType(string assemblyName, string typeName)
    {
        Type typeToDeserialize = null;
        if (!assemblyName.Contains("System") && !assemblyName.Contains("mscorlib"))
        {
            String currentAssembly = _assembly.FullName;
            assemblyName = currentAssembly;
            typeName = _typeName;
        }
        typeToDeserialize = Type.GetType(String.Format("{0}, {1}",
            typeName, assemblyName));
        return typeToDeserialize;
    }
}