Отладка приложений

         

Использование API-функций


Службы обладают рядом уникальных свойств, и от вас потребуются некоторые усилия, чтобы к ним приспособиться. Во-первых, точка входа, которая используется в службах — main или winMain — не имеет особого значения. Поскольку служба не программирует пользовательского интерфейса, то вы можете использовать как одну, так и другую.

 UPS — Uninterruptible Power Supply. — Пер.

С этих функций начинается выполнение Windows-приложений. Функция main является консольной точкой входа, a WinMain — точкой входа графического интерфейса пользователя (GUI). - Пер.

Внутри функций main и winMain прежде всего нужно обратиться к API-функции startServiceCtrlDispatcher. При этом ей следует передать структуру SERVICE_TABLE_ENTRY, в которой нужно указать  имя и главную точку входа службы. Диспетчер управления службами (Service Control Manager — SCM), который запускает все службы и с которым, в конечном счете, общается StartServiceCtrlDispatcher (для того чтобы установить вашу службу), является средством операционной системы, которое, судя по его названию, управляет всеми службами. Если служба не вызывает функцию StartServiceCtrlDispatcher в течение 2 минут (для Windows NT 4) или 30 секунд (для Windows 2000) после своего запуска, то SCM завершает эту службу. Как будет показано чуть позже, этот лимит времени может определенным образом влиять на запуск отладчика.

Как только выполняется обращение к SCM-менеджеру, он порождает поток для вызова точки входа вашей службы. С этой точкой входа связано одно жесткое Требование: она должна вызвать функцию RegisterServiceCtrlHandler в течение 1 секунды после старта службы. Если вызов не последует в течение 1 секунды, SCM подумает, что служба потерпела неудачу, но не завершитее. ЕСЛИ служба, В Конечном счете, Вызывает RegisterServiceCtrlHandler, то она будет выполняться нормально. На первый взгляд, было бы логично, если бы диспетчер SCM завершил службу, заподозрив, что она закончилась неудачей, на самом деле он этого не делает. Преимуществом такого поведения является то, что значительно облегчается отладка, поскольку служба продолжает выполняться.


Функция RegisterServiceCtrlHandler получает еще и другой указатель — на функцию, называемую функцией обработчика (handler function). SCM обращается к этой функции для того, чтобы управлять производительностью службы на таких операциях, как остановка, приостановка (пауза) или продолжение выполнения.

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

Некоторые детали, связанные с API-функциями, опущены, но в основном обращения к  StartServiceCtrlDispatcher, RegisterServiceCtrlHandler И setservicestatus — это все, что операционная система требует от вашей службы для ее запуска и выполнения. Заметьте, что ничего не сказано о требованиях к протоколам обмена, с помощью которых служба связывается пользовательским интерфейсом контроллера, который вы пишете. К счастью, службы имеют доступ ко всем регулярным API-функциям Windows, поэтому возможно использование файлов, отображаемых в память, почтовых ячеек и именованных каналов. Службы предоставляют все возможности для организации нормальных связей между процессами. Наиболее трудной проблемой, связанной со службами, является безопасность.



Обеспечение безопасности




Если не указано иное, службы запускаются под специальной учетной записью System. Поскольку в Windows 2000 защита всех объектов выполняется на пользовательском уровне, системный учет ведется для отдельной машины, а не для сети в целом. Следовательно, процесс, выполняемый под учетной записью System, не имеет доступа к сетевым ресурсам. Во время разработки многих служб, таких как упомянутая выше служба управления источником бесперебойного питания, проблемы безопасности могут никогда не возникать. Но если, например, попытаться разделить ресурс отображаемой памяти между вашей службой и клиентским UI-приложением, а защита не установлена правильно, то в клиентских приложениях вы столкнетесь с ошибками типа "access denied" (доступ запрещен).

