- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
О чём не пишут в книгах по Delphi - А. Григорьев
Шрифт:
Интервал:
Закладка:
Работа функции начинается с проверки того, что родительским окном для данного окна действительно является то окно, чей дескриптор связан с узлом родительского окна. Эта проверка нужна потому, что функция EnumChildWindows перечисляет не только дочерние, но и "внучатые", "правнучатые" и т.д. окна. Нам здесь это не нужно, на каждом шаге нас интересуют только непосредственные "дети" окна, а до "внуков" мы доберемся, когда вызовем EnumChildWindows для дочерних окон, поэтому и отсеиваем лишнее.
Следующий шаг — получение заготовка окна. Для этого мы используем сообщение WM_GETTEXT (разница между этим сообщением и функцией GetWindowText обсуждается в разд. 1.3.1). Буфером является переменная Text типа string. Сначала с помощью сообщения WM_GETTEXTLENGTH мы узнаем длину заголовка окна, а затем выделяем под строку Text требуемое количество памяти с помощью SetLength. После этого можно получить строку с помощью сообщения WM_GETTEXT. Второй параметр этого сообщения — адрес буфера, в который будет помещена строка. Так как переменная типа string и есть указатель на буфер строки (это детально обсуждается в разд. 3.3), достаточно просто привести переменную Text к типу LParam и передать получившееся значение.
ПримечаниеСтрого говоря, у нас здесь нигде нет параметра типа LPTSTR, однако при работе с параметрами этого типа можно действовать точно так же: выделить для строки типа string нужное количество памяти и передать эту переменную, приведенную к типу LPTSTR, в качестве параметра.
Далее получаем название класса окна. Для этого мы используем статический массив ClassName, т.е. размер буфера определяется на этапе компиляции. С одной стороны, это неправильно, потому что ограничений на длину имени класса не существует (по крайней мере, в документации они не упомянуты), а мы уже говорили, что такой метод следует применять только тогда, когда существует известное на этапе компиляции ограничение длины. По с другой стороны, когда речь идет об имени класса, не существует ничего подобного сообщению WM_SETTEXTLENGTH, т.е. API не дает возможности получить длину имени класса, что делает бессмысленными все манипуляции с размером буфера во время работы программы. Поэтому мы определяем размер буфера еще на этапе компиляции, исходя из того, что слишком уж длинные имена классов встречаются редко. При вызове функции с параметром типа LPTSTR можно просто передавать массив без приведения типа, т.к. LPTSTR — это PChar, а массивы символов Char, индексирующиеся с нуля, компилятор полагает совместимыми с этим типом и все необходимые преобразования делает неявно.
И, хотя мы и взяли размер буфера с хорошим запасом, нельзя исключать ситуации, когда имя класса окажется длиннее, чем буфер. Ничего страшного при этом не произойдет, т.к. мы передаем в функцию размер буфера специально для того, чтобы она не пыталась что-то записать за пределами буфера. Но в этом случае завершающий строку символ #0 не попадет в буфер, и при попытке дальше работать с этой строкой какая-нибудь другая функция может, не найдя конца строки в пределах буфера, попытаться поискать этот конец за его пределами, что приведет к непредсказуемым результатам. Поэтому на всякий случай записываем #0 в последний символ буфера. Если имя класса оказалось длиннее буфера, это обрежет строку по границе буфера, а если короче, то это ничему не повредит, т.к. признак конца строки будет в буфере где-то раньше, а все символы после него все равно игнорируются. После этого остается только создать новый элемент в дереве, а чтобы заполнить его дочерние элементы — вызвать EnumChildWindows для получения списка дочерних окон. Так как в EnumChildWindows передается та же функция обратного вызова, получается рекурсия, которая останавливается тогда, когда функция доходит до окна, не имеющего дочерних окон. Ранее мы говорили, что программа EnumWnd демонстрирует три метода получения строки через параметр типа LPTSTR, но пока мы увидели только два (действительно, трудно показать три различных метода на примере получения двух строк). Чтобы показать третий вариант — организацию буфера через строки типа PChar — перепишем функцию EnumWindowsProc (листинг 1.23). В исходном коде программы EnumWnd этот вариант присутствует в виде комментария. Можно убрать этот комментарий, а закомментировать, наоборот, первый вариант, чтобы попробовать, как работает получение строки с помощью PChar.
Листинг 1.23. Функция обратного вызова EnumWindowsProc (второй вариант)// Ниже приведен другой вариант функции
// EnumWindowsРrос, который отличается от предыдущего тем,
// что буфер для получения заголовка окна организуется
// вручную с помощью переменной типа PChar, а не string. По
// своим функциональным возможностям оба варианта равноценны.
function EnumWindowsProc(Wnd: HWND; ParentNode: TTreeNode): Bool; stdcall;
const
ClassNameLen = 512;
var
TextLen: Integer;
Text: PChar;
ClassName: array[0..ClassNameLen - 1] of Char;
Node: TTreeNode;
NodeName: string;
begin
Result := True;
if Assigned(ParentNode) and (GetParent(Wnd) <> HWND(ParentNode.Data)) then Exit;
// Здесь, в отличие от предыдущего варианта к длине,
// получаемой через WM_GETTEXTLENGTH, добавляется
// единица, потому что нужно вручную учесть добавочный
// байт для завершающего нуля.
TextLen := SendMessage(Wnd, WM_GETTEXTLENGTH, 0, 0) + 1;
// Выделяем требуемое количество памяти. Так как
// компилятор не освободит эту памяти автоматически,
// необходимо использовать блок try/finally, иначе будут
// утечки памяти при исключениях.
Text := StrAlloc(TextLen);
try
// Так как для буфера даже при пустом заголовке будет
// выделен хотя бы один байт, здесь можно отправлять
// WM_GETTEXT, не проверяя длину строки, как это было
// в предыдущем варианте - буфер всегда будет
// корректным.
SendMessage(Wnd, WM_GETTEXT, TextLen, LParam(Text));
// Обрезаем слишком длинною строку. Модифицировать
// PChar сложнее, чем string. Вставка нуля в середину
// строки приводит к тому, что все API-функции будут
// игнорировать "хвост", но на работу StrDispose это не
// повлияет, т.к. функция StrAlloc (а также прочие
// функции выделения памяти для нуль-терминированных
// строк модуля SysUtils) сохраняет размер выделенной
// памяти рядом с самой строкой, и StrDispose
// ориентируется именно на этот размер, а не на
// завершающий ноль.
if TextLen > 104 then
begin
(Text + 104)^ := #0;
(Text + 103)^ := '.';
(Text + 102)^ := '.';
(Text + 101)^ := '.';
(Text + 100)^ := ' ';
end;
GetClassName(Wnd, ClassName, ClassNameLen);
if Text^ = #0 then NodeName := 'Без названия (' + ClassName + ') '
else NodeName := Text + ' (' + ClassName + ');
Node := FormWindows.TreeWindows.Items.AddChild(ParentNode, NodeName);
Node.Data := Pointer(Wnd);
EnumChildWindows(Wnd, @EnumWindowsProc, LParam(Node));
finally
// Вручную освобождаем память, выделенную для буфера
StrDispose(Text);
end;
end;
Второй вариант функции EnumWindowsProc отличается от первого только тем что для организации буфера для получения имени окна вместо переменной типа string используется переменная типа PChar. Соответственно, все манипуляции с динамической памятью теперь выполняются вручную, а просто отсечь конец слишком длинной строки и прибавить к результату другую строку (многоточие) мы не можем, приходится модифицировать строку посимвольно. Тем не менее видно, что и с помощью типа PChar задача создания буфера для строки, возвращаемой API-функцией, достаточно легко решается.
1.2.2. Пример Line
Пример Line представляет собой невизуальный компонент TLine, который перехватывает оконные сообщения своего владельца (владельца в терминах VCL, разумеется, раз речь идет о неоконном компоненте). Компонент TLine рисует на своем владельце линию из точки (StartX, StartY) в точку (EndX, EndY) цветом Color. Пользователь может перемещать концы линии мышью. Достаточно разместить компонент TLine на форме, и на ней появится линия, которую пользователь может перемещать как во время проектирования формы, так и во время выполнения программы. Можно также разместить на форме, например, панель, и сделать ее владельцем компонента TLine — тогда линия будет рисоваться на панели. Но это можно сделать только во время исполнения программы, потому что владельцем всех компонентов, созданных во время проектирования формы, становится сама форма. Чтобы установить компонент, нужно выполнить следующие действия:

