
Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004
.pdf
ГЛАВА 13 Обработчики ошибок |
513 |
|
|
//Я должен был вынести этот блок за пределы функций
//SnapCurrentProcessMiniDumpA/W, так как они используют
//соглашение вызова naked и поэтому не могут работать с SEH. BOOL CalculateBeginningOfCallInstruction ( UINT_PTR & dwRetAddr )
{
BOOL bRet = TRUE ;
//При обработке исключений я обеспечиваю полную защиту.
//Мне нужно быть чрезвычайно внимательным, так как я читаю
//стек и могу исказить его вершину. Я не хочу, чтобы вызов
//функции SnapCurrentProcessMiniDump приводил к краху
//приложения, поэтому я обрабатываю все возможные исключения. __try
{
BYTE * pBytes = (BYTE*)dwRetAddr ;
if ( 0xE8 == *(pBytes k_CALLNEARBACK) )
{
dwRetAddr = k_CALLNEARBACK ;
}
else if ( 0xFF == *(pBytes k_CALLFARBACK) )
{
dwRetAddr = k_CALLFARBACK ;
}
else
{
bRet = FALSE ;
}
}
__except ( EXCEPTION_EXECUTE_HANDLER )
{
bRet = FALSE ;
}
return ( bRet ) ;
}
Резюме
В этой главе я описал обработчики ошибок, включающие в себя обработчики исключений и фильтры необработанных исключений. Обработчики ошибок по зволяют получить более подробную информацию об ошибках и оставить у пользо вателей при этом более благоприятное впечатление. Одно из условий более быс трой отладки состоит в своевременном получении необходимой информации — как раз для этого и нужны обработчики ошибок.
Исключения C++ и исключения SEH иногда путают. Исключения C++ входят в спецификацию языка C++, в то время как исключения SEH обеспечивает ОС; эти два типа обработки исключений абсолютно разные, хотя и тесно сплетены в де талях реализации.
Надеюсь, я смог показать вам всю голую правду об обработке исключений C++, и теперь вы дважды подумаете, прежде чем решитесь использовать их в своем

514 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода
приложении. Во первых, из за дополнительных расходов они ухудшают быстро действие программ. Однако гораздо хуже то, что в реализации компиляторов Microsoft блок catch (...) «съедает» ошибки SEH. Фактически это делает блок catch (...) самой плохой конструкцией программирования, которую только можно вообразить. Кроме того, нужно избегать функции _set_se_translator, потому что она работает не так, как многие предполагают. Возможно, некоторые пуристы объектно ориентированного программирования и языка C++ найдут мои взгля ды на исключения C++ излишне жесткими, но мне кажется, что единственная чистота, которая имеет значение, — это своевременная разработка качественных программ. Если вы откажетесь от обработки исключений C++, ваши программы станут чистыми настолько, насколько это вообще возможно.
Своим существованием обработчики ошибок обязаны магической функции SetUnhandledExceptionFilter, позволяющей установить конечный фильтр исключе ний SEH, который обеспечивает получение управления прямо перед появлением диалогового окна сообщения об ошибке и записать бесценную информацию о причинах проблемы. Приведенные мной функции API CrashHandler облегчат уста новку фильтров необработанных исключений и выполнят за вас всю грязную работу по преобразованию информации об ошибке, благодаря чему вы сможете сосредоточиться на отображении информации и уникальных компонентах свое го приложения.
В конце главы я рассказал про одну чрезвычайно полезную новую API функ цию — MiniDumpWriteDump. Она имеет несколько недостатков, но я о них позабо тился, сделав создание и последующее изучение минидампов максимально удоб ным. Вооружившись минидампами, созданными в самые подходящие моменты, вы сможете решать любые проблемы своих клиентов.

