Как отлаживать (шаг в) BinaryFormatter.Deserialize()?

Мое приложение пытается десериализовать данные, отправленные клиентом, и не работает со следующей ошибкой:

Исключение выбрано: 'System.Runtime.Serialization.SerializationException' в mscorlib.dll

Дополнительная информация: Невозможно получить член '<.ctor > b__0'.

googling не дает никаких результатов. Хорошо, я решил, что буду входить в логику десериализации и попытаться выяснить, что именно вызывает это. Ну, прошел день, и я нигде не закрылся.

Я использовал инструкции Microsoft Reference Source для настройки Visual Studio. Он что-то скачал

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

но отладчик не входит.

Я больше искал googled и нашел другой способ сделать это - установил приложение dotTrace и использовал его как исходный сервер. И это тоже не помогает. Я все еще вижу следующее:

введите описание изображения здесь

Symbol Load Information popup для mscorlib.pdb говорит

C:\Users\я\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 с теми же результатами).

Ответ 1

Без каких-либо подробностей я могу предположить, что это проблема совместимости с версиями объектов, которые вы пытаетесь сериализовать и десериализовать. Похоже, клиент отправляет вам несколько старых битов объекта (без лямбда в конструкторе). И ваш сервер работает с более новой версией программного обеспечения, ища какой-нибудь лямбда-метод.

<.ctor > b__0 - имя метода для первого лямбда-метода в .ctor(конструктор объекта).

Так, например, если у вас на объекте клиентской машины 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 в нем.

Сгенерированный IL для метода lamda в классе A1

Ответ 2

Microsoft не загружает источник каждого обновления mscorlib.dll, поэтому вы получаете только публичные PDB без каких-либо исходных данных. Но есть добавление Visual Studio из Redgate Reflector, где вы можете декомпилировать Сторонних DLL файлов и выполнить их в отладчике VS.

введите описание изображения здесь

введите описание изображения здесь

DotPeek от Jetbrains также поддерживает создание и размещение PDB сервера Symbol, чтобы разрешить отладку.

Возможно, это поможет вам отладить вашу проблему.

Ответ 3

tl; dr: другая версия компилятора (или разные настройки?) может генерировать разные имена для сгенерированного метода, соответствующего анонимной функции. Если такой метод указывается частным полем сериализованного класса, вы получаете исключение, даже если источник не изменился между 2 строками.


Я просто отслеживал точный тип ситуации, но с десериализацией, вызванной в сеансе приложения asp.net. Как и в этом случае, используется BinaryFormatter.

<.ctor > b__0 соответствует сгенерированному методу, соответствующему анонимной функции.

Теперь проблема заключается в зависимости от такого метода во время сериализации, потому что имя не гарантируется одинаковым для разных построений (даже с неизменным исходным кодом). Это почти наверняка отслеживает какой-то делегат в поле частного экземпляра сериализованного класса. Обратите внимание, что такой класс, где анонимная функция объявлена, не обязательно является классом, который содержит ссылку на эту функцию в частном поле.

К сожалению, у меня не было времени отслеживать, почему один и тот же источник создает разные имена для анонимной функции, но учитывая историю задействованного проекта, это либо другая версия компилятора опций, переданных ей. Я убежден, что это было раньше.

Если у вас есть доступ к сборкам с обеих сторон, вы можете подтвердить это изменение. Сначала я попытался экспортировать несогласованный источник обеих сборок в DotPeek, а затем выполнить разницу между папками. Это не доказывает хороший процесс, но это может быть связано с некоторыми настройками DotPeek, которые необходимо установить или что-то в этом роде.

Чем лучше работала комбинация ndepend и отражателя. Вы можете выполнить сравнение сборки ранее. То, как я это делал, - это изменить одну из запросов на сборку, чтобы получить все конструкторы сериализованных классов, которые имели какие-либо изменения. Это сузило его до нескольких классов/конструкторов (существует риск не поймать его таким образом, если анонимная функция была создана в несериализуемом классе).

Как только у меня было это до нескольких конструкторов, из ndepend я открыл старое vs новое сравнение, которое использует рефлектор для него. Это не только показало имя метода в том же формате, что и исключение, но уже показало, что я получил правильный код в базе кода.

Как только я новый класс, мне было лучше открыть каждую сборку в отдельном окне resharper и посмотреть методы этого класса. Там видно.

Также обратите внимание, что в случае, когда код изменен, даже одна и та же версия/версия компилятора может дать вам разные имена, поэтому очень хрупко иметь частные поля в сериализуемых классах, которые указывают на функции. Следующий ответ расширяется: fooobar.com/info/24995/...