Пособие-самоучитель on-line "Visual Basic с нуля"
§ 2. Окна. Поиск окна по заголовку. Координаты курсора мыши. Определение хендла окна по точке и заголовку. Структура POINTAPI.
Скачать исходник примера
Дата создания 11.04.2005 {Автор 4us}

Новые API-функции, используемые в этом параграфе.
Функция CloseWindow
Назначение Сворачивает заданное хендлом окно.
Объявление Declare Function CloseWindow Lib "user32" Alias "CloseWindow" (ByVal hwnd As Long) As Long
Аргументы hwnd (Long) - хендл сворачиваемого окна
Функция CloseWindow возвращает ненулевое значение в случае успеха операции и ноль при неудаче.
Функция DestroyWindow
Назначение Уничтожает заданное хендлом окно и все его дочерние окна. Не работает под Windows XP. Да и под Win98 тоже. И вообще, на фиг она нужна?
Объявление Declare Function DestroyWindow Lib "user32" Alias "DestroyWindow" (ByVal hwnd As Long) As Long
Аргументы hwnd (Long) - хендл уничтожаемого окна
Функция DestroyWindow возвращает ненулевое значение в случае успеха операции и ноль при неудаче.
Функция FindWindow
Назначение Находит хендл первого окно верхнего уровня по имени класса или по заголовку (тексту) окна.
Объявление Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Аргументы lpClassName (String) - строка с именем класса или ноль, если поиск происходит по заголовку (тексту) окна. В качестве нуля лучше всего использовать константу vbNullString.
lpWindowName (String) - строка с заголовком или текстом окна или ноль, если поиск окна происходит по имени класса. В качестве нуля лучше всего использовать константу vbNullString
Функция FindWindow возвращает хендл найденного окна.
Функция GetCursorPos
Назначение Передает в структуру POINTAPI текущую позицию курсора мыши в экранной системе координат.
Объявление Declare Function GetCursorPos Lib "user32" Alias "GetCursorPos" (lpPoint As POINTAPI) As Long
Аргументы lpPoint (структура) - содержит координаты X и Y в экранной системе координет в пикселях.
Объявление структуры POINTAPI:
Type POINTAPI
X As Long
Y As Long
End Type

Далее любая переменная объявляется с типом POINTAPT, например MouseCoordinat :
Public MouseCoordinat As POINTAPI 'если в модуле
Private MouseCoordinat As POINTAPI 'если в форме
Функция GetCursorPos возвращает ненулевое значение в случае успеха операции и ноль при неудаче.
Функция GetWindowText
Назначение Читает заголовок окна или текстовое содержимое элемента формы. (Свойства .Caption и .Text)
Объявление Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Аргументы hwnd (Long) - хендл окна, из котого читается текст
lpString (String) - заранее выделенная строка символов длиной не менее cch+1 символов, в которой после выполнения функции будет содержаться заголовок или текст окна.
cch (Long) - длина строки lpString
Функция GetWindowText возвращает длину строки, скопированной в lpString.
С помощью этой функции НЕЛЬЗЯ читать текст из окон других приложений!
Функция OpenIcon
Назначение Восстанавливает заданное хендлом свернутое окно и делает его активным.
Объявление Declare Function OpenIcon Lib "user32" Alias "OpenIcon" (ByVal hwnd As Long) As Long
Аргументы hwnd (Long) - хендл свернутого окна
Функция OpenIcon возвращает ненулевое значение в случае успеха операции и ноль при неудаче.
Функция WindowFromPoint
Назначение Возвращает хендл окна, содержащую точку с заданными координатами.
Объявление Declare Function WindowFromPoint Lib "user32" Alias "WindowFromPoint" (ByVal xPoint As Long, ByVal yPoint As Long) As Long
Аргументы xPoint (Long) - горизонтальная координата точки
yPoint (Long) - вертикальная координата точки
Функция WindowFromPoint возвращает хендл окна, содержащего точку.

В прошлом параграфе мы добрались до окна нашего проекта - формы, просто использовав свойство .hWnd, которое выложило нам на тарелечке хендл окна. Но что делать, если нам надо добраться до чужого окна? Давайте продолжим исследование окон.

А сейчас по пытаемся найти окно и сбить, т.е. я хотел сказать, произведем с ним какие-то действия. Для этого сделаем себе учебную цель. Создадим новый exe-проект, изменим свойство формы Form1.Caption="F-16 Fighter" и откомпилируем (то бишь создадим исполняемый файл exe). Запустим наш проект и пусть полученное окно болтается на Десктопе до поры.

Теперь создадим новый exe-проект. Для наших экспериментов нам потребуются четыре кнопки Command, один CheckBox, один таймер Timer1 и пять текстбоксов, причем для Text1 желательно включить свойства Multiline=True и ScrollBars=2 (Вертикаль).

В форме объявим две переменные, которые будут у нас использоваться для: HandleWin - хранения хендла (чего делать нельзя, я имею ввиду хранить нельзя, это мы отметим ниже особо) и KillWin - куда будут возвращать свои значения функции (хотел ее назвать KillBill, потом KillGates, но перепутал и стало лень менять. Но тоже по существу).

Option Explicit
Dim HandleWin As Long
Dim
KillWin As Long


Примечание: При работе с API использование Option Explicit уже не просто рекомендовано, а крайне необходимо и сугубо обязательно! Во всех модулях и формах.
Далее, в процедуре загрузки формы очистим текстбоксы, в для Text2 присвоим значение нашего учебного окна "F-16 Fighter". Кроме того установим интервал таймера и выключим его (он пока не нужен).
Еще примечание: Да, ты уж извини, но я не буду писать свойство .Text для текстбоксов. Пора уже отвыкать есть руками, раз уж полезли в API. Свойста, являющиеся главными для объектов понимаются по умолчанию и их явно писать не обязательно(.Text для текстбоксов, .Capture для лейблов и т. д.)

Private Sub Form_Load()
Text1 = ""
Text2 = "F-16 Fighter"
Text3 = ""
Text4 = ""
Text5 = ""
Timer1.Interval = 100
Timer1 = False
End Sub

Попробуем получить хендл окна по его (окна естественно) заголовку. Заголовок - это все то, что выводится в синенькой верхней полосочке почти в каждом окне. Для нашего окна-мишени это - Form1.Caption = "F-16 Fighter". Используем функцию FindWindow. Давай добавим в проект стандартный модуль и объявим ее (все что пишется в модуле я буду выделять коричневым цветом):

Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Как мы видим, в этой функции есть два аргумента, по которым можно найти окно: по имени класса (lpClassName) и по имени заголовка (lpWindowName). Как правило окно ищется по одному какому-нибудь аргументу. Поскольку мы не знаем имя класса искомого окна (да и вообще не знаем, что это такое), а знаем заголовок, то его и надо подставить в функцию вместо. Но и второе значение (lpClassName) нельзя пускать на самотек. Ему надо передать ноль (в API всегда надо чего-то передавать). Но аргумент этот имеет строковый тип, поэтому нужно использовать константу vbNullString. Так как заголовок окна мы храним в Text2, то использование функции будет выглядеть очень просто:
HandleWin = FindWindow(vbNullString, Text2)
В переменную HandleWin мы получим искомый хендл искомого окна. Напишем теперь процедуру:

Private Sub Command1_Click()
HandleWin = FindWindow(vbNullString, Text2)
Text1 = Text1 & "Хендл окна с именем " & Text2 & " " & HandleWin & vbCrLf
Text3 = HandleWin
End Sub


Нажимая на кнопку Command1, мы получаем хендл того окна, чей заголовок прописан в Text2. При этом в Text1 будет выводится общая информация о найденом окне.
Теперь попробуем произвести кое-какие манипуляции с окном по его хендлу: уничтожить, свернуть и развернуть. Для этого используются следующие функции
DestroyWindow - убивает окно. В Windows XP эта функция не работает. Она в общем-то и в Win98 тоже не хочет убивать окно. Ну я решил, что и черт с ней. Но пусть остается для примера.
CloseWindow - сворачивает окно
OpenIcon - восстанавливает свернутое окно
Их объявление совершенно идентично и смысл их использования сводится лишь к подстановке вместо аргумента переменной, содержащей хендл. Объявим их в модуле:

Public Declare Function DestroyWindow& Lib "user32" (ByVal hwnd As Long)
Public Declare Function CloseWindow Lib "user32" (ByVal hwnd As Long) As Long
Public Declare Function OpenIcon Lib "user32" (ByVal hwnd As Long) As Long


Обрати внимание, что первая функия объявлена с применением значка типа & вместо As Long в конце. Такое объявление ничем не отличается по смыслу и работе от двух других. Теперь пропишем каждую в свои процедуры соответствующих кнопок Command2, Command3 и Command4. Кроме того, мы еще и проанализируем результат действия каждой функции по возвращаемому значению (если ноль, то действие не удалось) и выведем соответствующее сообщения в Text1:

Private Sub Command2_Click()
KillWin = DestroyWindow&(HandleWin)
If KillWin = 0 Then
Text1 = Text1 & " Убийство окна " & HandleWin & " не удалось!" & vbCrLf
Else
Text1 = Text1 & " Мы убили окно " & HandleWin & vbCrLf
End If
End Sub


Private Sub Command3_Click()
KillWin = CloseWindow(HandleWin)
If KillWin = 0 Then
Text1 = Text1 & " Свернуть окно " & HandleWin & " не удалось!" & vbCrLf
Else
Text1 = Text1 & " Мы свернули окно " & HandleWin & vbCrLf
End If
End Sub


Private Sub Command4_Click()
KillWin = OpenIcon(HandleWin)
If KillWin = 0 Then
Text1 = Text1 & " Восстановить окно " & HandleWin & " не удалось!" & vbCrLf
Else
Text1 = Text1 & " Мы восстановили окно " & HandleWin & vbCrLf
End If
End Sub

Теперь я еще раз объясню, почему так делать нельзя. В процедуре Command1 мы нашли и сохранили в переменной HandleWin хендл окна. А используем его совершенно в другой процедуре по другому событию Command_Click. Между двумя нажатиями пользователем на кнопки может произойти черт знает что. Если ты определишь хендл , а затем закроешь и снова откроешь программку "F-16 Fighter", то ее окно будет иметь уже совершенно иной хендл и сделать с ним ничего будет нельзя, до тех пор, пока не определишь его новый хендл. Поэтому действия с окнами должны производится сразу же после определения хендла. Но я нарочно так сделал, чтобы для простоты разделить функции по процедурам и иметь показательный отрицательный пример под рукой.

Далее мы сделаем так, чтобы указав на окно курсором мыши мы могли посмотреть (не получить для использования, а именно посмотреть), какой у окна хендл и какой у него заголовок. Реализуется это с помощью функции WindowFromPoint, которая возвращает хендл окна по точке на этом окне. Точкой этой будут координаты курсора мыши. Получить их можно с помощью функции GetCursorPos. Вот с нее и начнем. Точнее начнем с процедуры, которая будет включать весь этот наш механизм. И процедура эта будет для Check1. Он лучше кнопки, так как имеет два положения включено и выключено. Именно здесь мы будем включать и выключать таймер. Зачем он нам нужен? А нужен он для того, чтобы не пытаться найти событие, которое происходит при наведении мыши на окно. Так как в нашем проекте его и не происходит. А таймер исправно , через каждые 100 миллисекунд будет нам включать наш механизм определения хендла окна, на котором находится курсор мыши.
Итак начнем. Короткая процедура для включения-выключения таймера:

Private Sub Check1_Click()
If Check1 = 0 Then Timer1 = False
If Check1 = 1 Then Timer1 = True
End Sub


