Приложение не отвечает delphi
. when altering one's mind becomes as easy as programming a computer, what does it mean to be human.
8 июня 2010 г.
Как узнать, почему зависла программа?
Сегодняшняя статья будет посвящена нескольким подходам к отладке зависаний программы.
Состоит она из шести частей:
- Подготовка - общие действия для всех случаев отладки.
- Delphi - отладка зависания в Delphi.
- EurekaLog - поиск причины зависания в EurekaLog.
- Process Explorer - поиск причины зависания утилитой Process Explorer.
- Threads Snapshot - поиск причины зависания утилитой Threads Snapshot.
- Практический пример - пример с искусственным зависанием в программе.
Ну, прежде чем приступать к отладке программы, вам надо бы её пересобрать (делайте Build, а не просто Compile), добавив в неё отладочную информацию. Для разных подходов требуется разные форматы отладочной информации, поэтому лучше включить всё сразу, чтобы не думать :) Это необходимо сделать, чтобы отладочные средства могли показывать вам читабельные названия процедур и номера строк в исходниках. В противном случае, вам придётся иметь дело с машинным кодом и смещениями.
Итак, вам нужно включить (Project/Options/Linking): "MAP file" - Detailed, "Debug Information" (в старых версиях Delphi она называлась "Include TD32 debug info") - True, "Include remote debug symbols" - True:
Кроме отладочной информации, полезно включить опции "Stack Frames" и "Use Debug DCUs" на вкладке "Compiling":
Уж позвольте мне не повторяться, что делают эти опции.
Не забываем сделать Build после изменения опций. Понятно, что если у вас несколько проектов (DLL, BPL и т.п.), то менять опции и пересобирать надо все. Также, если вы используете сборку с run-time пакетами - по возможности, выключите их на время тестирования, ибо пакеты сильно всё усложняют.
Вы можете подумать: "но я не могу воспроизвести это под отладчиком!" или "на проблемной машине у меня не стоит Delphi!". Но не спешите переходить к следующему разделу.
Во-первых, вам необязательно запускать программу в Delphi. Вы можете запустить программу как обычно, вне среды, и работать с ней, пока она не зависнет - после чего подключить к ней отладчик. Во-вторых, если на машине не стоит Delphi - вы можете установить на неё удалённый отладчик. Подробнее об удалённом отладчике - см. справку или мою статью (большой объём, вот вариант в PDF) - раздел 2, в конце секции 2.1.1. про работу с отладчиком.
Для отладки зависшего проекта в Delphi его, конечно же, нужно открыть. Предварительно он должен быть скомпилирован - с установленными опциями отладки, как мы сделали выше. Кроме того, для локальной машины желательно, чтобы вы запускали программу из Output path, указанном в опциях проекта (т.е. не перемещали бы скомпилированный exe перед запуском).
Итак, вы запустили свою программу, она сколько-то там поработала и зависла. Открываем Delphi, загружаем нужный проект и делаем Run/Attach to process:
Из списка выбираем вашу программу (введя при необходимости имя удалённой машины, если Delphi и программа находятся на разных машинах), устанавливаем галочку "Pause after attach" и жмём "Attach".
Отладчик подключится к процессу и установит его на паузу - путём возбуждения точки останова в потоке отладчика:
Вы можете игнорировать поток отладчика - просто перейдите на окно Threads и выберите любой свой поток для его отладки. Вы можете просмотреть стек вызовов, переключаться между потоками, анализировать переменные, делать пошаговое выполнение и т.п. - в общем, пользоваться отладчиком Delphi как обычно. Если вы включали отладочную информацию, то вы можете закрыть окно CPU и пользоваться отладкой по исходному тексту.
Напомню, что при возможности отладку зависаний стоит производить в ОС Vista и выше - потому что в этих системах появилась новая возможность для отладчиком: Wait Chain Traversal. Отладчик Delphi последних версий поддерживает WCT. Поэтому, если вы используете BDS 2009 или выше и Windows Vista или выше, то в окне Threads напротив каждого потока в колонке "Wait Chain" можно увидеть его статус, чего он ждёт, есть ли взаимоблокировка и т.п.:
В EurekaLog есть фишка "Anti-Freeze". Собственно, её я уже разбирал в отдельной статье, поэтому не буду останавливаться сейчас - у нас и так сегодня куча материала.
Если же по каким-то причинам среда Delphi для вас недоступна - вам придётся производить отладку руками.
Перед тем, как производить отладку, надо подготовить Process Explorer. Здесь будет два шага - оба опциональных, но для максимального удобства лучше сделать оба.
Шаг первый - настроить Process Explorer на загрузку отладочных символов. Дело в том, что Windows поставляется с обычными исполняемыми модулями без отладочной информации. Для своих программ мы только что включили генерацию отладочной информации (см. первый пункт), то как мы можем сделать это для Windows, чьи исходники нам недоступны? Ну, Microsoft позаботилась об этом: она распространяет отладочную информацию для своих программ отдельно. Вы можете скачать её и получить читабельные стеки вызовов.
Для начала вам понадобится скачать и установить Windows Debugging Tools. Затем, вам нужно решить, качать ли всю отладочную информацию скопом или же пусть она качается по запросу отладочной программы. Если вы выбрали первый путь - то вперёд, качаем и устанавливаем. Лично я выбираю второй путь.
Для второго способа вам нужно создать папку на своей машине с правом чтения-записи файлов и папок в ней. После чего остаётся только настроить Process Explorer:
В первом поле указывается путь к DbgHelp.dll - если вы устанавливали Windows Debugging Tools, то берите библиотеку оттуда. Если нет - то берите C:\Windows\System32\dbghelp.dll (однако я не уверен, будет ли это работать).
После того, как вы это настроили, Process Explorer будет пытаться получить отладочную информацию о каждом необходимом файле и кэшировать её в указанной папке. Поэтому, когда вы просматриваете потоки процесса или их стек вызовов, вы можете иногда видеть надпись "Loading symbols for ABC.exe+0xXYZ. ". В общем, после этого вам станет доступно больше информации для системных модулей.
Второй момент, который нужно сделать - сконвертировать map-файл вашего проекта в формат, понимаемый Process Explorer. Дело в том, что Delphi создаёт только различные Borland-ские форматы отладочной информации, а Process Explorer, как утилита Microsoft, понимает только Microsoft-ские форматы отладочной информации. Я уже говорил об этом. Сделать это можно утилитой map2dbg. Это простая консольная утилитка. Качаете архив, распаковываете, открываете консоль и пишете:
По файлам Project1.exe и Project1.map утилита сделает вам файл Project1.dbg, который может быть использован в Microsoft-ских утилитах.
Что-ж, я тут много чего сказал. Давайте я продемонстрирую, что вы получаете, выполнив указанные выше вещи. Ниже - три скриншота. Слева направо: вид стека вызовов без выполнения обоих пунктов (т.е. без системной и без проектной отладочной информацией), вид стека вызовов с первым пунктом (с системной, но без проектной отладочной информацией) и вид стека вызовов с обоими пунктами (с системной и с проектной отладочной информацией):
Как видите, подключение отладочной информации даёт вам три вещи:
- Более читабельный стек вызовов (вместо имя-модуля+смещение вы получаете имя-модуля!процедура+смещение)
- Более полный стек вызовов (без отладочной информации эвристика трассировки стека может опускать вызовы)
- Более правдивый стек вызовов (анализатор может неверно определять имя функции, если идёт вызов внутренней функции, которая не имеет публичного имени, но рядом находится другая функция, которая как-раз таки имеет публичное имя - поэтому анализатор может посчитать внутреннюю функцию частью публичной)
Если вы не будете (или не сможете) подключать отладочную информацию - вам придётся работать со смещениями. Вы конечно, можете искать смещения в map-файле руками, но это весьма хлопотно. Гораздо проще просто выписать на бумажку все числа, приписав имя модуля. Затем запускаете проект у себя, ставите его на паузу и используете команду Search/Goto address. Вводите адрес - и Delphi переводит вас на строчку в исходном тексте, а если это невозможно - то открывает окно CPU. Какой вводить адрес? Два примера. Вы выписали Project1.exe + $1234 и Project2.dll + $4321. Базовый адрес exe обычно не меняется и равен $400000. Вы загрузили проект у себя. Базовый адрес exe тот-же - $400000, а вот DLL оказалась загруженной по адресу $50000000 (вы можете выяснить это в окне Modules: View/Debug Windows/Modules). Тогда вас интересуют адреса $400000 + $1234 = $401234 и $50000000 + $4321 = $50004321.
Фух, разобрались. Как видите, намного удобнее подключать отладочную информацию, чем работать без неё :)
Теперь, что вы собственно должны делать для отладки зависания. Ну, вы запускаете свою программу и работаете с ней, пока она не зависнет. Потом вы запускаете Process Explorer, выбираете в списке процессов свою программу, правый щелчок -> Properties (свойства). В свойствах процесса переходим на вкладку Threads (потоки):
Здесь вы можете посмотреть, чем занимаются потоки вашей программы. Кто кушает процессор, кто чего-то ждёт и т.п. Выберите поток и нажмите кнопку "Stack" для просмотра его стека вызова (пример окна - см. три скриншота чуть выше).
Конечно, вы не сможете посмотреть переменные или что-то такое - только состояние и точки выполнения потоков. Так что вам придётся использовать свои телепатические способности, чтобы определить причину зависания.
Ну, использование Process Explorer хотя и несложно, но не выглядит таковым. Для новичка тут много работы и новых понятий. Да и вся эта возня с отладочной информацией не слишком удобна. Поэтому я написал простую утилитку (сейчас - часть EurekaLog Tools), которая позволяет вам выбрать запущенный процесс и дампит в текстовый файл информацию по всем потокам, включая:
- Базовая инфа: ID, приоритет и т.п.
- Стек вызова. Используются следующие источники отладочной информации: Borland-ские, JCL, EurekaLog и madExcept. Чуть позже добавлю поддержку Microsoft-ских и закачку по запросу, как у Process Explorer.
- Информация от Wait Chain Traversal (на Windows Vista и выше).
- Контекст потока - регистры и флаги.
Собственно, вы запускаете свою проблемную программу и работаете в ней, пока она не зависнет. Потом запускаете утилиту Threads snapshot и тыркаете её на зависший процесс. Она снимет вам снимок потоков, который вы сможете проанализировать.
Как видите - достаточно простая и удобная альтернатива ручной отладке с Process Explorer. Дополнительный плюс - вы можете попросить вашего клиента снять вам снимок процесса на его машине. В случае же с Process Explorer-ом - навряд ли вы сможете объяснить клиенту, что куда ставить и где жать. Не забудьте только передать все необходимые файлы (map-файлы, например).
Я написал эту утилиту буквально только что за два дня. Поэтому она "немножко" сыровата. Нормальная и отлаженная версия этой утилиты должна войти в EurekaLog v7.
Я написал простую программу с двумя кнопками. Вы можете использовать её как обучающий пример. Первая кнопка создаёт несколько потоков, которые ждут друг друга. Вторая кнопка вызывает порчу памяти, приводящую к зависанию. Запустите программу, нажмите на кнопку и попробуйте выяснить причину зависания. Посмотрим, как мы сможем отладить эти два случая.
В Delphi: если у вас есть поддержка WCT, то отладка первой кнопки вообще не представляет сложностей: запустили, нажали, поставили программу на паузу и смотрим окно Threads:
Если же WCT у вас нет, то придётся поработать головой и руками. Вы должны проанализировать стеки вызова каждого потока: щёлкаете по потокам в окне "Threads" и смотрите стеки вызовов:
После переключения на поток открывается окно CPU с текущей выполняемой инструкцией, но вы можете щёлкать по строчкам в окне "Call stack", чтобы посмотреть исходный код.
Проанализировав стеки вызовов всех трёх потоков, вы составите картину произошедшего.
Как эта же ситуация выглядит в Process Explorer и Threads snapshot? Ну, вы запускаете Process Explorer и смотрите стеки потоков (у меня первый поток открывался около минуты, не знаю, с чем связано - потом пошло гладко):
Хотя вы не видите здесь номеров строк, но вы видите имена функций и последовательность вызовов - например, в примере выше обратите внимание на Thread1Func, различные dispatch-функции, CriticalSection.Aquire и т.п. И снова: вы смотрите стеки вызова всех потоков и реконструируете ситуацию. Сделать это будет сложнее, чем используя Delphi (ибо нет номеров строк), но с известной долей телепатии - вполне возможно.
Что касается Threads snapshot, то она даёт такой лог (я отрезал лишние части для уменьшения лога):
К сожалению, вывод WCT пока не очень форматирован (это моё домашнее задание! :) ), но цепочку ожиданий увидеть можно. Плюс стеки потоков (к сожалению, без системной отладочной информации - это моё второе домашнее задание!) самым решительным образом намекают на происходящее.
Второй случай сложнее. Потому что зависание -лишь внешнее проявление другой проблемы.
Итак, в Delphi вы запускаете программу, она виснет, мы ставим её на паузу. У нас только один поток, поэтому WCT нам не помощник, даже если он есть. Поэтому сразу переключаемся на главный поток и смотрим стек вызовов:
Опять-таки: щёлкая по стеку вызовов, мы видим исходный код.
В этот раз, простой анализ стека вызовов нам не помогает. Пока что неясно, что же случилось. Тем более, что программа вроде бы не висит, а что-то делает (т.е., строго говоря, у нас не зависание, а зацикливание). Поэтому мы начинаем пошаговый прогон программы. Пройдясь по коду мы видим, что код постоянно крутит цикл со Sleep, проверяя некий флаг. Мы видим, что этот код - код менеджера памяти FastMM. Почитав комментарии в коде, мы узнаём, что FastMM пытается получить блокировку. А проверяемый флаг - это признак занят/свободен. Поскольку FastMM проверяет этот флаг уже полчаса - ясно, что тут что-то не так. Этот же вывод следует из того, что в нашёй программе всего один поток - т.е. ждать-то и вовсе некого, кроме нас в программе никого нет. Иными словами, состояние флага не соответствует действительности - т.е. он испорчен вследствие повреждения памяти.
К сожалению, найти повреждение памяти не так-то просто и это отдельная тема, которую я недавно закончил обсуждать.
Поэтому, задача анализа зависания - выяснить причину. Мы её нашли: это - повреждение памяти. Дальше, анализ зависания закончен, но проблема пока не найдена и не устранена - мы переходим к анализу и поиску проблем с памятью.
Собственно, второй случай выглядит примерно аналогично везде (в Delphi, Process Exporer и Threads snapshot): мы получаем примерно один и тот же стек вызовов, который может меняться время от времени, но всегда это будет цикл и с вероятностью в 99% - стоять на Sleep. Делается просто несколько проверок, чтобы в этом убедиться. Для примера - вот как это выглядит в Process Explorer:
Напомню, что из этого мы вынесем только вывод (не однозначный, конечно же, а всего лишь обоснованное предположение), что у нас есть повреждение памяти. Дальше - это уже другая история.
Фух. На сегодня у меня всё. Надеюсь, этот материал был полезен. Если хотите, вот ещё дополнение - презентация по ручному поиску места ошибки по адресу (см. "How to find the exception source line"). На английском, но может пригодится или будет интересно.
Проблема такая:
Приложение-процесс работает постоянно, но иногда оно зависает по так и невыясненной причине. Можно ли сделать так, чтобы приложение само определило, что оно повисло (в этом случае я мог бы вставить код выгрузки/загрузки приложения)? Если такой вариант невозможен, то какой есть более-менее простой способ определить из другого приложения что искомая прога висит?
В 2002-Октябрь одна - IsHungAppWindow - уже есть. Но раньше не было, хотя работают начиная с 95-ой. Я сам о них в свое время на каком-то буржуйском форуме прочитал.
AlexVit © (04.03.03 08:33)
Парни, большое спасибо за ответы!
Я до сих пор пробую разобраться, почему прога виснет.
Назначение этой проги в следующем:
она висит на машине пользователя в круглосуточном режиме и отправляет/принимает данные с оракловского сервера. Отправляемые/принимаемые данные клиентской машины читаются/пишутся в локальные таблицы БД (dbf-формата). С оракловской БД работаю в клиент/серверном режиме через Oracle Provider, используя ADO. С локальной БД работал сначала через BDE - висла, предположил, что проблема с BDE и перешел также на ADO - продолжает виснуть. Связь с сервером идет по радиодоступу (клиентские машины находятся на постах ГАИ, которые на пару десятков км отдалены от города). Трафик приличный, иненсивность обработки данных - около десятка записей в сек. Связь надежная, но иногда рвется, поэтому предусмотрел обход этого дела с обработкой возникающих exceptions при разрыве связи и дальнейшем переподключении к серверу и приему/отправке накопившейся очереди данных. Обработка данных идет паралалельно тремя "потоками" через три таймера.
Прога виснет случайным образом: может несколько дней работать без проблем, а может несколько раз в сутки зависать. Закономерности какой-то пока не выяснил (сами понимаете - на посты не больно-то наездишься, а их всего шесть, выручает RA - удаленный админ, установленный на каждом посту). Все это затрудняет выяснение причин зависания. Что я пробовал: вставил ловушку для любых exceptions (кроме оракловских, которые обрабатываются, как уже сказал, отдельным образом) через OnException - не помогло, так как желанного exception не возникает; ввел протоколирование действий программы в log-файл - помогло только выснить, что Microsoft Provider для ODBC иногда выдает зарезервированную ошибку без описания, но это легко обошел самоперезагрузкой проги; дошел от отчаяния даже до того, что вставил перед каждой строкой кода команду записи номера данной строки в log-файл, чтобы определить после какой строки виснет - тоже ничего не дало, так как зависание обнаруживалось при работе кода разных таймеров, т.е. в разных местах кода.
Может быть, есть какие-то технологии для отладки таких вещей?
Был бы очень рад, если кто-то чего подсказал по этому поводу!
Извиняюсь, что сумбурно изложил, если кого заинтересуют подробности, пишите - сообщу.
>Прога виснет случайным образом: может несколько дней работать без проблем, а может несколько раз в сутки зависать.
> Для пересылки пакетов данных использовал FTP протокол -
> он с докачкой, правами доступа и т.п. Для пересылки realtime
> данных наверно свой протокол (100мс не слишком большой траффик,
> хотя смотря сколько данных).
Поначалу похожим образом и предполагалась работа: через TCP/IP-протокол, мой шеф навязывал TCP/IP-навроченные и сложные библиотеки фирмы-партнера, которые к тому же требовали создание дополнительных модулей на стороне сервера. Появляется доп. звено, которое по времени вообще некогда было писать и тестировать, поэтому пришлось обойтись прямым подключением к БД, благо что Oracle client все равно шлет запросы в виде TCP/IP-пакетов.
> В чем выражается зависание?
В том, что данные перестают приниматься/передаваться клиентом; когда в этом случае пробуешь вызвать процесс (его окно), ни какой реакции на позывы мыши от процесса не происходит, то бишь повис, родной. Пробовал ждать (долго ждать), думал, занято приложение обработкой данных, но в нормальном (не зависшем) режиме Popup Menu выскакивает сразу же в ответ на щелканье мыши.
При этом никакой видимой запредельной загрузки ОЗУ не происходит, и другие процессы и приложения (тоже относящиеся к этой же системе) работают без проблем сколько угодно времени.
Если же ты ждёшь пока отработает другой процесс, например пока файл не освободится или пока внешняя программа не завершит работу или ещё какое действие, то разумно подойти немного по другому:
Как часто зависит от того насколько длинные циклы. Если считается напряжная математика и циклы очень большие, то если вызывать при каждом проходе цикла то будет сильное замедление.ProcessMessages почти не тормозит систему, можно легко убедиться если сделать тест, получается несколько миллионов вызовов в секунду.
1/5 секунды достаточно малое время и торможения реакции на мышь и клавиатуру не заметно, в тоже время при такой частоте замедления работы циклов не будет.А вот это наоборот очень заметно, сильная не ритмичность при движении мышки, при перемещении окна.
Чего вызывать? И главное зачем?
Выполнялся с и без Application.ProcessMessages. На разница в скорости составила в аккурат в 2 раза. Так это операции с файлами, которые в общем-то довольно долгие сами по себе.
Теперь по поводу математики, о которой я говорил, вот пример кода
С использованием Application.ProcessMessages цикл работает на 4 порядка медленнее!
А вот это наоборот очень заметно, сильная не ритмичность при движении мышки, при перемещении окна.А ты пробовал? Я пробовал на если делать Application.ProcessMessages 10 раз в секунду то я не замечаю вообще никакой тормознутости, если 5 раз в секунду - ощущается, но только если присматриваться.
Я пробовал, на моем старом компьютере P3 650 цикл в миллион выполнился за три секунды, на новом 2.4 ггц за 1,5 секунды.
По моему не о чем беспокоиться 1,5 секунды на 1 000 000 вызовов, особенно когда надо просто таскать окно.
Нет, не правильно . Есть один случай, когда Application.ProcessMessages надо использовать с осторожностью: когда мы имеем очень длинный цикл, который делает внутри какую-то очень быструю операцию, тогда время затраченное на выполнение Application.ProcessMessages оказывается сравнимо или больше, чем основной код. Как правильно заметил Anatoly Podgoretsky, на циклах меньше миллиона иттераций можно применять Application.ProcessMess без всяких проблем. Рассмотрим несколько примеров:
For i:=0 to 100000000 do
begin
j:=i+j;
Application.ProcessMessages ;
end;
Здесь мы имеем очень длинный цикл: 100 миллионов раз делается простое арифметическое действие и Application.ProcessMessages. Действие j:=i+j очень простое, и компьютер выполняет его на 4-5 порядков быстрее чем Application.ProcessMessages, следовательно в этом коде 99.99\% времени выполнения прийдётся на выполнение баластной операции Application.ProcessMessages, так как цикл долгий, то разница во времени выполнения - пара секунд и пара часов будет слишком существенной, чтобы необращать на неё внимание. Следовательно этот код не годится, переделываем его примерно таким образом:
For i:=0 to 100000000 do
begin
j:=i+j;
if i mod 1000 = 0 then Application.ProcessMessages;
end;
Теперь приложение не будет выглядеть зависшим, но Application.ProcessMessages будет занимать лишь несколько процентов времени работы программы и замедление программы с лихвой компенсируется удобностью работы.
Теперь другой случай:
For i:=0 to 100000 do
begin
j:=i+j;
Application.ProcessMessages ;
end;
Код похож, только количество проходов невелико - хоть с Application.ProcessMessages, хоть без него он будет выполнятся практически мгноменно. Можете ставить его, можете не ставить - разницы нет.
Допустим мы запускаем квери на базе данных:
For i:=0 to 100 do
begin
Query.чего-то-там-делаем-и-запускаем;
Application.ProcessMessages ;
end;
Запуск квери - очень долгая операция по сравнению с Application.ProcessMessages, даже самая быстрая квери на несколько порядков медленнее, чем Application.ProcessMessages - здесь поставите вы Application.ProcessMessages или нет - выигрышь в скорости выполнения будет 0.001\% в самом лучшем случае, поэтому желательно Application.ProcessMessages в данном случае ставить.
Теперь ещё один момент: допустим у вас есть цикл - любой из них, а как его прервать, например, чтобы кликнуть кнопку "Stop" и цикл прервался? Без Application.ProcessMessages это сделать трудно.
Вот как мы это реализуем, возьмём за основу следующий код:
For i:=0 to 100000000 do
begin
j:=i+j;
if i mod 1000 = 0 then Application.ProcessMessages;
end;
Теперь объявим глобальную переменную Canceled:boolean;
На кнопку "Start" "повесим" следующий код:
Canceled:=False;
For i:=0 to 100000000 do
begin
j:=i+j;
if i mod 1000 = 0 then
begin
Application.ProcessMessages;
if Canceled then
begin
ShowMessage('Loop has been interrupted!')
Break;
end;
end;
end;
На кнопку "Stop" "повесим" следующий код:
Теперь нажатие кнопки Start - запустит цикл, а кнопкой Stop этот цикл можно досрочно прекратить.
Вопрос
Имеем следующую проблему: SSO-агент блокирует запуск Delphi на одной из рабочих станций, а именно файлы delphi32.exe и install.exe: при запуске приложений без какого-либо перехвата окон появляется ошибка запуска приложения. Были предприняты следующие действия:
- удаление SSO-агента, результат - ошибка запуска исчезает, приложения запускаются;
- повторная установка SSO-агента, переименование файлов в delphi3232.exe и install123.exe, результат - ошибка запуска исчезает, приложения запускаются;
- переименование файла C:\Program Files (x86)\Indeed-ID\Enterprise SSO\IndeedID.SSO.black.list.dll в C:\Program Files (x86)\Indeed-ID\Enterprise SSO\IndeedID.SSO.black.list1.dll, результат - ошибка запуска исчезает, приложения запускаются.
ОС на рабочей станции: Windows Server 2008 R2 Standard SP1, на рабочей станции с ОС WinXP ситуация не повторяется.
Delphi версии 7
Ответ
Delphi запускается на Win 2008 R2, Win 7 x64 в режиме совместимости.
Для блокирования пользовательских приложений Indeed-Id Enterprise SSO использует механизм AppCertDlls, который позволяет регистрировать dll, которые экспортируют функцию CreateProcessNotify. Она и принимает решение о том, блокировать процесс или нет. В x64 окружении регистрируется 2 такие библиотеки (x86, x64) для блокировки приложений соответствующих типов.
И, при включении одновременно помощника по совместимости и механизма AppCertDlls в случае с Delphi происходит конфликт этих двух механизмов. Скорее всего речь идет о системной ошибке или же недокументированном моменте использования AppCertDlls в данном случае (хотя AppCertDlls сам по себе официально не документируется). Проявляется это следующим образом:
При наличии сколь-нибудь сложного кода (вызовы winapi, stl) в CreateProcessNotify() происходит сбой инициализации и старта процесса. Нашему коду управление не передается вовсе, дело не доходит даже до DllMain().
Если же код тривиален (сравнения и return'ы), то инициализация и старт происходят успешно.
При изменении имени исполняемого файла сигнатура приложения сбивается и ОС не воспринимает его как требующего особого режима совместимости. В этом случае все отрабатывает штатно.
Для проверки были сделаны тестовые AppCertDlls (32 и 64 бита), которые умеют только логгировать вызов своей функции. Результат был получен аналогичный, delphi начинает падать при регистрации в реестре тестовой x86 dll.
Экспортируемые функции описаны правильно, проблем со стеком быть не должно. Даже обойти это со своей стороны мы не можем, нам не передается управление.
Одним из возможных вариантов решения (кроме сбивания сигнатур исполняемых файлов) является ручное отключение SSO AppCertDlls. Сделать это можно в реестре, удалив значения из ключа HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDlls
Вопрос
Мы выполнили удаление указанных Вами значений для Indeed-Id из реестра. Ошибка после этого не проявляется, Delphi запускается, но фактически эта операция, насколько я понимаю, аналогична процедуре переименования блэклиста.
Также в одной части объяснения Вы говорите о том, что конфликтует процесс запуска приложения в режиме совместимости и работа SSO-агента (используемого им механизма AppCertDlls), с другой стороны, Вы пишете о том, что SSO-агент не влияет на запуск Delphi.
Кроме того, проблема нерешаема с точки зрения Indeed-Id, так как затрагивает базовые механизмы, на которых основывается работа системы. Таким образом, если я Вас правильно понимаю, мы должны быть готовы к тому, что у всех пользователей, работающих с 64-битными системами, после установки клиентских частей Indeed-Id начнут падать все (или некоторые) приложения, запускаемые в режиме совместимости, и единственным выходом будет отключение блэклиста SSO, хотя SSO-агент не влияет на запуск приложений, приэтом мы не сможем использовать для таких пользователей блокировку запуска нежелательных приложений, так как отключим блэклист для того, чтобы запускались приложения в режиме совместимости. Или я Вас неправильно понимаю?
Также мы проверили запуск Delphi при включенном и отключенном режиме совместимости: результат один и тот же - без дополнительных действий (переименование запускаемых файлов, блэклиста, удаления агента или ключей в реестре) приложение не запускается.
Ответ
SSO-агент не влияет на запуск Delphi с точки зрения своей внутренней функциональности, его код блокировки нежелательных приложений не выполняется при падении и до падения Delphi. Delphi 7 не является приложением, совместимым с ОС Windows 2008 R2 Server и Windows 7, поэтому ОС обрабатывает запуск этого приложения особым образом, основываясь на сигнатуре исполняемого файла.
Я думаю, проблемы могут быть только с приложениями, несовместимыми с теми версиями ОС, которые используются у пользователей и о запуске которых ОС предупреждает особым образом (как и в случае с Delphi). Для запуска подобных приложений при наличии запущенного SSO агента у конечных пользователей можно либо сбивать сигнатуры исполняемых файлов (переименование), либо отключать блокировку запуска нежелательных приложений.
Читайте также: