.NET Windows Service аварийно завершает работу при отправке сообщения Windows - PullRequest
0 голосов
/ 21 октября 2011

У меня серьезная проблема с .NET Windows Service. Он работает на нескольких серверах с очень разными конфигурациями. Служба, похоже, подвержена сбоям на некоторых серверах, но стабильна на других. Нестабильность вводится недавно, но пока условия неизвестны. У нас есть серверы под управлением Windows 2003 / Windows 2003 R2 / Windows 2008. Большинство из них полностью обновлены.

Мы пытались создать сервис для разных версий target-framework-версии (2.0 / 3.5 / 4.0), но это не имеет значения. Машины с нестабильным сервисом нестабильны с каждой версией фреймворка. Я пытался восстановить платформы .NET, но это тоже не имеет значения. Насколько я могу судить, весь сервис и его зависимости находятся в управляемом коде.

Я также пытался запустить серверный код в версии для командной строки. Это, кажется, работает стабильно. Мы используем это в качестве обходного пути сейчас. Однако проблема не связана с учетной записью пользователя. Служба обычно работает как «Локальная служба». Я попытался запустить его под локальной учетной записью администратора, которая используется для запуска версии командной строки. Но служба все еще нестабильна.

До сих пор я смог создать воспроизводимую ситуацию на одном из серверов: - Запустите сервис на сервере. - Войдите в систему как пользователь домена в новом сеансе RDP на том же сервере. - Запустите наше клиентское программное обеспечение, которое обращается к нашему сервису через TCP-удаленное взаимодействие в этом сеансе. - Закройте клиент и сессию. - Откройте новый сеанс RDP с пользователем домена на сервере. - Мгновенный сбой в обслуживании!

Обратите внимание, что служба падает в тот момент, когда пользователь домена входит в новый сеанс RDP. На этом этапе наше клиентское программное обеспечение не было запущено. Если я не открою клиент и не получу доступ к службе с помощью удаленного взаимодействия по протоколу TCP в первом сеансе, служба не будет аварийно завершена во время второго входа в систему. Если я открою сеансы как локальный администратор, служба также не выйдет из строя.

Мне удалось подключить встроенный отладчик (OllyDbg) к аварийно завершающему сервису. Сбой с нарушением прав доступа при попытке выполнить по адресу 0x4bcdcee9. Этот адрес одинаков для всех серверов и конфигураций (я видел этот адрес каждый раз в журнале событий). Я посмотрел на стек сбой потока. Кажется, что нить создана непосредственно перед сбоем. Сначала он пытается загрузить Ole32.dll. Он запускает некоторый код из Ole32, а затем я вижу, что эти функции вызываются:

  • User32.SetTimer
  • User32.GetMessageW
  • User32.TranslateMessage
  • User32.DispatchMessageW

Сбой находится где-то в DispatchMessageW. Я могу видеть аргумент * MSG для DispatchMessageW в стеке. Похоже, это прошло:

  • hWnd = 0x00090082
  • Сообщение = 0x0000001e
  • wParam = 0x00000000
  • lParam = 0x00000000

Я пробовал Spy ++. Но он, похоже, не обнаруживает никаких hWnd в службе Windows.

Итак, служба получает это сообщение, пытается проанализировать и отправить его и каждый раз завершает вызов 0x4bc4cee9, который не отображает память и вылетает.

РЕДАКТИРОВАТЬ: По предложению Ганса я исследовал системные события. Я отлаживал сервис. Я добавил дополнительный сервис к своему исполняемому файлу, чтобы я мог запустить вспомогательный сервис, затем подключить отладчик и запустить реальный сервис. Таким образом, я могу отладить даже запуск службы. Я установил точки останова на SetWindowsHookA, SetWindowsHookW, SetWindowsHookExA и SetWindowsHookExW, но ни одна из них не была достигнута!?

