Чтение онлайн

на главную - закладки

Жанры

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:

 // запущенная из Delphi, вылетает в отладчик (при

 // запуске вне среды Delphi проблем не замечено). Чтобы

 // этого не происходило, слишком длинные строки

 // обрезаются.

 if TextLen > 100 then

Text := Copy(Text, 1, 100) + '...';

 GetClassName(Wnd, ClassName, ClassNameLen);

 ClassName[ClassNameLen - 1] := #0;

 if Text = '' then NodeName := 'Без названия (' + ClassName + ') '

 else NodeName := Text + ' (' + ClassName + ')';

 Node := FormWindows.TreeWindows.Items.AddChild(ParentNode, NodeName);

 //
Записываем в данные узла дескриптор соответствующего

 // ему окна, чтобы иметь возможность отбросить непрямые

 // потомки.

 Node.Data := Pointer(Wnd);

 // Вызываем EnumChildWindows, передавая функцию

 // EnumWindowsProc в качестве параметра, а указатель на

 // созданный узел - в качестве параметра этой функции.

 // При этом EnumWindowsProc будет вызываться из

 // EnumChildWindows, т.е. получается рекурсия.

 EnumChildWindows(Wnd, @EnumWindowsProc, LParam(Mode));

end;

Как мы помним, первый параметр функции обратного вызова для

EnumWindows
содержит дескриптор найденного окна, а второй параметр может быть произвольным 4-байтным значением, которое система игнорирует, просто копируя сюда то значение, которое было передано при вызове
EnumWindows
или
EnumChildWindows
. Мы задействуем этот параметр для передачи ссылки на узел дерева, соответствующий родительскому окну. Также договоримся, что в свойство Data каждого узла будем записывать дескриптор связанного с ним окна. Для окон верхнего уровня ссылка будет иметь значение
nil
— это обеспечивается тем, что при вызове EnumWindows второй параметр равен нулю (см. листинг 1.21).

Работа функции начинается с проверки того, что родительским окном для данного окна действительно является то окно, чей дескриптор связан с узлом родительского окна. Эта проверка нужна потому, что функция

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, иначе будут

 // утечки памяти при исключениях.

Поделиться:
Популярные книги

Черный дембель. Часть 5

Федин Андрей Анатольевич
5. Черный дембель
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Черный дембель. Часть 5

30 сребреников

Распопов Дмитрий Викторович
1. 30 сребреников
Фантастика:
попаданцы
альтернативная история
фэнтези
фантастика: прочее
5.00
рейтинг книги
30 сребреников

Жребий некроманта 2

Решетов Евгений Валерьевич
2. Жребий некроманта
Фантастика:
боевая фантастика
6.87
рейтинг книги
Жребий некроманта 2

Охота на разведенку

Зайцева Мария
Любовные романы:
современные любовные романы
эро литература
6.76
рейтинг книги
Охота на разведенку

Чужбина

Седой Василий
2. Дворянская кровь
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Чужбина

Возвышение Меркурия. Книга 4

Кронос Александр
4. Меркурий
Фантастика:
героическая фантастика
боевая фантастика
попаданцы
5.00
рейтинг книги
Возвышение Меркурия. Книга 4

Надуй щеки! Том 3

Вишневский Сергей Викторович
3. Чеболь за партой
Фантастика:
попаданцы
дорама
5.00
рейтинг книги
Надуй щеки! Том 3

Идеальный мир для Лекаря 16

Сапфир Олег
16. Лекарь
Фантастика:
боевая фантастика
юмористическая фантастика
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 16

По воле короля

Леви Кира
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
По воле короля

Он тебя не любит(?)

Тоцка Тала
Любовные романы:
современные любовные романы
7.46
рейтинг книги
Он тебя не любит(?)

Курсант: назад в СССР 9

Дамиров Рафаэль
9. Курсант
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Курсант: назад в СССР 9

Штуцер и тесак

Дроздов Анатолий Федорович
1. Штуцер и тесак
Фантастика:
боевая фантастика
альтернативная история
8.78
рейтинг книги
Штуцер и тесак

Камень Книга седьмая

Минин Станислав
7. Камень
Фантастика:
фэнтези
боевая фантастика
6.22
рейтинг книги
Камень Книга седьмая

Хозяйка дома в «Гиблых Пределах»

Нова Юлия
Любовные романы:
любовно-фантастические романы
5.75
рейтинг книги
Хозяйка дома в «Гиблых Пределах»