Г Л А В А
14
Отладка служб Windows и DLL, загружаемых
в службы
После драйверов устройств сложнее всего отлаживать код служб Microsoft Win dows и DLL, загружаемых в службы. Может показаться, что, раз службы являются всего лишь процессами пользовательского режима без UI, то отлаживать их столь же легко, как и консольные приложения. Увы, все не так просто. На самом деле со службами Windows и DLL, загружаемыми в службы, связано столько подводных камней, особенно касающихся защиты в Windows, что при работе с ними вам может захотеться рвать на себе волосы. При появлении Microsoft Windows NT очень немногие разработчики писали службы или вообще знали, что они существуют. Однако в сегодняшнем мире COM+, Microsoft Internet Information Services (IIS), расширений Microsoft Exchange Server и Windows Clustering многим разработчи кам придется иметь дело со службами. И отлаживать их.
В этой главе я представлю обзор основных характеристик служб. Чтобы по нять, как отлаживать службы и DLL, загружаемые в службы, такие как ISAPI филь тры и расширения, надо знать, как службы работают. Затем я поясню аспекты, напрямую связанные с отладкой служб. По мере прохождения этапов отладки службы я буду отмечать моменты, касающиеся определенных технологий служб Microsoft.
Основы служб
Служба характеризуется тремя основными свойствами:
служба должна работать постоянно, даже если в системе компьютера никто не зарегистрирован или при первоначальном запуске компьютера;

