Прототип к варианту 8 - Просмотр информации об окнах, страница 2

Функция MenuOwned_Click() демонстрирует другой способ перечисления окон, в данном случае — всех собственных окон заданного окна. Функция EnumWindows относится к особому семейству функций, называемых «функциями перечисления» (enumeration functions). При вызове этим функциям передается адрес функции вашего приложения. Затем Windows вызывает функцию перечисления для каждого перечисляемого объекта. В случае EnumWindows Windows вызовет заданную вами функцию для каждого окна верхнего уровня в системе. В функции перечисления мы просто проверяем, есть ли у окна родитель, и если есть — совпадает ли он с искомым окном. EnumWindows позволяет передать параметр функции перечисления. В нашем примере передается манипулятор выделенного окна.

Однако при этом возникает одна хитрость: в Visual Basic 5.0 функции перечисления должны располагаться в стандартном модуле. Следовательно, событие Callbackl_EnumWindows помещается

в модуль WINVIEW1.BAS. При определении типов параметров и возвращаемого значения функции перечисления необходимо быть очень внимательным. Любая ошибка может привести к исключениям и аварийному завершению приложения (и даже зависанию системы в Windows 95).

В Visual Basic 4.0 перечисление поддерживалось при помощи нестандартного элемента DWCBK32D.OCX, прилагаемого к книге. Этот элемент обеспечивает общую поддержку механизма косвенного вызова; он создает пул адресов функций, которые могут использоваться для перечисления. Когда Windows вызывает функцию, элемент DWCBK32D.OCX инициирует событие Visual Basic.

Команда Point захватывает мышь. Это означает, что весь ввод с мыши передается конкретному окну независимо от того, над каким окном находится курсор — при условии, что это окно принадлежит вашему приложению (в Win 16 можно было захватить ввод с мыши во всех приложениях системы). Оба режима наведения, описанных выше, реализуются весьма прямолинейно. Перед вами один из тех случаев, когда процесс переноса прост с точки зрения синтаксиса, но сложен из-за изменений в архитектуре Win32. Кроме присоединения ввода всех приложений в системе к Winview (что делать не рекомендуется), есть лишь один способ воспроизвести прежние функции приложения в Win 16 — перехват щелчков мыши на уровне системы. В Visual Basic для этого существуют специальные элементы, созданные независимыми фирмами.

Координаты X, Y передаются событию MouseMove в клиентской системе координат на основании свойства ScaleMode, в котором для данной формы выбраны пикселы. Поскольку форма захватила мышь (см. описание события MenuPointed_Click), она будет получать события MouseMove независимо от позиции курсора внутри приложения или на экране (в зависимости от того, какой режим наведения используется). Клиентские координаты преобразуются в экранные функцией ClientToScreen; затем функция WindowFromPoint определяет окно, находящееся в этой позиции. Чтобы лучше понять происходящее, можно создать на форме дополнительную надпись и выводить в ней значения клиентских и экранных координат при перемещении мыши.

Получение имени элемента для окна уже не является таким простым процессом, как в 16-разрядном Visual Basic. Специальный API Visual Basic, позволявший легко определить имя элемента по манипулятору окна, теперь недоступен. В функции GetControlNameFromWindow продемонстрирован еще один способ определения имени окна по манипулятору. Остальные информационные функции работают просто. Для извлечения данных стиля применяется оператор AND; за дополнительными сведениями о поразрядных операторах обращайтесь к разделу «Флага и битовые поля» главы 3.

В функции GetWindowDesc$ мы снова сталкиваемся с тем, как изменения архитектуры Win32 влияют на функциональность приложения. Раньше для любого окна можно было получить манипулятор экземпляра приложения при помощи функции GetWindowWord, после чего воспользоваться функцией GetModuleFileName для получения имени исполняемого файла, соответствующего этому экземпляру. В отличие от Win 16 в Win32 манипуляторы экземпляров не являются однозначными идентификаторами приложений в системе. Они действительны лишь в контексте своих приложений. Это означает, что мы можем определить имена модулей только для окон исполняемого файла приложения, а также библиотек динамической компоновки и нестандартных элементов, используемых текущим приложением. Функция GetWindowThreadProcessId используется для получения уникального идентификатора процесса, владеющего окном. Имя файла модуля определяется лишь в тех случаях, когда этот идентификатор совпадает с идентификатором текущего процесса.

Когда функция API записывает строку в буфер (как, например, функции GetClassName и GetModuleFileName), она просто копирует данные и завершает их нуль-символом, обозначающим конец строки. Функция не изменяет длину строки, о которой знает Visual Basic, Чтобы строка VB имела правильную длину, можно воспользоваться функцией InStr для определения позиции завершающего нуль-символа. В библиотеке динамической компоновки также имеется функция agGetStringFromLPSTR$, решающая ту же задачу.

Если внимательно присмотреться к исходной 16-разрядной версии приложения Winview, можно заметить, что в ней для извлечения манипулятора окна из строки описания просто использовалась функция Val. Встретив пробел или символ табуляции, функция Val прекращала обработку строки, поэтому такое решение работало. Впрочем, это оказалось ошибкой Visual Basic — по спецификации функция должна была пропускать пробелы и символы табуляции. В Visual Basic 4.0 ошибка была исправлена, что нарушило работу исходной программы. Например, строка «&HD04 Foreign window» раньше интерпретировалась как число &HD04. В VB4 символ табуляции игнорировался, в результате чего та же строка интерпретировалась как Val(&HD04F), что с учетом знака расширялось в 32-разрядную величину &HFFFD04F. Проблема решается явным извлечением шестнадцатеричной величины в следующем фрагменте: