【问题标题】:TargetInvocationException on BinaryFormatter.Deserialize after i added strong naming to my assembly向程序集添加强命名后 BinaryFormatter.Deserialize 上的 TargetInvocationException
【发布时间】:2016-01-19 16:46:12
【问题描述】:

我编写了一个带有Save()Load() 方法的DLL,使用BinaryFormatter.Serialize()BinaryFormatter.Deserialize()List<MyObject> 保存或加载到我计算机上的文件中。

在我决定为我的程序集添加强命名之前,这一直很好。一旦我在编译过程中添加了一个具有强名称的密钥,我的程序就不再能够加载预强命名文件。我得到一个 TargetInvocationException 和一个 FileLoadException 作为 InnerExceptionMyAssembly, Version=1.0.1.0, Culture=neutral, PublicKeyToken=null 没有找到。

当我使用 HEX 编辑器打开文件时,我可以在文件中看到对我的程序集的多个引用:MyAssembly, Version=1.0.1.0, Culture=neutral, PublicKeyToken=null

当我使用新的强命名程序集保存文件时,会发生一件事:PublicKeyToken=123456789。当然,123456789 是另一个值。

现在我想到了几个问题:

  1. 为什么保存文件的 Assembly 版本很重要?
  2. 如果我想编写另一个程序来访问这些文件中的数据……这可能吗?
  3. 我可以转换 pre-strong-naming-files 以便我可以使用强命名程序集读取它们吗?

我已经尝试将 PublicKeyToken=null 更改为 PublicKeyToken=123456789 但这只会引发 SerializationException 表示没有有效的 BinaryHeader 或对象版本已更改。

【问题讨论】:

    标签: c# .net serialization strongname


    【解决方案1】:

    强命名程序集是不同的程序集(因为具有不同公钥的程序集实际上是不同的程序集)。

    考虑到这一点,你不能加载它们而不是预期的更合理,因为BinaryFormatter 默认情况下会尝试加载完全需要的程序集。

    您可以做的是创建您的 SerializationBinder,在其中将 null PublicKey 替换为当前正在执行的程序集中的 PublicKey。您的自定义序列化绑定器可用于分配 BinaryFormatter.Binder 属性。

    var formatter = new BinaryFormatter { Binder = new MyCustomBinder() };
    

    MyCustomBinder 在哪里(只是相关部分):

    sealed class MyCustomBinder : SerializationBinder
    {
       public override Type BindToType(string assemblyName, string typeName)
       {
           var name = new AssemblyName(assemblyName);
           if (name.GetPublicKeyToken() == null) // Better check here...
           {
               var publicKeyToken = Assembly.GetExecutingAssembly()
                   .GetName().GetPublicKeyToken();
    
               name.SetPublicKeyToken(publicKeyToken);
           }
    
           // Now let's create required type using name and typeName
       }
    
       // Other code
    }
    

    当然,您应该只为您自己的程序集(而不是您不知道的任何其他类型)执行此操作,并且您应该仔细检查这不会破坏您的应用程序安全性。

    编辑:请注意,某些类型使用序列化代理(例如 MemberInfoMemberInfoSerializationHolder)。由于此代理项而加载的类型不会通过您的自定义序列化绑定程序。值得注意的案例是代表。是的,序列化委托并不是一个好主意,我倾向于避免它(另见.NET 4.5 MethodInfo serialization breaking change),但如果你不得不忍受它,你还需要处理AppDomain.CurrentDomain.AssemblyResolve事件(与上面的逻辑相同):在反序列化之前附加一个处理程序,并在完成后将其删除;理想情况下,您可以在序列化活页夹中完成所有操作:

    sealed class MyCustomBinder : SerializationBinder, IDisposable
    {
        public MyCustomBinder()
        {
            AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
        }
    
        void IDisposable.Dispose()
        {
            AppDomain.CurrentDomain.AssemblyResolve -= OnAssemblyResolve;
        }
    
        // Your code here
    }
    

    像这样使用:

    using (var binder = new MyCustomBinder())
    {
        var formatter = new BinaryFormatter { Binder = new MyCustomBinder() };
    }
    

    注意:我没有尝试,但如果您需要使用 AppDomain.AssemblyResolve,那么您也许甚至不需要自定义活页夹,一切都可以在那里完成...

    【讨论】:

    • 可与Type.GetType() 一起使用的完整类型名称(带程序集)。例如:return Type.GetType(String.Format("{0}, {1}", typeName, name.ToString()));
    • 啊。现在我懂了。谢谢你。不幸的是,它不起作用。第一次调用BindToType assemblyNamemscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089typeNameSystem.Collections.Generic.List`1[[MyClass, MyNamespace, Version=2.0.2.0, Culture=neutral, PublicKeyToken=null]] -> 没有任何反应。我仍然得到错误。
    • 最后一段:_“当然你应该只为你自己的程序集这样做......”。该代码仅对您自己的程序集有意义!
    • 既然我序列化了List<MyObject> 而不仅仅是MyObject,这种方法对我不起作用,对吧?
    • 是的,已经放弃了。下一步是使用 BinaryFormatter ;)
    猜你喜欢
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多