516 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода
служба не имеет UI;
службу могут контролировать и управлять ею как локальные, так и удаленные клиенты.
Решая, как реализовать свое приложение: в виде службы или обычного прило жения пользовательского режима, — спросите себя, предъявляются ли к пробле ме, которую вы хотите решить, эти три требования. Если да, надо реализовать приложение как службу. А если вы решили написать службу (и хотите ее отлажи вать), убедитесь, что вы четко понимаете работу служб. Сведений, приведенных в этом разделе, хватит, чтобы составить представление о том, с чем вам придется столкнуться. Если хотите узнать о службах больше, ознакомьтесь с прекрасной книгой Джеффри Рихтера (Jeffrey Richter) и Джейсона Кларка (Jason Clark) «Prog ramming Server Side Applications for Microsoft Windows 2000» (Microsoft Press, 2000 г.)1.
Прекрасный пример случая, когда следует писать службу, — создание прило жения, контролирующего источник бесперебойного питания (UPS). Все, что нужно делать ПО для UPS, — следить, когда UPS сообщит о сбое питания в сети, а когда заряд батареи подойдет к концу, программе следует инициировать управляемое выключение (controlled shutdown). Очевидно, что если ПО для UPS не работает постоянно (первый критерий в решении, должно ли приложение быть службой), выключения не произойдет, и, когда в UPS закончится заряд батарей, компьютер просто остановится. В ПО для UPS нет необходимости в UI (второй критерий), так как ему лишь надо выполняться в фоновом режиме, следя за UPS. Наконец, если вы работаете над системой бесперебойного питания для хранилищ данных, сис темные администраторы определенно захотят проверять состояние удаленных UPS (третий критерий).
Пока все довольно просто. Теперь приступим к работе служб. Первый аспект, о котором я расскажу, — специальные функции API, вызываемые для превраще ния обычного процесса пользовательского режима в службу.
API
Некоторые качества служб потребуют от вас определенных действий, чтобы при способиться к ним. Во первых, не важно, какую точку входа вы используете в служ бах: main или WinMain. Поскольку службы не имеют UI, точки входа для консоль ных приложений или приложений с GUI взаимозаменяемы.
Внутри main или WinMain прежде всего следует вызвать функцию API StartService CtrlDispatcher. Ей передается структура SERVICE_TABLE_ENTRY, в которой указывает ся имя и главная точка входа службы. Диспетчер управления службами (Service Control Manager, SCM), запускающий все службы, с которым в конечном счете общается StartServiceCtrlDispatcher, чтобы установить вашу службу, является сред ством ОС, которое, как следует из его названия, управляет всеми службами. Если ваша служба не вызовет StartServiceCtrlDispatcher в течение 30 секунд с момента запуска, SCM завершит ее работу. Как вы увидите ниже, такое ограничение по времени может сделать запуск отладки чуть интереснее.
1Рихтер Дж., Кларк Дж. Программирование серверных приложений для Microsoft Windows 2000. — М.: «Русская Редакция», 2001. — Прим. перев.
ГЛАВА 14 Отладка служб Windows и DLL, загружаемых в службы |
517 |
|
|
Когда вы вызываете SCM, он создает поток для вызова точки входа вашей службы. К точке входа службы предъявляется одно жесткое требование: нужно зарегист рировать обработчик через RegisterServiceCtrlHandlerEx и вызвать SetServiceStatus
втечение 82 секунд с момента запуска. Если за это время служба не выполняет вызовов, SCM считает, что в службе произошел сбой, хотя и не завершает ее. Если
вконце концов служба вызовет RegisterServiceCtrlHandlerEx, она продолжит вы полнение в нормальном режиме. Считая, что произошел сбой, SCM должен бы за вершить работу службы, но этого не происходит, — такое поведение, каким бы странным оно ни казалось, облегчает отладку выполняющейся службы.
RegisterServiceCtrlHandlerEx принимает еще другой указатель — на функцию обработчик. SCM вызывает функцию обработчик для управления рабочими харак теристиками службы в таких операциях, как остановка, приостановка или возоб новление.
Когда служба переходит между состояниями, запускаясь, останавливаясь и приостанавливаясь, она общается с SCM через функцию API SetServiceStatus. Боль шинству служб надо просто вызвать SetServiceStatus и инициировать основное состояние, в которое они переходят, — в этой функции нет ничего особенного.
Ясгладил некоторые подробности, связанные с функциями API, но обычно
вызовы StartServiceCtrlDispatcher, RegisterServiceCtrlHandlerEx и SetServiceStatus —
все, что нужно ОС от вашей службы, чтобы обеспечить ее работоспособность. За метьте, я ничего не упомянул о требованиях к коммуникационным протоколам, используемым службой для связи между написанным вами UI контроллера и ва шей службой. К счастью, службы имеют доступ ко всем обычным функциям Win dows API, так что вы вправе использовать проецируемые в память файлы (memory mapped files), почтовые ящики (mail slots), именованные каналы (named pipes) и т. д. В службах вам действительно доступны те же варианты, что и в обычном меж процессном взаимодействии. Самая сложная проблема со службами, как я гово рил в начале главы, — это защита.
Защита
Если не указать иное, службы выполняются под специальной учетной записью System. Поскольку Windows для всех объектов реализует защиту на уровне пользо вателей, учетная запись System допустима для машины, а не для сети в целом. Следовательно, процесс под учетной записью System не имеет доступа к сетевым ресурсам. Для многих служб, скажем, для упомянутого выше примера с UPS, про блем защиты в процессе разработки может не возникнуть. Но, если вы, например, пытаетесь разделить проецируемую память от службы к клиентскому приложению с UI, а защита установлена неправильно, вы столкнетесь с ошибками нарушения прав доступа от клиентских приложений, когда они будут пытаться проецировать общую память.
К сожалению, отладкой проблем защиты не решить — вам придется обеспе чить программирование служб и клиентских приложений с правильно настроенной защитой. Полное описание программирования защиты в Windows займет отдель ную книгу, так что приготовьтесь провести некоторое время, планируя програм мирование защиты с самого начала разработки. Чтобы у вас сложилось представ ление о диапазоне нюансов с защитой в службах, настоятельно рекомендую ста