К сожалению, никакая отладка не решает до конца проблем безопасности. Подход к проблемам безопасности при программировании как служб, так и клиентских приложений должен быть одинаково серьезным. Полное описание программирования всех аспектов безопасности (security programming) в Windows 2000 заняло бы целую книгу, поэтому будьте готовы потратить некоторое время на планирование программирования безопасности с самого начала разработки приложения. Для первого ознакомления с вопросами безопасности программных служб я настоятельно рекомендую прочитать статью Фрэнка Кима "Why Do Certain Win32 Technologies Misbehave in Windows NT Services?" (Почему некоторые Win32 технологии плохо ведут себя в службах Windows NT?) в мартовском выпуске журнала Microsoft Systems Journal за 1998 год (http://www.microsoft.com/MSJ/0398/service2.htm). Другой превосходный источник — колонка Кейта Брауна (Keith Browns) "Security Briefs" (Коротко о безопасности) в журнале Microsoft Systems Journal.

Те из вас, кому приходилось заниматься этими проблемами, могут подумать, что вместо возни с разработкой служб гораздо проще запустить программу SRVANY.EXE из Windows 2000 Resource Kit, которая позволяет преобразовать любое приложение в службу, и покончить с этим делом. Это иногда действительно помогает, но в упомянутой статье Кима рассмотрены несколько причин, по которым SRVANY.EXE не годится для этого.

Теперь вернемся к основному вопросу этой главы: как отлаживать службы?



Основы программных служб


Вот три основных характеристики программной службы:

служба выполняется все время, даже когда на компьютере никто не зарегистрирован или когда он запускается впервые;  служба не имеет пользовательского интерфейса;  служба может управляться и контролироваться как локальными, так и удаленными клиентами.

Принимая решение о том, нужно ли писать приложение как службу или как нормальное приложение пользовательского режима, спросите себя, отвечает ли задача разработки, которую вы пытаетесь решить, этим трем требованиям? Если — да, то следует рассматривать приложение как службу. В этом случае необходимо хорошо понимать механизм работы службы. Если вы захотите изучить службы подробнее, просмотрите статью Джеффри Рихтера (Jeffrey Richter) "Design a Windows NT Service to Exploit Special Operating System Facilities" (Проектирование служб Windows NT для использования специальных возможностей операционных систем) в октябрьском (за 1997 год) журнале Microsoft Systems Journal (на MSDN).

Подходящим опытом написания именно службы (а не обычного приложения) является разработка программного обеспечения (ПО), которое должно контролировать источник бесперебойного электропитания (UPS1) для компьютера. ПО UPS должно контролировать сообщения аппаратуры UPS об отказе электропитания. Кроме того, когда питание пропадает, это ПО должно инициировать управляемое завершение работы компьютера. Очевидно, что если ПО UPS не выполняется все время (первый критерий для решения, должно ли приложение быть службой), то завершения не будет, и компьютер остановится только тогда, когда в аппаратуре UPS иссякнет батарейное питание. ПО UPS действительно не нуждается в интерфейсе пользователя (второй критерий), потому что от него требуется только выполнение в фоновом режиме и управление аппаратурой UPS. Наконец, если речь идет об аппаратуре UPS, используемых в распределенных центрах данных, то системные администраторы определенно захотят проверить состояние аппаратуры удаленных UPS (третий критерий).

Пока все это звучит достаточно просто. Теперь поговорим о том, как службы работают. Сначала рассмотрим специфические API-функции, которые нужно вызывать, чтобы превратить нормальный процесс пользовательского режима в службу.



Отладка базовых служб


Проверив общую логику, можно начинать отладку кода для его выполнения как службы. Начальная отладка должна выполняться на той системе, все управление которой находится в руках разработчика. В идеале рядом с ней следует установить вторую машину, причем версия и параметры операционной системы (Windows) на этой машине должны моделировать клиентскую машину, на которой будет выполняться данная служба. Если целью отладки основного кода была проверка базовой логики службы, то целью предварительной отладки службы является реорганизация базового кода в специфический код службы. Для выполнения отладки первого варианта кода службы следует:

 включить свойство Allow Service To Interact With Desktop (Разрешить службе взаимодействовать с рабочим столом);  установить идентификатор (имя) службы;   присоединить средства отладки к службе;   отладить код запуска.

При обсуждении каждой задачи мы будем, по мере необходимости, рассматривать конкретные проблемы, важные для различных технологий.

Включение режима Allow Service To Interact With Desktop

Независимо от того, какой тип службы вы отлаживаете, необходимо установить флажок Allow Service To Interact With Desktop на вкладке Log On диалогового окна Properties отлаживаемой службы. Хотя у службы не должно быть никаких элементов интерфейса пользователя, наличие панелей с сообщениями от утверждений, которые позволяют контролировать службу через отладчик, весьма желательно. Эти панели, объединенные с чрезвычайно полезным регистрационным кодом (например, кодом, который ATL-библиотека выдает для записи в журнал регистрации событий), могут значительно облегчить отладку служб.

На начальных этапах разработки я включаю в службу код, открывающий диалоговое окно ASSERTION FAILURE (SUPERASSERT), что позволяет быстро оценивать общее состояние службы (более подробная информация о программе SUPERASSERT приведена в главе 3). Однако после нескольких прогонов службы я изменяю параметры сообщений, направляя их через операторы трассировки.


До окончания отладки кода службы лучше оставить режим Allow Service To Interact With Desktop включенным. При отладке одной службы долго не удавалось устранить неприятную ошибку, которая заключалась в том, что хотя этот режим был выключен, панель сообщений от SUPERASSERT все еще раскрывалась. Вследствие того, что средства безопасности операционной системы не позволяют нормальным службам показывать панель сообщений, моя служба казалась зависшей. Прежде чем выключить режим Allow Service То Interact With Desktop, я дважды проверил (с помощью команды DUMPBIN /IMPORTS), что служба и все DLL, которые она использует, не вызывают панели сообщений (тем самым удостоверяясь, что не выполняются непредусмотренные вызовы функции MessageBoxA ИЛИ MessageBoxW).

Установка идентификатора службы

Чтобы избежать проблем безопасности при попытке запуска службы, можно установить ее идентификатор. По умолчанию все службы выполняются под учетной записью LocalSystem. Однако имеется возможность установить службу для запуска под учетной записью пользователя с правами Администратора.

Для этого нужно открыть диалоговое окно Properties службы, перейти на вкладку Log On и установить переключатель This Account, а затем нажать кнопку Browse и выбрать подходящую учетную запись из диалогового окна Select User. После этого следует напечатать и подтвердить пароль выбранной учетной записи. Для СОМ+-служб с помощью специальной утилиты (DCOMCNFG.EXE) можно также установить идентификатор регистрации (logon identity) службы, если вы предпочитаете им пользоваться.

Присоединение отладчика к службе

Если служба стартует успешно, отладка обычно не составляет особого труда. Все, что нужно сделать — это присоединить к ней процесс отладчика Microsoft Visual C++. Для того чтобы прикрепить к службе активный процесс отладчика Visual C++:

1. Запустите утилиту MSDEV.EXE.

2. Выберите пункт Start Debug меню Build и в раскрывшемся подменю выберите команду Attach To Process....

3. Установите флажок Show System Processes (Отображать все служебные процессы).



4. Выберите из списка процесс, который вы хотите отладить, и нажмите кнопку ОК.

Альтернативный метод присоединения отладчика: нужно вызвать API-функцию DebugBreak и, когда раскроется диалоговое окно Application Error, нажать кнопку Cancel, а затем выполнять отладку как обычно. Имейте в виду, что если вы создаете СОМ+-службу, то нужно вызывать DebugBreak вне любого СОМ-метода или обращений к свойствам. Если вы этого не сделаете, то СОМ-служба будет поглощать исключения точек прерывания, генерируемые функцией DebugBreak, и вы никогда не добьетесь присоединения отладчика. Кроме того, не следует вызывать DebugBreak в коде начального запуска службы (причины изложены ниже в разделе "Отладка кода запуска" данной главы).

Все другие средства присоединения отладчика (к службе) должны использовать Диспетчер задач (Task Manager) операционной системы. Загрузите Диспетчер задач, выберите вкладку Processes, щелкните правой кнопкой мыши на процессе, который хотите отладить, и выберите пункт Debug в раскрывшемся контекстном меню. В этом случае операционная система облегчает присоединение отладчика.

Если пункт Debug контекстного меню Диспетчера задач не активен, не волнуйтесь, — вы просто видите работу службы безопасности Windows 2000. Только пользователям, с правами администратора на локальной машине позволено присоединять отладчик к службам. Если программисты вашей компании обычно входят в систему под учетной записью домена, следует добавить эту учетную запись в группу Administrators на каждой машине.

US ISAPI фильтры и расширения

В версии 5 Internet Information Services (US 5) изменены программы, в которых выполняются ISAPI-фильтры и расширения. В предыдущих версиях IIS все фильтры и расширения запускались внутри INETINFO.EXE (главной службы IIS). В IIS 5 расширения выполняются в DLLHOST.EXE вследствие использования новой объединенной внепроцессной модели1расширений. ISAPI-фильтры все еще выполняются внутри IIS-процесса INETINFO.EXE. Новая модель делает IIS-службы намного более устойчивыми и, согласно Microsoft, намного более расширяемыми.


Единственная проблема отладки состоит в том, что вы можете не знать, под каким процессом из DLLHOST.EXE выполняется ваше расширение.

 От англ, pooled out-of-process model. — Пер. •,

Новая объединенная модель расширений применяется только к Web-сайтам, созданным после обновления (upgrade) IIS до версии 5. В результате обновления существующие совместно используемые Web-ресурсы будут выполнять расширения точно так же, как и версия IIS 4. Если ваше расширение обрабатывает свои собственные пулы потоков или использует любую форму функции RevertioSeif, то нужно установить это расширение для выполнения в адресном пространстве IIS. Сведения об установке расширений можно найти в MSDN в разделе "Pooled Out-of-Process Model for ISAPI" (Объединенная внепроцессная модель для ISAPI).

В документации IIS также говорится, что расширения, выполняющиеся внутри IIS, необходимо устанавливать так, чтобы иметь возможность их отладки. Единственный нюанс, связанный с изменением места выполнения расширения, заключается в том, что при этом они должны по-прежнему поддерживать объединенную внепроцеесную модель. Будучи убежденным сторонником отладки, продемонстрирую некий трюк, позволяющий отлаживать расширения, выполняемые под управлением DLLHOST.EXE.

Прежде чем говорить об использовании отладчика, выясним, как следует вычислять тот процесс, который выполняет ваш фильтр или расширение (потому что одновременно будет выполняться множество экземпляров диспетчера внепроцессного пула расширений DLLHOST.EXE). Сначала нужно загрузить свободно распространяемую утилиту HandleEx для Windows NT, с Web-сайта www.sysinternals.com Марка Руссиновича (Mark Russinovich) и Брюса Когсвелла (Bruce Cogswell). Эта утилита показывает дескрипторы уже открытых процессов и, что более важно, какие DLL в какой процесс загружены. Чтобы найти DLL с помощью HandleEx, нажмите клавишу <F3>, и введите имя DLL-файла в поле DLL substring диалогового окна HandleEx Search. Затем нажмите кнопку Search, и HandleEx выведет список имен и идентификаторы процессов (PID), которые загружают вашу DLL.


На рис. 10. 1 показано окно поиска утилиты HandleEx, сообщающее, в каком процессе выполняется расширение SIMPLE.DLL.

Рис. 10.1. Поиск расширения, выполняющегося в диспетчере внепроцессного пула (dllhost.exe), входящего в состав служб IIS 5

Получив значение PID, можно с помощью команды Attach To Process присоединить к процессу отладчик Visual C++. Имейте в виду, что, прикрепив к процессу отладчик, необходимо, чтобы он выполнялся даже тогда, когда отладка конкретной DLL будет закончена. Если завершить отладчик, то остановится и отлаживаемый процесс.

Поскольку вы ищете загружаемую DLL, то, очевидно, должны удостовериться, что она загружается, прежде чем ее можно будет отлаживать. Фильтры выполняются внутри INETINFO.EXE, поэтому нельзя присоединять отладчик до запуска службы IIS (попытка отладить инициализацию службы до запуска IIS завершится неудачей). А в случае расширений можно отлаживать и инициализацию, если вы достаточно изобретательны. Идея состоит в том, чтобы создать фиктивное расширение, которое приведет к загрузке IIS при соединении с Web-сайтом при помощи MS Internet Explorer, который заставит IIS-службы запустить DLLHOST.EXE. После того как вы найдете PID нового DLLHOST.EXE, можно присоединить отладчик и установить точку прерывания на функцию LdrpRuninitiaiizeRoutines, чтобы получить прямой доступ к точке входа расширения (ollMain). Мэт Пьетрек (Matt Pietrek) точно разъясняет, что нужно делать, чтобы установить эту точку прерывания в своей колонке "Under the Hood" в сентябрьском (за 1999 год) номере журнала Microsoft Systems Journal. После установки точки прерывания можно загружать реальное расширение с помощью Internet Explorer и отлаживать инициализацию службы.

Отладка кода запуска

Самая тяжелая часть отладки служб — отладка кода запуска. Диспетчер SCM ждет только 2 минуты под Windows NT 4 или 30 секунд под Windows 2000

Перед запуском службы И вызывает функцию StartServiceCtrlDispatcher, чтобы указать, что служба выполняется нормально.


Это время можно потратить на пошаговый прогон своего кода с просмотром переменных.

Единственный "чистый" способ отладки кода запуска службы состоит в том, чтобы использовать операторы трассировки и отладчик Visual C++. Кроме того, операторы службы можно просматривать с помощью утилиты DebugView/Enterprise Edition Марка Руссиновича, рассмотренной в главе 3. К счастью, код запуска службы обычно проще, чем ее основной код, поэтому отладка с помощью операторов трассировки не слишком болезненна.

Значения тайм-аутов SCM могут создать трудности для служб, которые не способны быстро стартовать. Медленная часть аппаратуры или природа службы иногда диктуют длительное время запуска. Решить эти проблемы можно при помощи полей dwCheckPoint и dwwaitHint структуры SERVICE_STATUS, которая передается в функцию SetServiceStatus.

Когда служба стартует, можно сообщить диспетчеру SCM, что вы входите в состояние SERVICE_START_PENDING (ожидание_запуска_службы), ввести рекомендуемое время ожидания (в миллисекундах) в поле dwwaitHint и установить поле dwCheckPoint в 0, чтобы SCM не использовал тайм-аут по умолчанию. Если на запуск службы требуется больше времени, то можно повторять вызов setservicestatus столько раз, сколько необходимо, увеличивая значение поля dwCheckPoint перед каждым следующим вызовом.

Кроме того, SCM добавляет записи в журнал регистрации событий (event log), объясняя, почему он не может стартовать конкретную службу. Чтобы просмотреть эту запись, запустите утилиту Event Viewer и поищите в колонке Source запись "Service Control Manager". Если журнал регистрации событий используется также и для упрощенной трассировки, то у вас должна появиться возможность разрешать многие проблемы запуска служб. Убедитесь, что параметры служб установлены так, чтобы ваша служба запускалась после службы Event Log.

   Кривая обучения (learning curve) определяет скорость изучения программного продукта. Чем круче эта кривая, тем меньше период его изучения (обучения).


— Пер.

 Реальная отладка

Ограничения GUI- отладчиков делают отладку служб интересным, но достаточно сложным испытанием для разработчиков. Все же отлаживать службы с помощью отладчиков, поставляемых вместе с Microsoft Visual Studio или Platform SDK, можно.

Если вы разрабатываете службы, которые предъявляют усложненные требо- , вания к обработке (выполняя, например, довольно трудные для Win32 процессы синхронизации или разделения памяти) или загружают DLL в службы типа IIS, то можете воспользоваться отладчиком SoftICE компании Compuware NuMega. SoftICE выполняется между CPU и операционной системой и может значительно облегчить отладочные процедуры кода пользовательского режима, например, запуск служб или межпроцессные связи, что не всегда позволяют делать GUI-отладчики. Работая с SoftICE, вы просто загружаете исходный код своего модуля, устанавливаете точку прерывания и, независимо от того, как или где ваш модуль загружается в память, можете сосредоточиться на отладке, не беспокоясь о проблемах, связанных с GUI-отладчиками. Представленный на сопровождающем компакт-диске SoftICE имеет намного более крутую кривую обучения1, чем отладчик Visual C++, но если вы разрабатываете службы и DLL, которые в них загружаются, то в итоге сэкономите много времени на его изучении.



Отладка основного кода


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

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

Службы СОМ+

Если вы компилируете СОМ+-службу с библиотекой активных шаблонов (Active Template Library — ATL) — такую, например, как утилита TraceSrv из главы11, то не нужно предпринимать никаких мер защиты. По умолчанию ATL выполняется как обычная программа пользовательского режима до тех пор, пока вы не зарегистрируете свое приложение, запустив его из командной строки с ключом Service.

Фильтры и расширения ISAPI

Экспортируемые функции, которые необходимо предусмотреть для фильтров и расширений ISAPI (Internet Server API) довольно просты, и вы легко сможете написать тестовую оболочку, которая действует как фиктивная система информационных служб Internet (Internet Information Services — US). В такой управляемой среде можно протестировать все основные алгоритмы службы, выполнив, таким образом, их полную отладку до того, как служба будет запущена под соответствующим сервером информационных служб Internet (IIS).

IIS-сервер — сервер информационных служб Интернета (его называют также Web-сервером), он является частью соответствующих служб. — Пер.

Exchange Server

Можно также создавать приложения служб обмена (Exchange service applications), которые выполняются как консольные приложения, если использовать вспомогательные функции из WINWRAP.LIB. Запуск службы с параметром notserv (он должен быть первым в списке параметров) приведет к тому, что она будет выполняться как нормальный процесс.



Отладка служб


Уникальный характер служб означает, что их программирование обусловливает необходимость решения многих проблем, не характерных для разработки обычных приложений пользовательского режима. Имейте в виду, что до сих пор обсуждались лишь минимальные функциональные возможности, которые необходимы для установки и выполнения служб. Мы даже не коснулись фундаментального требования — на необычных частях служб работали общие алгоритмы и реализации. Самый легкий поход к отладке служб — поэтапный.

Различают три основных этапа отладки служб: 

 отладка основного кода;   отладка базовой службы;  реальная отладка.

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



В этой главе описаны некоторые



В этой главе описаны некоторые особенности отладки служб Windows 2000 и загружаемых в них DLL. Службы имеют специальный статус в операционной системе, и связанные с этим обстоятельством вопросы безопасности требуют от разработчика четкого понимания сущности служб и нюансов их поведения. Отладка служб требует более серьезного предварительного планирования, чем большинство других видов отладок.
Первый шаг в отладке служб и загружаемых в них DLL — их отладка как нормальных приложений. На втором шаге необходимо воспользоваться преимуществами окружения служб, такими как включение взаимодействия с рабочим столом и применение инструментов типа HandleEx, чтобы найти информацию, необходимую для более быстрой отладки. Наконец, если разрабатываются большие, усложненные службы, которые описаны выше в разделе "Реальная отладка" данной главы, то для отладки можно прибегнуть к помощи SoftICE от NuMega.