РЕДАКТИРОВАТЬ 2: Я проверил все свои заметки и обнаружил, что я сделал неверные выводы, потому что в моих заметках была опечатка: -S В любом случае адрес сбоя - 0x4bc4cee9.В какой-то момент выполнения msado15.dll загружается туда.Я вижу, что когда клиент отключается от сервера, в отладчике есть 2 управляемых исключения.Вскоре после этого я вижу сообщение WM_Timer, которое обрабатывается диспетчером и вызывает CoFreeUnusedLibraries ().Это приводит к разгрузке msado15.dll.Я открыл msado15.dll в дизассемблере и загрузил символы от Microsoft.DLL является частью компонентов доступа к данным Microsoft (MDAC) 2.8 SP1.Версия 2.82.4795.0, что указывает на то, что это последняя версия, выпущенная в январе 2011 года. Для ADOConnection и ADORecordset имеются функции Advise () и Unadvise ().Advise () вызывает InitAsyncEvents () и вызывает RegisterClassEx ().WndProc, который передается в RegisterClassEx (), является FireEventOnMainThread (), который находится в 0x4bc4cee9!Я вижу функцию там!Что должно произойти, так это то, что при удалении объектов должны вызываться Unadvise () и DestroyAsyncEvents () и UnregisterClassEx ().Но почему-то этого не происходит.DLL выгружается до того, как она может отменить регистрацию классов.Который приводит к сбою на следующем событии.Это может как-то относиться к двум управляемым исключениям.Я буду исследовать дальше.

Stacktrace: http://pastebin.com/dsSjMe4Y

Log: http://pastebin.com/qD2MXvHd

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

Спасибо, Хитклифф

1 Ответ

0 голосов
/ 31 октября 2011

Я нашел проблему. Мне потребовалось почти 8 дней, чтобы придумать это и создать обходной путь!

Все версии ADODB до 6.0 имеют серьезную ошибку! ADODB 2.8 является частью MDAC 2.8 (для XP и Win2003), ADODB 6.0 является частью Vista / Win2008, а ADODB 6.1 является частью Win7 / Win2008R2. Ядро DLL является msado15.dll. Когда создается экземпляр класса Connection или Recordset, он регистрируется с помощью RegisterClass () и имеет WndProc с именем __FireEventOnMainThread (). После того, как все COM-объекты будут удалены снова, счетчик ссылок будет установлен в 0. При вызове Ole32! CoFreeUnusedLibraries () он вызовет DllCanUnloadNow () всех COM-библиотек DLL. DllCanUnloadNow () проверяет счетчик ссылок, и когда он равен 0, он возвращает 0, указывая, что он может выгружаться. В ADODB 6.1 (выпущен только для Win7 и Win2008R2) Microsoft реализовала исправление в DllCanUnloadNow (). Они проверяют AsyncEventsWnd и, если он все еще существует, они не будут выгружать DLL. Но настоящая ошибка все еще есть в распоряжении COM-объекта. Количество ссылок уменьшается, но по какой-то причине UnregisterClass () не вызывается. Когда DLL выгружается и отправляется широковещательное событие, приложение столкнется с нарушением прав доступа, поскольку WndProc больше не находится в памяти. Крах! В случае службы создается Ole32! CDllHost (не уверен, где). Этот класс запускает таймер с помощью TimerProc STAHostTimerProc (), который запускается каждые 300 секунд. STAHostTimerProc () вызывает CoFreeUnusedLibraries (). Есть много разных широковещательных сообщений. Например, когда новый сеанс пользователя запускается на терминальном сервере, он будет транслировать WM_TIMECHANGE. Таким образом, на компьютерах с Windows до Vista / Win2008, когда приложение создает ADODB.Connection или ADODB.Recordset и создает Ole32! CDllHost, а затем удаляет все COM-объекты, а затем ждет, пока таймер выгрузит msado15.dll, а затем дождитесь широковещательного сообщения, приложение рухнет!

Страшно, что Microsoft исправила это в MDAC 6.1, но они не выпустили исправление для более ранних версий. Это касается всех старых операционных систем.

В качестве обходного пути мы избежим того, что счетчик ссылок объектов COM ADO станет 0, создав статический объект ADODB.Connection.

...