ГЛАВА 14 Отладка служб Windows и DLL, загружаемых в службы |
519 |
|
|
ISAPI-фильтры и расширения
Экспортируемые функции, которые надо предоставить для фильтров и расшире ний, довольно просты, и можно легко создать тестовую программу, действующую как фиктивная IIS система. Вы можете протестировать все свои базовые алгорит мы в контролируемой среде, так что их можно полностью отладить до запуска службы в IIS.
Exchange Server
Можно собирать службы Exchange Server, запускающиеся в виде консольных при ложений, используя вспомогательные функции из WINWRAP.LIB. Запуск службы со стартовым параметром notserv вызовет ее исполнение в виде обычного про цесса; notserv должен быть первым среди указанных параметров.
Отладка службы
После тестирования и отладки общей логики можете приступать к отладке ваше го кода, выполняющегося как служба. Вся первоначальная отладка должна прохо дить в системе, где все под вашим контролем. В идеале нужна вторая машина ря дом с главной машиной для разработки, которую можно использовать для перво начальной отладки. На второй машине должна быть та же по версии и особенно стям система Windows, что рекомендуется пользователям для среды, в которой будет работать ваша служба. Цель отладки базового кода — проверка основной логики, тогда как предварительная отладка службы выполняется, чтобы «перетряхнуть» основной код, относящийся к службе. В ходе отладки вашего первого кода служ бы следует выполнить четыре задачи:
включить Allow Service To Interact With Desktop;
установить идентификационные данные службы;
подключиться к службе;
отладить стартовый код.
Включение Allow Service To Interact With Desktop
Независимо от типа отлаживаемой службы следует включить Allow Service To Interact With Desktop (Разрешить взаимодействие с рабочим столом) на вкладке Log On (Вход в систему) диалогового окна Properties (Свойства) службы. Хотя в службе не должно быть элементов UI, уведомления утверждений (assertion notifications), которые позволяют получить управление в отладчике, очень помогут. Уведомле ния утверждений в сочетании с прекрасным регистрирующим кодом (logging code), таким, какой предоставляет вам ATL для записи в журнал событий, могут облег чить отладку служб.
На начальных стадиях отладки я включаю диалог утверждений SUPERASSERT, чтобы быстро оценить общее состояние моего кода. (О SUPERASSERT см. главу 3.) Но по мере выполнения службы я устанавливаю параметры утверждений так, чтобы все утверждения проходили только через операторы трассировки.
Пока я не уверюсь в коде службы, я обычно оставляю параметр Allow Service To Interact With Desktop включенным. Одну мерзкую ошибку, встретившуюся в написанной мною как то службе, я долго не мог обнаружить, поскольку я отклю


ГЛАВА 14 Отладка служб Windows и DLL, загружаемых в службы |
521 |
|
|
да вы приступаете к отладке. Давайте создадим специальные проекты подключе ния (обратите внимание: следующие действия предполагают, что вы запускаете отладчик под учетной записью, имеющей все привилегии администратора и на ходящейся на машине в группе Debugger Users).
1.Соберите приложение. По завершении сборки закройте существующее реше ние.
2.Из меню File выберите Open Solution.
3.В диалоговом окне Open Solution измените значение в раскрывающемся списке Files Of Type на Executable Files и перейдите туда, где находится собранный EXE файл службы. Выберите EXE файл как решение и щелкните кнопку Open.
4.Сохраните решение, выбрав Save All из меню File, и в диалоговом окне Save File As присвойте ему имя вроде <проект>_Attach.SLN.
5.Щелкните правой кнопкой узел .EXE в окне Solution Explorer и из контекстно го меню выберите команду Properties.
6.На странице Debugging диалогового окна Property Pages проекта установите поле Attach на Yes (рис. 14 1).
Рис. 14 1. Страница свойств Attach Debugging
7.Щелкните OK в диалоговом окне Property Pages проекта.
8.Установите и запустите вашу службу обычным способом.
9.Когда будете готовы отлаживать службу, просто нажмите F5 в Visual Studio .NET с загруженным проектом подключения — и все!
В качестве альтернативного метода подключения отладчика можно вызвать функцию API DebugBreak. Когда появится диалоговое окно Application Error, про сто щелкните кнопку Cancel (Windows 2000) или Debug (Microsoft Windows XP/ Server 2003) и отлаживайте, как обычно. Помните: если вы собираете службу COM+, вызов DebugBreak следует выполнять за пределами любых COM методов или ини циаций свойств. Иначе COM поглотит исключение точки прерывания, генериру емое DebugBreak, и отладчик не будет подключен. Кроме того, не вызывайте Debug Break как часть начального стартового кода службы — причины я объясню в раз деле «Отладка стартового кода».

