Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Книга о KOL.doc
Скачиваний:
29
Добавлен:
30.04.2019
Размер:
1.77 Mб
Скачать

Приложение а. Ошибки программистов, начинающих изучать kol

Слона-то я и не приметил…

(Басня Крылова)

Мысль о необходимости этой главы возникла у меня, когда я в очередной раз, заглянув на форум, обнаружил вопрос, который уже задавался многократно, многократно был отвечен, занесен в ЧаВо и F.A.Q., но продолжает задаваться все снова и снова, и иногда даже людьми, начавшими писать на KOL не вчера. Я надеюсь, что наличие еще одного источника с ответами на такие вопросы поможет уменьшить количество подобных ошибок, оставляя место на форуме для более содержательных обсуждений. Некоторые из приведенных здесь проблем уже описывались в этой книге в соответствующих разделах. Однако, собрать их все вместе и обсудить еще подробнее, наверное, будет не лишним. Я попробую отсортировать ошибки по частоте их повторений: первыми будут те, что имеют наивысший рейтинг в перечне вопросов от разработчиков.

А.1. Назначение обработчика события, используя функцию MakeMethod и приведение типа к tOnSomeEvent. («Почему мой обработчик не реагирует на событие?»)

Обычно подобная проблема встречается у людей, даже неплохо знакомых с языком Паскаль, но не задумывающихся особенно о том, какой машинный код формируется в результате работы компилятора. / Не огорчайтесь, подавляющее большинство программистов об этом не задумываются. И правильно поступают: на то и существуют компиляторы, чтобы думать не о том, какой машинный код получится из исходного кода на Языке Высокого Уровня, а думать лишь о решаемой задаче. /

Выглядит это так. Вы пытаетесь назначить обработчику события не метод объекта, а обычную процедуру. Например, так:

procedure MyOnClick( Sender: PObj );

begin

… какой-то код …

end;

MyControl.OnEvent := TOnEvent( MakeMethod( nil, @ MyOnClick ) );

На первый взгляд, здесь все верно, и описание функции MyOnClick вполне соответствует описанию типа обработчика

TOnMessage = procedure(Sender: PObj) of object;

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

procedure MyOnClick( DummySelf, Sender: PControl );

Почему же не работал прежний вариант? Объяснение достаточно простое: на место формального параметра Sender попадал указатель объекта nil (в точном соответствии с тем, как был «создан» ваш метод функцией MakeMethod), а сам параметр Sender вообще не доходил до обработчика, оставаясь за бортом.

И если вспомнить о реализации механизма передачи параметров в Паскаль-процедурах, становится ясным и то, почему неверный вариант заголовка не обязательно приводил к гораздо более фатальным последствиям. Первые три параметра, умещающиеся в двойные машинные слова (32 бита на платформе PC, или 4 байта), передаются через регистры процессора EAX, EDX и ECX. Отсутствие в описание обработчика одного параметра приводило к тому, что в процедуре путался порядок параметров, переданных через регистры, и только. Замечу, что в случае соглашения о передаче параметров через машинный стек, как это происходит в Си, или при использовании директивы stdcall, первая же попытка обратиться к неверно объявленной функции наверняка привела бы к нарушению уровня стека и немедленному вылету приложения. Пожалуй, в таком поведении в данном случае толку было бы больше: по крайней мере, стало бы ясно незамедлительно, что на данном участке действительно что-то неверно – по сравнению с молчаливым игнорированием обработчиком своих обязанностей, в нашем случае. Впрочем, если неверно переданный параметр задействуется в обработчике, то обращение к полям объекта, на место которого попал указатель nil, так же будет немедленно обнаружено при попытке выполнить обработчик.