![]() |
Модераторы: Snowy, MetalFan, bems, Poseidon |
![]() ![]() ![]() |
|
Avers |
|
||||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 113 Регистрация: 20.6.2008 Где: 26rus Репутация: нет Всего: нет |
Не могу разобраься, какой объект вызвал ошибку, а какой метод (в котором эта ошибка возникла)
Простой пример: бросаем на форму кнопку и пишем код
Как бы кнопку не нажимали, в сообщении будет TButton А если написать свой обработчик исключений))... начинается самое интересное
При фокусировке на кнопке жмем энтер и тоже получаем TButton. Но вот если нажать кнопку мышью, то получим TForm1 (!!!!) Почему? --------------------
Born to be wild |
||||
|
|||||
CodeMonkey |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1839 Регистрация: 24.6.2008 Где: Россия, Тверь Репутация: 29 Всего: 89 |
Вообще-то метод, в котором возникло исключение, стандартными средствами не отслеживается никак.
Стандартный Application.HandleMessage всегда вызывается с Sender = форме, в методах которой расположен его вызов, а если он вызывается вне формы - то в Sender передаётся Application. Если вас интересует диагностика места возникновения ошибки - см., например, Вопрос КС №57687 -------------------- Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. |
|||
|
||||
Avers |
|
|||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 113 Регистрация: 20.6.2008 Где: 26rus Репутация: нет Всего: нет |
В данный момент мне хотелось бы узнать, почему MyException передатся один раз TButton, другой раз TForm1
То, что нельзя легко и просто узнать метод, вызвавший ошибку, я смирюсь. Но хочется хотя бы знать объект и/или модуль, в котором ошибка возникла. --------------------
Born to be wild |
|||
|
||||
CodeMonkey |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1839 Регистрация: 24.6.2008 Где: Россия, Тверь Репутация: 29 Всего: 89 |
Здесь я немного наврал. Не заметил:
Вероятно это связанно с особенностями обработки сообщений в Windows. Дело в том, что многие сообщения шлются не самому контролу, а его контейнеру. При низкоуровневом программировании это позволяет положить на окно кнопку, не создавая для неё отдельный класс, а управляя ей (ловить её сообщения) из контейнера. Соответственно VCL для эмуляции того поведения, что мы видим сейчас, должна ловить сообщения в контейнере и перенапрявлять их (прямым вызовом через Perform) дочерним контролам (CN_COMMAND с BN_CLICKED для случая OnClick кнопки). Обработчик исключений при этом остаётся установленным на контейнер. Такую работу выполняет, например, DoControlMsg. Соответственно, кто будет в Sender зависит от того, кому было направлено сообщение. Объект - это как? Объект - это вообще-то адрес в памяти. Вы хотите узнать число типа 0A34F3A7? ![]() Обычно узнают класс и имя метода. Если объект - это компонент, то при сильном желании можно вытащить его свойство Name. Только в этом случае некрасиво получится. -------------------- Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. |
|||
|
||||
Avers |
|
|||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 113 Регистрация: 20.6.2008 Где: 26rus Репутация: нет Всего: нет |
я неверно выразился - мне нужно имя класса.
Т.е., если бы не было этой канители то, все было бы замечательно: по Sender можно было определить класс от которого пришло исключение, а следовательно и модуль и т.п. и во многих (пусть не всех, но во многих) случаях метод, в котором произошло исключение... (а мне больше и н надо). Но не зная класса.... все остальное становится бессмыслено. Как же тогда определить модуль, в котором произошло исключение? --------------------
Born to be wild |
|||
|
||||
CodeMonkey |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1839 Регистрация: 24.6.2008 Где: Россия, Тверь Репутация: 29 Всего: 89 |
Ну вот поскольку Sender равен тому, кто поймал сообщение, а часть сообщений для кнопки отправляются именно форме, то так вот запросто это сделать не получится. 1. В принципе, на руках есть ExceptAddr - точный адрес инструкции, возбудившей исключение. У нас на руках есть и RTTI - в частности, список имён published-методов (в том числе обработчиков событий) и их адресов. Проблема состоит в том, как определить, какому методу принадлежит ExceptAddr. Имеется ввиду, что начало каждого метода известно, а вот сколько он занимает байт в длинну - нет. Может здесь и можно что-то придумать, но мне ничего в голову не приходит. 2. Простейшее в реализации будет подключить JclDebug, включить в опциях генерацию map-файлов и вызвать функцию GetLocationInfo. Простейшее в том смысле, что уже всё готово и написано. 3. Ну и, конечно, как вариант можно рассмотреть применение грязных хаков - например, подмена функций и методов перенаправления сообщения. В частности, обернуть Control.Perform в try ... except Application.HandleException(Control); end; Тогда бы Sender указывал на того, кого вы хотите. Но, помимо того, что это очень грязно, проблема ещё и в том, что некоторые нужные нам функции (например, тот же DoControlMsg) находятся в implementation-секциях модулей и не имеют внешних ссылок. Т.е. неизвестен их адрес. Аналогично - готового решения здесь мне не видно. Добавлено через 1 минуту и 54 секунды ...хотя... с другой стороны, сам Perform ведь в интерфейсной части, можно попробовать подменить его... -------------------- Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. |
|||
|
||||
CodeMonkey |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1839 Регистрация: 24.6.2008 Где: Россия, Тверь Репутация: 29 Всего: 89 |
По способу номер 3 в последнем моём посте - если хотите, то можете попробовать такой код (для подмены стандартного класса используется метод Geo):
На первый взгляд вроде бы это работает, но, блин, серьёзно это не тестировалось. Я не рекомендую вам применять этот код как есть - это просто пример. Дело в том, что полностью ловить исключение в NewWndProc неправильно - т.к. существующий код (в том числе и VCL) рассчитывает на то, что исключение будет передаваться наружу. Поэтому вместо HandleException должно стоять запоминание экземпляра в глобальную переменную и её анализ и сброс в OnException. -------------------- Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. |
|||
|
||||
Avers |
|
|||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 113 Регистрация: 20.6.2008 Где: 26rus Репутация: нет Всего: нет |
1. Здесь идейка одна была и она заработала (по крайней мере в простом случае.
Кажется, в другой теме я выкладывал этот код:
но это для малой программы, где всего один моудль, поэтому можно уверенно искать в медоах Selfю 2. Не спорю. Смотрю как работает эврика лог - загляденье. Но столь мощый обработчик не требуется (раз). Нужен свой, бесплатный (специфика работы, можно сказать) - это два. 3.... сложно и запутанно.... (имхо). Нужен просто модуль (реализация в виде пакета bpl) подключаемый к написанной программе. Т.е. так, чобы не переписывать код. --------------------
Born to be wild |
|||
|
||||
CodeMonkey |
|
||||||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1839 Регистрация: 24.6.2008 Где: Россия, Тверь Репутация: 29 Всего: 89 |
Вообще-то это число в точности равно переменной ExceptAddr ;)
Здесь как раз проблемы нет: вы всегда можете перебрать Screen.Forms. И перебрать методы у каждой формы. А настоящая проблема с приведённым способом в том, что он может давать ложную информацию. Например, пусть у вас в модуле есть (именно в таком порядке) OnButton1Click, затем в модуле идёт какой-то код (функция) и далее расположен Button2Click. Так вот, в Button2Click вы вызываете функцию, которая расположена между Button1Click и Button2Click. Предположим, что в ней возникнет исключение. Что сделает ваш код? Код возьмёт адрес исключения (он расположен где-то между Button1Click и Button2Click) и начнёт уменьшать его, сдвигаясь к Button1Click, на каждом шаге пытаясь получить имя метода. Поскольку код между Button1Click и Button2Click не входит в RTTI (это просто какая-то функция), то сделать вам этого не удасться. Это значит, что ближайшее место, где вы остановитесь - это Button1Click. В итоге, в реальности мы вызывали Button2Click -> SomeFunc, а обработчик исключений сообщит нам, что ошибка возникла в Button1Click. Ложная информация. Добавлено через 1 минуту и 22 секунды P.S. Это в точности та проблема, что я говорил чуть ранее: известны только адреса методов, но не их размер. -------------------- Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. |
||||||
|
|||||||
Avers |
|
|||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 113 Регистрация: 20.6.2008 Где: 26rus Репутация: нет Всего: нет |
Упс)) Кста, как воспользоваться этой самой переменной?? Т.е. где она объявлена. Стыдно, но я не знаю. Я ею ни разу не пользовался. Добавлено через 2 минуты и 57 секунд Нашел)) Это сообщение отредактировал(а) Avers - 11.11.2008, 12:06 --------------------
Born to be wild |
|||
|
||||
CodeMonkey |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1839 Регистрация: 24.6.2008 Где: Россия, Тверь Репутация: 29 Всего: 89 |
JclDebug - бесплатен. P.S. Эй, это же ваш вопрос: http://forum.vingrad.ru/forum/topic-231665.html Все ссылки сами и нашли. Это сообщение отредактировал(а) CodeMonkey - 11.11.2008, 12:27 -------------------- Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. |
|||
|
||||
Avers |
|
|||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 113 Регистрация: 20.6.2008 Где: 26rus Репутация: нет Всего: нет |
Можете считать меня упрямым, но я все равно хочу написать что-нить свое))
Буду развиваться, как программист=))) --------------------
Born to be wild |
|||
|
||||
CodeMonkey |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1839 Регистрация: 24.6.2008 Где: Россия, Тверь Репутация: 29 Всего: 89 |
Ну так что, исчерпан вопрос или нет?
Вариант ответа на исходный вопрос был. -------------------- Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. |
|||
|
||||
Avers |
|
|||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 113 Регистрация: 20.6.2008 Где: 26rus Репутация: нет Всего: нет |
ответ на вопрос был.
Признаюсь, про контейнеры - не очень ясно, но суть понял - Sender в MyException - малополезный параметр (имхо). Прост хотелось бы стабильности. Однорвеменно с тем появились новые вопросы. Например: Какие есть еще способы, кроме Self.MethodName, по адресу получить имя процедуры или фнуции.... Ведь, если посмотреть View -> Debug Windows -> Modules, то обнаружиться, что там есть адреса процедур и не принадлежащих объекту.... Application.MethodName - вообще ничего не дает (ни от одного адреса) ![]() Наверняка все проще, чем кажется... --------------------
Born to be wild |
|||
|
||||
CodeMonkey |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1839 Регистрация: 24.6.2008 Где: Россия, Тверь Репутация: 29 Всего: 89 |
Этот и другие (CallStack и т.п.) инструменты используют отладочную информацию. В готовую программу отладочная информация по-умолчанию не подключается. Попасть в exe она может несколькими способами, примеры каждого есть.... эээ... в JclDebug ;) Есть только единственный верный способ это сделать - воспользоваться отладочной информацией. Ваши попытки использовать для этого MethodName (читай: RTTI) некорректны, что я показал в этом посте. Ну нет в exe-файле информации о принадлежности адреса функции. Нету. Хоть головой об стенку бейтесь, но её там нет ![]() Единственный вариант - это её туда добавить. И вот здесь уже напридумывать можно много разных способов. Добавлено через 1 минуту и 50 секунд По самой идее - проще некуда ;) по реализации - готовых стандартных решений нет, писать нужно всё самому или использовать написанное другими. -------------------- Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. |
|||
|
||||
Avers |
|
|||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 113 Регистрация: 20.6.2008 Где: 26rus Репутация: нет Всего: нет |
Биться об стенку тоже вар)) Если хорошо удариться кодинг вообще не нужен станет ![]() Создатели EurekaLog заявляют, что используют только стандартную Debug Info.... Они же получают имя процедуры.... даже если, она "висит сама по себе".... Я в тупике. --------------------
Born to be wild |
|||
|
||||
CodeMonkey |
|
||||||||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1839 Регистрация: 24.6.2008 Где: Россия, Тверь Репутация: 29 Всего: 89 |
Давайте ещё раз. Приведу кусок текста из своей статьи про исключения:
Формат DCU файлов недокументирован и меняется в (почти) каждой версии Delphi. Очевидно, что пытаться брать отладочную информацию из DCU, как это делает отладчик в Delphi - не самая лучшая идея. Окей, тогда где её берут? Ну во-первых, есть две таких опции: "TD32 Debug Info" и "Remote Debug Info" (вкладка "Linker"). Включение опции заставляет компилятор добавить в exe секцию с данными, которые содержат всю отладочную информацию для exe в формате TD32 или удалённого отладчика Delphi. Это уже можно использовать - в частности JclDebug и EurekaLog при недоступности других источников берут информацию именно из отладочной информации для TD32 - см. например TJclDebugInfoTD32.InitializeSource (есть даже целый модуль JclTD32.pas). Но обычно это является запасным вариантом, а не основным. Почему? Потому что отладочная информация в этих форматах предназначена для внешнего (Turbo Debugger 32) и удалённого отладчика соответвенно. Это значит, что её формат оптимизирован для отладки, но не для ваших целей. В частности, с практической точки зрения это означает многократное (!) увеличение размера exe-файла. Ещё варианты? Окей, вот основной вариант:
Вот его и используют все готовые решения. Текстовый файл фиксированного формата. Что может быть проще? Читаем его, ищем нужный адрес и смотрим, какой функции, какому классу он принадлежит. Разумеется, читать (парсить) файл каждый раз - долго и неудобно (да и большой он). Именно поэтому большинство готовых решений при компиляции берут этот файл, выдёргивают из него нужную информацию, сохраняют её в удобном для себя формате и подключают её к exe-файлу. Получается аналог отладочной информации в формате TD32 в exe, только формат отладочной информации будет ваш личный. Кстати, готовые решения при этом часто ещё сжатие применяют. В среднем прирост размера exe файла составляет где-то 30%. JclDebug, например, получает информацию о имени функции по заданному адресу с помощью таких классов: - TJclDebugInfoMap - получает информацию из map-файла с тем же именем, что и exe-файл. - TJclDebugInfoBinary - получает информацию из встроенной в exe секции JCLDEBUG или jdbg-файла с тем же именем, что и exe-файл. - TJclDebugInfoTD32 - получает информацию из встроенной в exe секции TD32. - TJclDebugInfoSymbols - получает информацию из встроенной в exe секции виндовой отладочной информации - аналог TD32, только решение от Microsoft. Основным вариантом является TJclDebugInfoBinary, а остальные используются как запасные. EurekaLog, во-первых, использует JclDebug для получений той же информации: - TELDebugInfoMap - получает информацию из map-файла с тем же именем, что и exe-файл. - TELDebugInfoJedy - получает информацию из встроенной в exe секции JCLDEBUG или jdbg-файла с тем же именем, что и exe-файл. - TELDebugInfoTD32 - получает информацию из встроенной в exe секции TD32. Плюс добавляет свою информацию - реализация полностью аналогична TJclDebugInfoBinary/TELDebugInfoJedy, только формат не JDBG, а EurekaLog (отдельного класса нет, получение информации лежит леликом внутри GetSourceInfoByAddr). Этот вариант является для неё основным, а остальные используются как запасные. Т.е. как я и говорил - сама идея проста до безобразия (искать строчки в текстовом файле), но чтобы её реализовать самому.... попотеть придётся. -------------------- Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. |
||||||||
|
|||||||||
Avers |
|
|||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 113 Регистрация: 20.6.2008 Где: 26rus Репутация: нет Всего: нет |
Спасибо) большое)
Вот. теперь все ясно и понятно... Как говориться, дело ясное, что дело темное ![]() Теперь ударюсь в способы работы с exe файлами)) надо подумать, как дополнить его нужной инфой, но при этом не попортить)) Добавлено через 2 минуты и 17 секунд Жаль у меня постов мало, а то бы обязательно плюсик поставил, поэому ограничусь человеческим спасибо. А за столь подробную информацию и нескольких +ов не жаль))) --------------------
Born to be wild |
|||
|
||||
CodeMonkey |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1839 Регистрация: 24.6.2008 Где: Россия, Тверь Репутация: 29 Всего: 89 |
Проще всего (и обычно так все и поступают) - добавить в exe файл пользовательский тип ресурса (RCData) с уникальным именем. Если я не ошибаюсь, то JCL добавляет секцию с именем JCLDEBUG, а EurekaLog - с ELDATA. -------------------- Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. |
|||
|
||||
CodeMonkey |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1839 Регистрация: 24.6.2008 Где: Россия, Тверь Репутация: 29 Всего: 89 |
P.S.
Кстати, в текущей версии EL есть как минимум три серьёзные проблемы (Fabio пообещался исправить в следующей версии): 1. Используется RAW-метод трассировки. Это значит, что стек вызовов может содержать ложные элементы. По-хорошему нужна возможность использовать frame-based трассировку (на выбор, разумеется). 2. В стеке вызовов не показывается запись, если для неё нет отладочной информации. Видимо, подразумевалось, что это сгладит пункт 1. В реальности это сделало некоторые логи очень тяжело читаемыми. По-хорошему нужно показывать просто адрес без доп. информации. 3. Из map-файлов не забираются записи из модулей, для которых нет отладочной информации (в этом случае в map файле нет информации о строках, но по-крайней мере там есть список функций - вот его бы нужно брать, как это делает JCL). Например, для стандартных модулей (SysUtils и т.п.), если отключена опция "Use Debug DCUs". Это приводит проблемам в совокупности с пунктом 2. Лично для себя я вписываю в EL трассировку из JCL и модифицирую парсер. Ну пока это не поправят. Это сообщение отредактировал(а) CodeMonkey - 13.11.2008, 17:01 -------------------- Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. |
|||
|
||||
![]() ![]() ![]() |
Правила форума "Delphi: Для новичков" | |
|
Запрещается! 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делиться вскрытыми компонентами
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Delphi: Для новичков | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |