How to debug (step into) BinaryFormatter.Deserialize()?

时间:2015-10-29 15:57:47

标签: .net visual-studio debugging debug-symbols

My app tries to deserialize data sent by client and it fails with the following error:

Exception thrown: 'System.Runtime.Serialization.SerializationException' in mscorlib.dll

Additional information: Cannot get the member '<.ctor>b__0'.

googling gives no results. Okay, I decided I would step into deserialization logic and try to figure out what exactly is causing this. Well, a day has passed and I'm nowhere close.

I used instructions from Microsoft Reference Source website to configure Visual Studio. It does download something

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

but debugger does not step in.

I googled more and found another way to do it - installed dotTrace app and used it as source server. And that does not help either. I still see the following:

enter image description here

Symbol Load Information popup for mscorlib.pdb says

C:\Users\me\AppData\Local\Temp\SymbolCache\MicrosoftPublicSymbols\mscorlib.pdb\e47257b512ba49bc9fc367c532fc5f1e2\mscorlib.pdb: Symbols loaded.

I can step in into System.Windows.Forms, System.Linq, etc - so generally speaking, it works - it just this particular call to BinaryFormatter.Deserialize() does not work. What could be the reasons for that and how can I get it to step into?

Could it be because of SecuritySafeCritical attribute?

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

I'm using VS 2015 .Net 4.5.2 (though I tried 4.5 with the same results).

3 个答案:

答案 0 :(得分:8)

没有任何细节,我可以假设这是您尝试序列化的对象版本的兼容性问题。反序列化。看起来客户端会向您发送一些旧的对象位(在构造函数中没有lambda)。并且您的服务器运行较新版本的软件,搜索一些lambda方法。

&lt; .ctor&gt; 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的服务器版本中有私有隐形方法&lt; .ctor&gt; b__0。

Generated IL for lamda method in class A1

答案 1 :(得分:2)

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

enter image description here

enter image description here

来自Jetbrains的DotPeek也supports PDB generation and hosting of a Symbol server允许调试。

也许这可以帮助您调试问题。

答案 2 :(得分:1)

tl; dr:不同版本的编译器(或不同的设置?)可能会为与匿名函数对应的生成方法生成不同的名称。如果序列化类的私有字段指向这样的方法,则会得到异常,即使源代码在2个版本之间没有变化。

我只是跟踪确切的情况类型,但是在asp.net应用程序的会话中触发了反序列化。像这种情况一样,使用了BinaryFormatter。

&lt; .ctor&gt; b__0对应于与匿名函数对应的生成方法。

现在,这里的问题是在序列化期间依赖于这样的方法,因为不能保证名称在不同的构建中是相同的(即使使用未更改的源)。这几乎肯定会跟踪序列化类的私有实例字段中的某种委托。请注意,声明匿名函数的类不一定是在私有字段中保存对该函数的引用的类。

不幸的是,我没有时间跟踪为什么相同的源为匿名函数生成不同的名称,但考虑到涉及项目的历史,它是传递给它的选项的不同编译器版本。我确信它是早些时候的。

如果您可以访问双方的装配,则可以确认更改。起初我尝试在DotPeek中导出两个程序集的分解源,然后对文件夹进行差异处理。这并不是一个好的过程,但可能是由于需要设置一些DotPeek设置或其他原因。

更好的方法是使用ndepend和反射器的组合。您可以对之前的程序集进行比较。我这样做的方法是更改​​其中一个构建查询以获取具有任何类型更改的序列化类的所有构造函数。这将它缩小到几个类/构造函数(如果匿名函数是在非序列化类中创建的,则存在以这种方式捕获它的风险)。

有一次我把它归结为几个构造函数,从ndepend开始,我打开了一个旧的vs比较,它使用了反射器。这不仅显示了与异常相同格式的方法名称,而且已经显示在代码库中将我带到了正确的位置。

一旦我开始上课,我发现最好在一个单独的resharper窗口中打开每个程序集并查看该类的方法。那里很明显。

另请注意,在更改代码的情况下,即使相同的编译器版本/选项可能会给您不同的名称,因此在可序列化类中指向函数的私有字段非常容易。以下答案扩展了它:https://stackoverflow.com/a/1133465/66372

相关问题