debugging 如何调试(单步执行)BinaryFormatter. pageualize()?

rn0zuynd  于 2023-10-24  发布在  其他
关注(0)|答案(4)|浏览(127)

我的应用程序试图将客户端发送的数据格式化,但失败并出现以下错误:
引发的异常:mscorlib.dll中的“System. System. Serialization.SerializationException”
附加信息:无法获取成员“<.ctor>b__0”。
谷歌搜索没有结果。好吧,我决定我将进入实体化逻辑,并试图找出到底是什么导致了这一点。嗯,一天过去了,我还没有接近。
我使用Microsoft Reference Source网站上的说明来配置Visual Studio。它确实下载了 * 一些东西 *

MicrosoftPublicSymbols\mscorlib.pdb\
   DCF1E4D31F6944AC87E7A634262BEE881\mscorlib.pdb (780kb)
   E47257B512BA49BC9FC367C532FC5F1E2\mscorlib.pdb (953kb)

但是调试器不介入。
我在谷歌上搜索了更多,找到了另一种方法-安装dotTrace应用程序和used it as source server。这也没有帮助。我仍然看到以下内容:

Symbol Load Information popup for mscorlib.pdb说:
C:\Users\me\AppData\Local\Temp\SymbolCache\MicrosoftPublicSymbols\mscorlib.pdb\e47257b512ba49bc9fc367c532fc5f1e2\mscorlib.PDB:已加载符号。
我可以进入System.Windows.Forms、System.Linq等-所以一般来说,它可以工作-只是这个对BinaryFormatter.Deserialize()的特定调用不起作用。这可能是什么原因?我如何才能让它进入?
难道是因为SecuritySafeCritical属性?

[System.Security.SecuritySafeCritical] 
public Object Deserialize(Stream serializationStream)

我使用的是VS 2015 .Net 4.5.2(尽管我尝试了4.5,结果还是一样)。

nnvyjq4y

nnvyjq4y1#

没有任何细节,我可以假设这是与你试图序列化和重新序列化的对象版本的兼容性问题。看起来像客户端发送给你一些旧的对象位(在构造函数中没有lambda)。而你的服务器运行更新版本的软件,搜索一些lambda方法。
<.ctor>b__0 -.ctor中第一个lambda方法的方法名(对象构造函数)。
例如,如果你在客户端的机器上有对象A:

class A {
  public A() {
   int a = 5;
   int b = 7;
   // Plain code, no lambdas
  }
}

然后你更新了服务器上的类,在构造函数中引入了lambda:

class A {
  public A() {
   int a = 5;
   int b = 7;
   Func<int,int> some = x => x * 2 + a; 
  }
}

之后,它们的二进制表示不一样,A的服务器版本中有私有的不可见方法<.ctor>b__0。

6pp0gazn

6pp0gazn2#

微软不会上传每个mscorlib.dll更新的源代码,这就是为什么你只会得到没有任何源代码数据的公共PDB。但是有一个来自Redgate's Reflector的Visual Studio插件,你可以decompile 3rd party DLLs and step through them in the VS debugger

来自Jetbrains的DotPeek也支持supports PDB generation and hosting of a Symbol server以允许调试。
也许这有助于您调试您的问题。

e3bfsja2

e3bfsja23#

tl;博士:不同版本的编译器(或不同的设置?)可能会为生成的匿名函数对应的方法生成不同的名称。如果这样的方法被序列化类的私有字段指向,即使源代码在两次构建之间没有改变,也会得到异常。
我只是跟踪了确切的情况类型,但是在asp.net应用程序的会话中触发了格式化。
<.ctor>b__0对应于匿名函数所对应的生成方法。
现在,这里的问题是在序列化过程中对这样一个方法的依赖,因为不能保证不同版本的名称相同(即使来源不变)。这几乎肯定会跟踪到序列化类的私有示例字段中的某种委托。请注意,声明匿名函数的此类类不一定是在私人领域。
不幸的是,我没有时间来追踪为什么同一个源代码会为匿名函数产生不同的名称,但是考虑到所涉及的项目的历史,它要么是传递给它的选项的不同编译器版本。
如果你可以访问两边的程序集,你可以确认更改。起初我尝试在DotPeek中导出两个程序集的分解源代码,然后对文件夹进行比较。这并不证明是一个好的过程,但可能是由于需要设置DotPeek的一些设置或其他原因。
更好的方法是使用ndepend和reflector的组合。你可以做一个前面的汇编比较。我做的方法是改变一个内置查询,以获得序列化类的所有构造函数,这些构造函数有任何类型的变化。这将它缩小到几个类/构造函数(如果匿名函数是在不可序列化的类中创建的,那么这种方法有无法捕获它的风险)。
一旦我把它减少到几个构造函数,从ndepend我打开了一个使用反射器的old vs new比较。这不仅显示了与异常相同格式的方法名称,而且已经显示了代码库中的正确方法。
一旦我新建了这个类,我发现最好在一个单独的resharper窗口中打开每个程序集并查看类的方法。
还要注意的是,在代码被更改的情况下,即使是相同的编译器版本/选项也可能给予不同的名称,因此在可序列化的类中拥有指向函数的私有字段是非常脆弱的。

mpbci0fu

mpbci0fu4#

我们花了几个星期的时间来调试同样的问题,在我们的例子中,代码已经改变了,但是我们遍历了每一个提交,没有看到任何明显的应该破坏的东西。
我们发现了一个提交,* 确实 * 打破了事情,感谢@Alexey Korovin的回答,我们能够使用dotPeek(直到上周才知道它的存在)来追踪发生了什么。
这是一个看似奇迹般的重新编号无意连载的阿扎尼亚,看起来无害的东西,

public event EventHandler StatUpdated = delegate{};

但是为什么要重新编号呢?突然间b_6变成了b_0,不同模块中的其他数字也发生了变化。
答案是Visual Studio更改了csproj文件中某些模块的顺序,因为它们是先编译的,他们的匿名成员被点算了。我们重新-我们现在也在确保我们不会意外地序列化匿名委托,他们正在积极地寻找二进制格式化程序的替代品。不幸的是,我们不得不留下来由于向后兼容性,我们意识到了这一点,但至少我们现在已经找到了第一个可能的位置。
当人们升级到较新版本的Visual Studio时,可能会发生类似的情况。

相关问题