В ходе разработки одного из плагинов для ЛОЦМАН:PLM я столкнулся с ошибкой «Access violation at address...» при выгрузке плагина. Ошибка возникала не каждый раз, а только иногда, и приводила к краху всего приложения.
При запуске под отладчиком, в качестве адреса ошибки показывалась пустая область памяти, не занятая ни кодом, ни данными. Судя по всему, что-то портило память. Но с другой стороны никакого сложного кода в плагине не было, использовались давно проверенные и отлаженные решения.
После некоторого времени размышлений, я обратил внимание на адрес загрузки плагина
$03500000
— он был немного меньше адреса возникновения ошибки $035AB036
. Стало ясно, что после выгрузки DLL плагина остался выполняться поток, а так как код DLL уже выгружен, то возникает ошибка доступа к памяти. Добившись повторной загрузки плагина по адресу $03500000
, я посмотрел в отладчике, что же находится по адресу $035AB036
. В нормально режиме там находился код функции HintMouseThread
из модуля Forms.pas
.
К сожалению, на тот момент, времени дальше разбираться с ошибкой не было, и проблема была решена максимально просто:
Application.ShowHint := False
.
Второй подход
При эксплуатации приложения выяснилось, что обходится без подсказок в принципе можно, но с подсказками было бы гораздо лучше. Все-таки всплывающие подсказки были придуманы не зря, и часто помогают пользователям приложения. Поэтому было решено вернуться к проблеме и найти какой-нибудь способ ее решения.
Баг, судя по всему, был либо в VCL, либо в компонентах Virtual Trees, так как с данной ошибкой я столкнулся только при работе с Virtual Trees. При работе с другими компонентами эта ошибка не проявляется. Поэтому я попутно отправил Issue 229 в Issue Tracker компонентов Virtual Trees, с вопросом, не могут ли они как-нибудь самостоятельно победить этот баг. Но там никто не откликнулся.
Решено было посмотреть, что по этому поводу пишут в Интернете. После недолгих поисков нашелся в Quality Central баг QC85705. Там был описан как раз мой случай — выгрузка DLL и оставшийся поток HintMouseThread. В качестве решения предлагалось исправить код TApplication.ActivateHint, но я решил поступить проще — написать маленькую функцию, которая вызовет
UnhookHintHooks
. Править VCL это все-таки крайний случай, и если без этого можно обойтись, то лучше поступить именно так. Решением стал такой модуль, включаемый в DLL:
unit VCLPatch; interface implementation uses Classes, Controls, Forms; type TApplicationHelper = class helper for TApplication procedure _CancelHint; end; procedure TApplicationHelper._CancelHint; begin Self.FHintControl := TControl(1); Self.CancelHint(); end; initialization finalization Application._CancelHint(); end.
Код использует Class Helpers и будет компилироваться только в новых версиях Delphi (начиная с версии 2009).
Комментариев нет:
Отправить комментарий