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

发布于 2024-10-20 12:20:49 字数 474 浏览 2 评论 0 原文

我有一个带有引用的 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 选项吗?

I have a C# solution with a referenced dll (also C# with the same .Net version). When I build the solution and run the resulting exe, without merging the exe and the referenced dll, everything works fine.

Now I want to merge these into one exe. I run ILMerge and everything appears to work ok. I try to execute the exe and it seems to run just fine until it tries to deserialize an object defined in the referenced 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
}

Is there maybe some ILMerge option I'm missing here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

栖竹 2024-10-27 12:20:49

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

You can do this by creating and adding a SerializationBinder sub class that will change the assembly name before the deserialization happens.

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

Then when deserializating add this to the BinaryFormatter:

BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new PreMergeToMergedDeserializationBinder();
object obj = bf.Deserialize(ms);
温柔女人霸气范 2024-10-27 12:20:49

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

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

It sounds like you've serialized an object inside a DLL, then merged all of the assemblies with ILMerge and are now trying to deserialize that object. This simply won't work. The deserialization process for binary serialization will attempt to load the object's type from the original DLL. This DLL doesn't exist post ILMerge and hence the deserialization will fail.

The serialization and deserialization process need to both operate pre or post merge. It can't be mixed

肥爪爪 2024-10-27 12:20:49

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

}

用法:

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

SerializationBinder was also my solution. But I have the class in a DLL which is referenced. So i have to search in all load assemblies. I have modified the answers bevor with the parameter if the binder should search in dlls.

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

}

Usage:

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
[旋木] 2024-10-27 12:20:49

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

此处进行一些讨论

You may have serialized that from the separate assembly and then tried to deserialize it with another assembly (or a newer version of the same assembly).

Some discussion here

绻影浮沉 2024-10-27 12:20:49

我找到了这个问题的另一个解决方案。我的问题可能有点不同......

我试图从序列化的同一个库进行反序列化,但它无法找到正确的程序集来执行此操作。我尝试了上面所有的解决方案,但没有一个有效。

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

总之,重写+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;
}

这修复了我的错误,允许我反序列化。

I found another solution for this problem. Mine problem was maybe a little different...

I was trying to deserialize from the same library that serialized, but it could not locate the correct assembly to do so. I tried all the solutions above, and none worked.

I found a solution on another website (Here)

In short, override the ResolveEventHandler of AppDomain.CurrentDomain.AssemblyResolve in the +tor

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

Then implement the method for "CurrentDomain_AssemblyResolve" as follows:

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

This fixed the error for me which allowed me to deserialize.

以歌曲疗慰 2024-10-27 12:20:49

对于任何遇到此问题并尝试从不同程序集反序列化的人,我发现这个解决方案对我来说似乎非常有用,它使用一个小型“BindChanger”类,该类具有所讨论对象类型的共享命名空间。
https://www.daniweb。 com/programming/software-development/threads/339638/deserializing-in-a- different- assembly

For anyone having this issue, trying to deserialize from a different assembly, I found this solution which seems to great for me using a small "BindChanger" class with a shared namespace for the Object type in question.
https://www.daniweb.com/programming/software-development/threads/339638/deserializing-in-a-different-assembly

别闹i 2024-10-27 12:20:49

如果您将程序集合并到现有程序集中(例如,将所有 DLL 合并到 EXE),您可以使用 这个答案

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

这至少适用于反序列化预合并。 IL-Merge 也仍然通过;即使您无法使用类型前向编译到同一程序集的类型...

我还没有尝试过,序列化是否在合并后有效。但我会不断更新我的答案。

In case you merge assemblies into a existing one (for-example all DLLs to the EXE) you can use the solution proposed in this answer:

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

This at least works for deserializing pre-merge. IL-Merge also still passes; even if you can't compile with a type-forward to a type of the same assembly...

I have not tried, if serializing works post-merge yet. But I'll keep my answer updated.

世界等同你 2024-10-27 12:20:49

我遇到过这样的情况:序列化数据由已经存在多年的旧 .NET 服务存储在 SQL Server 中。我需要从 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;
        }
    }

I had a situation where serialized data was being stored in SQL server by an old .NET service that had been in place for years. I needed to get the data out of SQL and ran into this also. I was able to refer to the .exe and make it work until I used the solution mentioned above. However my assembly names were different.

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;
        }
    }
謌踐踏愛綪 2024-10-27 12:20:49

我得到了解决方案

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

}

反序列化问题:反序列化时出错不同的程序版本

i got the solution

   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

清秋悲枫 2024-10-27 12:20:49
    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;
    }
}
    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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文