522 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода
Другой способ подключения отладчика к вашей службе — если вы зарегист рированы в системе с правами администратора — использовать Task Manager. Вызовите Task Manager, выберите вкладку Processes, щелкните правой кнопкой процесс, который хотите отладить, и из контекстного меню выберите команду Debug. ОС позволяет легко подключить отладчик, если вам известно, какой про цесс надо отладить.
Подключать отладчик к службам могут только пользователи с правами адми нистратора на локальной машине, иначе при попытке отладить процесс, выпол няющийся не под вашей учетной записью, команда Debug выведет информаци онное окно Unable To Attach Debugger (невозможно подключить отладчик).
ISAPI-фильтры и расширения IIS
До IIS 5 все ISAPI фильтры выполнялись внутри INETINFO.EXE — главной IIS службы, т. е. вы просто подключались к INETINFO.EXE и отлаживали единый процесс. В IIS 5 и выше из за новой объединенной внепроцессной модели расширения выполня ются в DLLHOST.EXE. ISAPI фильтры по прежнему выполняются внутри IIS процесса INETINFO.EXE. Новая модель делает IIS гораздо стабильнее и, по утверждению Microsoft, гораздо более масштабируемыми. Единственная проблема с отладкой в том, что вы можете не знать, в каком процессе DLLHOST.EXE выполняется ваше расширение.
В документации IIS сказано, что следует настроить свои расширения на выпол нение внутри IIS, чтобы иметь возможность их отлаживать. Единственная проблема изменения места выполнения ваших расширений в том, что развертывать их надо, используя объединенную внепроцессную модель. Поскольку я проповедую отладку в сценариях, близких к тем, в которых будут работать ваши пользователи, я пока жу вам трюк, который позволит отлаживать расширения, даже когда они выпол няются в DLLHOST.EXE, т. е. там, где они будут работать.
Но, прежде чем поговорить об отладчике, надо знать, как определить, в каком процессе выполняется ваш фильтр или расширение, так как выполняются несколько экземпляров DLLHOST.EXE. Во первых, скачайте фантастическую бесплатную ути литу Process Explorer с Web сайта Марка Руссиновича (Mark Russinovich) и Брюса Когсвелла (Bruce Cogswell) www.sysinternals.com. Я впервые упомянул Process Explo rer в главе 2, потому что это прекрасный инструмент, которым можно определить, перемещались ли DLL, загруженные в ваше адресное пространство.
Process Explorer покажет вам описатели, открытые процессом, и — главное — какие DLL в какие процессы загружены. Чтобы найти вашу DLL с помощью Process Explorer, сначала нажмите Ctrl+D чтобы указать, что вы хотите просмотреть DLL, затем нажмите Ctrl+F и в диалоговом окне Process Explorer Search укажите имя файла вашей DLL в поле ввода DLL Substring. Щелкните кнопку Search, и Process Explorer покажет список имен и идентификаторов процессов (PID), в которые загружена ваша DLL. Имея PID, вы можете подключить отладчик Visual Studio .NET к процес су командой Debug Process из меню Tools. Не забудьте прочитать врезку о других возможностях Process Explorer, так как это один из лучших инструментов кото рые можно держать на жестком диске.
Если вы ищете инструмент, эквивалентный Process Explorer, но работающий из командной строки, это TLIST.EXE, поставляемый в комплекте Debugging Tools