gpt4 book ai didi

c# - BinaryFormatter - 是否可以在没有程序集的情况下反序列化已知类?

转载 作者:太空宇宙 更新时间:2023-11-03 13:51:24 25 4
gpt4 key购买 nike

我目前正在尝试与首先使用 C# 的 BinaryFormatter 格式化后通过网络发送数据的程序进行互操作。这是一个愚蠢的想法,我讨厌它,但我必须与之互操作。

我知道字体是什么样的,我知道它的确切布局。但由于各种原因,我无法在我的程序中添加对该特定程序集的引用。

考虑到 BinaryFormatter 与特定类型/版本的紧密耦合,尽管知道数据结构,但我似乎无法找到一种方法来反序列化它。

我目前正在考虑创建一个具有所有正确属性的假程序集并尝试将其链接(看起来真的很乱),或者手动尝试遍历二进制流并提取我正在寻找的值(我正在查看关于此的 MS 文档,关于布局一目了然)。

还有其他好点子吗,或者过去有没有人在这方面取得过成功?似乎我知道我需要的所有信息,而 BinaryFormatter 只是出了名的脆弱。

编辑:

要回答下面的问题(顺便说一句,这是一个很好的观点)有几个原因。

  1. 项目清洁度。为一个功能添加一个 5MB 的外部 .exe 引用有点不对。

  2. 与我交互操作的设备在世界各地部署了各种版本。我关心的项目的内部数据结构在所有项目中都是相同的,但程序集的版本不同,导致 BinaryFormatter 损坏。我可以将二进制流转换为字符串,搜索版本号,然后加载正确的版本,但现在我有十几个 .exe 等待我加载正确的版本。然后该方案不是很适合 future (好吧,整个方案并不是真正的 future 证明,但我至少想抽象出 BinaryFormatter 的一些脆弱性以使我的工作更轻松)。仅仅写下这个回复就让我开始考虑使用 emit 或类似的东西来动态创建自定义程序集......但是,伙计,必须有更简单的方法,对吧?我实际上是在寻找中型数据结构中的几个 bool 值。

  3. 对象的变量通过具有某些逻辑的 get/set 属性公开,并尝试进行函数调用并更新我这边可能不存在的其他对象(也就是 get 获取值我需要,但也触发了通过链接的依赖项波动的通知,我可以得到异常冒泡到我的应用程序。谈论代码味道!)。它变成了一个依赖/实现兔子洞。

edit2:制造商正在与我合作以改进他们的系统,但是当我们宣传“适用于 X”时,我们希望它只适用于 X,而不需要特定版本。特别是我们的一些客户系统受到严格的版本控制,仅更新有问题的应用程序就成为一项重大工作。

最佳答案

正如您在对问题的评论中推测的那样,SerializationSurrogate 和 SerializationBinder 可以帮助您实现目标。鉴于您只对少数属性感兴趣,您可以反序列化为代理类,您将通过枚举传递给 SerializationSurrogate 的 SerializationInfo 来填充该代理类。不幸的是,它只会让您完成部分工作。

问题是访问属性不会在序列化对象中触发任何副作用,因为您只是使用 SerializationSurrogate 访问数据——没有任何二进制代码被执行。下面的快速和肮脏的测试代码说明了这个问题:

namespace BinaryProxy
{
using System;
using System.Collections.Generic;
using System.IO;

using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

[Serializable]
class TestClass
{


public bool mvalue;

public TestClass(bool value)
{
BoolValue = value;
}

public bool BoolValue
{
get
{
// won't happen
SideEffect = DateTime.Now.ToString();
return mvalue;
}

set
{
mvalue = value;
}
}

public string SideEffect { get; set; }

}

class ProxyTestClass
{
private Dictionary<string, object> data = new Dictionary<string, object>();

public Object GetData(string name)
{
if(data.ContainsKey(name))
{
return data[name];
}
return null;
}
public void SetData(string name, object value)
{
data[name] = value;
}

public IEnumerable<KeyValuePair<string, object>> Dump()
{
return data;
}
}

class SurrogateTestClassConstructor : ISerializationSurrogate
{
private ProxyTestClass mProxy;
/// <summary>
/// Populates the provided <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the object.
/// </summary>
/// <param name="obj">The object to serialize. </param>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data. </param>
/// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization. </param>
/// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
{
throw new NotImplementedException();
}

/// <summary>
/// Populates the object using the information in the <see cref="T:System.Runtime.Serialization.SerializationInfo"/>.
/// </summary>
/// <returns>
/// The populated deserialized object.
/// </returns>
/// <param name="obj">The object to populate. </param>
/// <param name="info">The information to populate the object. </param>
/// <param name="context">The source from which the object is deserialized. </param>
/// <param name="selector">The surrogate selector where the search for a compatible surrogate begins. </param>
/// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
{
if (mProxy == null) mProxy = new ProxyTestClass();
var en = info.GetEnumerator();
while (en.MoveNext())
{
mProxy.SetData(en.Current.Name, en.Current.Value);


}
return mProxy;

}



}

sealed class DeserializeBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{


return typeof(ProxyTestClass);
}
}

static class Program
{

static void Main()
{
var tc = new TestClass(true);
byte[] serialized;
using (var fs = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(fs, tc);
serialized = fs.ToArray();

var surrSel = new SurrogateSelector();
surrSel.AddSurrogate(typeof(ProxyTestClass),
new StreamingContext(StreamingContextStates.All), new SurrogateTestClassConstructor());

using (var fs2 = new MemoryStream(serialized))
{
var formatter2 = new BinaryFormatter();
formatter2.Binder = new DeserializeBinder();
formatter2.SurrogateSelector = surrSel;
var deser = formatter2.Deserialize(fs2) as ProxyTestClass;
foreach (var c in deser.Dump())
{
Console.WriteLine("{0} = {1}", c.Key, c.Value);
}
}

}

}
}
}

在上面的示例中,TestClassSideEffect 支持字段将保持为空。

如果您不需要副作用而只想访问某些字段的值,则该方法在某种程度上是可行的。否则,除了遵循 Jon Skeet 的建议之外,我想不出任何其他可行的解决方案。

关于c# - BinaryFormatter - 是否可以在没有程序集的情况下反序列化已知类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13594831/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com