Основы офисного программирования и язык VBA

         

Функция EnumWindows


Окна являются одним из основных объектов операционной системы. Обычно, программист и не подозревает, сколь много таких объектов существует в процессе работы его программы. Функция EnumWindows позволяет перечислить все такие объекты. В нашем примере мы в результате работы с окнами сформируем три коллекции, - описателей окон, имен классов окон, заголовков окон. Задачей функции обратного вызова будет формирование этих коллекций путем добавления очередного элемента коллекции при каждом вызове Callback функции.

Начнем с описания функции EnumWindows в том виде, в каком оно представлено в документации Platform SDK:

BOOL EnumWindows( WNDENUMPROC lpEnumFunc, // pointer to callback function LPARAM lParam // application-defined value );

Функция EnumWindows перечисляет все окна верхнего уровня, передавая текущий описатель окна Callback функции, определенной в приложении. Функция не перечисляет дочерние окна - Child Windows . Функция выполняется, пока перечисление не будет закончено или Callback функция не вернет значение False. Ее параметры:

  1. lpEnumFunc - указатель на определенную в приложении Callback функцию.
  2. lParam - определенное приложением значение, передаваемое в Callback функцию.

Функция возвращает значение 0 в случае неуспеха и ненулевое значение при благоприятном исходе.

Описание Callback функции EnumWindowsProc, полученное из той же документации, имеет вид:

BOOL CALLBACK EnumWindowsProc( HWND hwnd, // handle to parent window LPARAM lParam // application-defined value );

Функция EnumWindowsProc является Callback функцией, определенной в приложении, используемой при вызове функций Win32 API EnumWindows или EnumDesktopWindows. Она получает при вызове описатель окна верхнего уровня. Тип WNDENUMPROC определяет указатель на эту Callback функцию. Имя EnumWindowsProc является держателем места (placeholder) и должно быть замещено именем функции, определенной в приложении.

Ее параметры:

  1. hwnd - описатель окна верхнего уровня.
  2. lParam - определенное приложением значение, данное в EnumWindows или EnumDesktopWindows.


Для продолжения перечисления функция возвращает значение True, для окончания - False.

Как видите, представленные описания функций ориентированы на язык C/C++ и нуждаются в преобразовании для их использования в программах на VB/VBA. Используя обозреватель API Viewer, можно получить оператор Declare для функции EnumWindows. Вот как выглядит заголовок этой функции после соответствующей трансляции:

Public Declare Function EnumWindows Lib "user32" _ (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long

Заметьте, появились описатели ByVal, а все типы заменились в данном случае на тип Long. Поскольку API Viewer не помощник в деле преобразования описания Callback функций, то эту работу необходимо проделать самостоятельно. В результате, описание имеет вид:

Public Function EnumWindowsProc(ByVal HandleW As Long, _ ByVal lParam As Long) As Long

Заметим, формальная трансляция не вызывает затруднений, - описатель Callback следует опустить, имя можно дать произвольное, а трансляция типов в данном случае достаточно проста. Вместе с тем, с описанием типа параметра lParam не все так просто. Ведь он должен служить для передачи произвольной информации, поэтому теоретически допускается задание любого произвольного типа для этого параметра, например, этот параметр может быть объектом. В этом случае следует быть особо внимательным, так первая наша попытка передать функции параметр, отличный от типа Long, привела к критической ошибке и прекращении работы приложения. Как я уже говорил ранее, можно применить альтернативный способ и передавать информацию, пользуясь глобальными переменными.

Перейдем теперь к описанию реализации нашего примера. В проекте тестового документа был создан модуль с именем "ОбратныйВызов". Вот текст раздела объявлений этого модуля:

Option Explicit 'Операторы Declare вызываемых функций Win32 API



Public Declare Function EnumWindows Lib "user32" _ (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long

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



Public Declare Function GetClassName Lib "user32" Alias "GetClassNameA" _ (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long

'Описание глобальных переменных, обеспечивающих связь 'с функцией обратного вызова EnumWindowsProc Public HandleCol As New Collection Public CaptCol As New Collection Public ClassNameCol As New Collection

Помимо функции EnumWindows, основной для нашего примера, но о которой уже много говорилось, в разделе приведено объявление новой, ранее не упоминавшейся Win32 API функции GetClassName. Она похожа на функцию GetWindowText и возвращает по описателю окна имя класса этого окна. Функцию GetWindowText мы объявляем повторно, соответствующий оператор Declare есть в разделе объявлений другого модуля проекта, но, заметьте, в этом есть необходимость, поскольку в других модулях эта функция использовалась с различными псевдонимами. Раздел объявлений модуля содержит объявление трех глобальных переменных - трех коллекций, с которыми будет работать Callback функция EnumWindowsProc, формируя на каждом шаге своего вызова очередной элемент каждой из коллекций. Коллекции будут содержать соответственно описатели окон, заголовки окон и имена классов. Заметьте, что все окна имеют имя класса, но не все окна имеют заголовок, так что число элементов в коллекциях будет различным в процессе работы. Приведем теперь текст Callback процедуры EnumWindowsProc:

Пример 6.7.

(html, txt)

Напомним, эта процедура вызывается автоматически в процессе работы процедуры EnumWindows. Поскольку процедура всегда возвращает значение 1, означающее успешность ее работы, то число ее вызовов определяется размером перечисляемого множества окон. Обратите внимание и на то, что в процедуре используется только первый параметр - описатель текущего окна, который передается вызываемым Win32 API функциям GetWindowText и GetClassName. Второй параметр вообще не используется, вместо этого напрямую происходит заполнение коллекций, заданных глобальными переменными.


Так обеспечивается связь с внешним миром.

Чтобы закончить пример, нам осталось привести описание процедуры GetCaptions, вызывающей EnumWindows:

Пример 6.8.

(html, txt)

Несколько комментариев к этой процедуре:

  1. Главное, на что следует обратить внимание, это на операцию "AddressOf" в момент вызова Win32 API функции EnumWindows. В результате ее выполнения создается ссылка на Callback функцию (адрес расположения функции в памяти). Заметьте, на имя передаваемой функции не накладывается ограничений, поэтому в разных вызовах могут быть разные имена, что и позволяет иметь при необходимости несколько Сallback функций.
  2. Мы передаем 0 в качестве значения второго параметра. Это своего рода заглушка, поскольку, как говорилось ранее, передача информации производится через глобальные переменные. В следующем примере мы продемонстрируем возможность передачи информации и через этот параметр.
  3. После завершения работы процедуры перечисления начинается обработка коллекций, созданных в процессе ее работы. В данном случае обработка проста и сводится к печати числа элементов коллекции и первых десяти элементов каждой из коллекций. Возможно, было бы интересно посмотреть, сколько же объектов - окон существует в момент выполнения обычного приложения. Но полная распечатка всех имен классов и заголовков заняла бы несколько страниц текста. Ведь таких объектов несколько сотен.


Давайте познакомимся с начальными элементами коллекций, содержащих описатели, заголовки и имена классов объектов - окон, существующих в момент запуска процедуры GetCaptions:

Пример 6.9.

(html, txt)



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


  • Давайте познакомимся с начальными элементами коллекций, содержащих описатели, заголовки и имена классов объектов - окон, существующих в момент запуска процедуры GetCaptions:

    Число окон = 254 Описатели окон 3735790 131912 131888 131916 65684 40370412 917748 262866 852650 852668 131844 Число окон с заголовками =76 Заголовки окон Continue Microsoft Agent Microsoft Office Shortcut Bar Menu Parent Window NetDDE Agent Edit Microsoft Visual Basic - DocOne6 [running] - [ОбратныйВызов (Code)] Ch6 - Microsoft Word Edit Properties Microsoft Office Shortcut Bar Число окон, возвращающих класс =254 Имена классов окон OfficeTooltip tooltips_class32 ComboLBox tooltips_class32 tooltips_class32 AgentAnimBalloon AgentAnim tooltips_class32 tooltips_class32 tooltips_class32 tooltips_class32

    Пример 6.9.


    Содержание раздела