Тверской государственный университет
Опубликован: 13.09.2006 | Доступ: свободный | Студентов: 3491 / 369 | Оценка: 4.65 / 4.29 | Длительность: 30:37:00
Специальности: Программист, Менеджер
Лекция 7:

WinApi

Классы как обертка вызовов функций Win32 API

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

Каждый из наших примеров работы с функциями Win32 API достоин лучшей упаковки и представления его в виде класса. Мы ограничимся одним последним примером и представим в виде класса работу с таймером. Пожалуй, этот пример наиболее интересен, поскольку он представляет особый случай из-за наличия Callback функции, которая, конечно же, не может быть стандартизована и должна быть задана конечным пользователем, в интересах которого и строится класс.

Построение класса "ВашТаймер"

Поскольку мы уже хорошо знакомы с тем, как строятся классы и как работать с функциями Win32 API, то нам осталось рассказать, как объединить эти две вещи в единое целое. Никаких особых деталей здесь нет. Общая стратегия такова:

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

Применим эту общую схему для создания класса ВашТаймер. Начнем с проектирования его интерфейса. Естественно, целью класса является предоставление пользователю возможности создавать таймер, посылающий сообщения с заданным интервалом, и удалять его, когда необходимость в нем исчезнет. Пользователь не должен ничего знать о функциях Win32 API, об операторах Declare, преобразовании типов. Кажется естественным с этих позиций в интерфейс класса включить два метода: " СоздатьТаймер " и " УдалитьТаймер " и свойство " ИнтервалТаймера ", доступное для чтения и записи. Методы не имеют параметров, что облегчает работу с ними. Чтобы созданный таймер посылал сообщения с заданным интервалом, необходимо предварительно установить подходящее значение свойства, но можно этого и не делать, - в этом случае будет использоваться значение по умолчанию.

Рассматриваемый нами случай упаковки функций Win32 API особый, поскольку одна из этих функций требует вызова Callback функции. Заметьте, функции обратного вызова не должны принадлежать упаковке, - нашему классу. Они не являются стандартными средствами, это функции, создаваемые пользователем. Поэтому они должны находиться вне модуля класса, - в стандартном классе, созданном пользователем, там, где он будет создавать и объекты класса ВашТаймер. Чтобы не возникала соблазна поместить в класс функцию обратного вызова, такая возможность исключается синтаксически. Обратите, однако, внимание, что созданный класс предъявляет определенные требования к заголовку функции обратного вызова и даже диктует ее имя. Но обо всем по порядку и давайте вначале рассмотрим описание класса ВашТаймер:

Option Explicit
'Класс ВашТаймер служит упаковкой функций WIN32 API работы с таймером
'Интерфейс класса будут составлять две функции:
'СоздатьТаймер, УдалитьТаймер и свойство ИнтервалТаймера

'При работе с классом необходимо описать Callback функцию по следующему образцу:

'Public Sub TimerProc(ByVal HandleW As Long, ByVal msg As Long, _
'			ByVal idEvent As Long, ByVal TimeSys As Long)
'		'Функция обратного вызова. Вызывается при обработке сообщения WM_Timer,
'		'посылаемого таймером, созданным процедурой SetTimer
'
'	'Поместите здесь свой код!
'
'End Sub

'Функции Win32 API для работы с таймером
Private Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, _
		ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long

Private Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long

'Свойства: Интервал - хранит значение интервала посылки сообщений
Private Интервал As Long
'Идентификатор таймера
Private IdEv As Long


Public Sub СоздатьТаймер()
 'Создает таймер, вызывая Win32 Api функцию SetTimer
		IdEv = SetTimer(0&, 0&, Интервал, AddressOf TimerProc)
		If IdEv = 0 Then
			MsgBox ("Не удалось создать таймер!")
		Else
			Debug.Print "Создан Таймер: Идентификатор = ", IdEv
		End If
 
End Sub

Public Sub УдалитьТаймер()
'Удаляет таймер
		If IdEv > 0 Then
			Call KillTimer(0&, IdEv)
			Debug.Print "Удален Таймер: Идентификатор = ", IdEv
			IdEv = 0
		End If