И теперь самое интересное - процедура самого таймера.
Private Sub Timer1_Timer()
'объявим тройку нужных нам переменных
Dim PosCur As Long
Dim
DlinaTexta As Long
Dim
WindowCaption As String


В модуле объявим функцию для определения координат мыши:

Public Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long

И опааа, видим всего один аргумент, в то время координат должно быть две. А дело в том, что lpPoint представляет структуру POINTAPI, которая содержит как раз эти две координаты. Структура - это по-сути тот же пользовательский тип данных, который мы разбирали в Главе 17 первой части самоучителя, только задан жестко и никаких здесь вольностей быть не должно. Поэтому в модуле объявляем тип POINTAPI так как это требуется для API-функций:
Type POINTAPI
X As Long
Y As Long
End Type

и затем объявляем какую-нибудь переменную с типом POINTAPI, например MouseCoordinat:
Public MouseCoordinat As POINTAPI
Вот теперь, возвращаемся к нашей процедуре таймера в форме и получаем, наконец, координаты мыши:

PosCur = GetCursorPos(MouseCoordinat)
'записываем координаты в текстбоксы для большей наглядности
Text4 = MouseCoordinat.X
Text5 = MouseCoordinat.Y


Теперь нам надо по этим координатам получить хендл окна. Объявим в модуле функцию:

Public Declare Function WindowFromPoint Lib "user32" (ByVal xPoint As Long, ByVal yPoint As Long) As Long

Ну здесь все просто, как апельсин. Два аргумента - две координаты, что еще нужно джигиту, чтобы встретить старость. Пишем в нашей процедуре:

HandleWin = WindowFromPoint(MouseCoordinat.X, MouseCoordinat.Y)
Text3 = HandleWin


Теперь у нас в переменной HandleWin есть хендл и еще по нему мы можем узнать заголовок экрана с помощью функции GetWindowText. Объявим ее в модуле:

Public Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long

Ну с первым аргументом (hwnd) - хендлом понятно, мы его имеем. Второй аргумент (lpString), это как раз то, что мы хотим получить, т.е. заголовок , а третий (cch)- длина этого самого заголовка. Поскольку мы этой длины не знаем, то ставим по-максимуму -255 (вряд ли заголовок будет более 256 символов). А вот что касается второго аргумента, тут дело несколько сложнее. Если мы подставим просто нашу переменную WindowCaption, которую мы и объявили для заголовка окна
DlinaTexta = GetWindowText(HandleWin, WindowCaption, 255)
то это результатов не даст. Дело в том, что строковые аргументы API-функций требуют, чтобы для них был в памяти зарезервирован буфер размером не меньше строки, которую мы хотим получить. А для этого надо передать в функцию строку пробелов, соответствующую размеру буфера. Короче, смысл в том , что мы присваиваем переменной строку пробелов, а вместо нее получаем строку данных. И если строка пробелов длинее , например "____________", чем строка данных "123", то мы получим "123_____________", т.е. данные плюс лишние пробелы, а если строка пробелов короче, то данные усекутся по длине строки пробелов. Исходя из этих мудреных рассуждений возвращаемся опять к нашей процедуре и резервируем буфер (на самом деле просто присваиваем переменной WindowCaption строку пробелов по-длинее

WindowCaption = Space(256)

и теперь используем функцию по-правильному

DlinaTexta = GetWindowText(HandleWin, WindowCaption, 255)

Теперь, раз у нас есть длина заголовка (это как раз то, что возвратила функция), мы можем отсечь ненужные нам пробелы и вывести даныые в текстбокс

Text2 = Left(WindowCaption, DlinaTexta)
End Sub


На этом пока все. В это параграфе мы разобрали технику использования API. Теперь ты можещь, двигая мышью узнавать хендл любых окон и их заголовки (если есть). Причем на собственной форме ты увидишь содержимое текстов, названия кнопок и т.п. Исходник примера можно скачать вверху страницы.

Copyright © 2005 4us




Сайт создан в системе uCoz