End Sub

Public Property Get ИнтервалТаймера() As Long
		ИнтервалТаймера = Интервал
End Property

Public Property Let ИнтервалТаймера(ByVal NewValue As Long)
		Интервал = NewValue
End Property

Private Sub Class_Initialize()
		Интервал = 1000
End Sub

Private Sub Class_Terminate()
		УдалитьТаймер
End Sub
6.12.

Некоторые комментарии к этому тексту:

  1. О проектировании интерфейса класса мы уже говорили. Его составляют два метода, не имеющие параметров, - СоздатьТаймер и УдалитьТаймер, а также процедуры - свойства Property Get и Property Let ИнтервалТаймера, позволяющие взаимодействовать с закрытым свойством Интервал.
  2. Закрытых свойств и методов больше. Закрытыми являются операторы Declare, описывающие функции Win32 API SetTimer и KillTimer, уже упомянутое свойство Интервал и свойство IdEv, хранящее идентификатор таймера, о котором конечный пользователь может и не знать.
  3. Закрытыми являются конструктор класса по умолчанию и деструктор: Class_Initialize и Class_Terminate. В конструкторе инициализируется свойство Таймер, значение которого устанавливается по умолчанию, равным одной секунде. В деструкторе класса таймер уничтожается, если он не был удален до этого.
  4. При создании таймера устанавливается ссылка на функцию обратного вызова с именем TimerProc. Процедура с таким именем и уже упоминавшимися требованиями к ее заголовку должна быть описана в стандартном модуле класса, созданным конечным пользователем.
  5. В описание класса в качестве комментария вставлена заготовка функции обратного вызова, чтобы облегчить создание этой функции конечному пользователю.
Использование класса ВашТаймер

Рассмотрим, как работать с классом ВашТаймер. Мы не стали изобретать ничего нового, - в тестовый документ добавили две кнопки Start1 и Finish1, которые работают также как и их тезки Start и Finish, но вызывают для этого методы и свойства объекта MyTimer класса ВашТаймер. В модуле, где объявлен соответствующий объект, находится и процедура обратного вызова TimerProc. Вот соответствующий текст этого модуля:

Option Explicit
'Модуль Таймер1
'Глобальная информация
Public Counter As Long	'Счетчик числа вызовов Callback функции
Public MyTimer As New ВашТаймер

Public Sub Start1()
		MyTimer.ИнтервалТаймера = 5000
		MyTimer.СоздатьТаймер
End Sub

Public Sub Finish1()
		MyTimer.УдалитьТаймер
End Sub

Public Sub TimerProc(ByVal HandleW As Long, ByVal msg As Long, _
			ByVal idEvent As Long, ByVal TimeSys As Long)
		'Функция обратного вызова. Вызывается при обработке сообщения WM_Timer,
		'посылаемого таймером, созданным процедурой SetTimer
		
		Counter = Counter + 1
		Debug.Print "Hi", Counter

End Sub
6.13.

Комментируя этот текст, следует заметить, что введение обертывающего класса облегчает работу с таймером. Единственной проблемой остается достаточно сложное и возможно непонятное конечному пользователю описание заголовка Callback функции TimerProc. Чтобы облегчить ее решение, можно, как это сделано в нашем примере, заготовку этой функции включить в описание класса в качестве комментария.

В заключение приведем результаты эксперимента с нажатием кнопок Start1 и Finish1:

Создан Таймер: Идентификатор =31711 
Hi					1 
Hi					2 
Hi					3 
Удален Таймер: Идентификатор =31711 
Создан Таймер: Идентификатор =31704 
Hi					4 
Hi					5 
Hi					6 
Hi					7 
Удален Таймер: Идентификатор =31704

На этом мы закончим рассмотрение темы работы с функциями Win32 API.

полина есенкова
полина есенкова
Дмитрий Вологжин
Дмитрий Вологжин
Добрый день, прошел тесты с 1 по 9, 10 не сдал, стал читать лекцию и всё пройденные тесты с 1 по 9 сбросились, когда захотел пересдать 10 тест.