Использование DLL и Windows API
П Библиотеки динамической компоновки (DLL)
О Правила вызова DLL
П Программа API Viewer
О Использование функций API в приложениях
О Создание класса WavePlayer
О Работа с системной панелью Visual Basic позволяет наделить приложение широкими возможностями при минимальных затратах времени на программирование. И все же в некоторых ситуациях стандартных средств Visual Basic оказывается недостаточно. К счастью, Microsoft потратила немало усилий на то, чтобы встроить широкие функциональные возможности в операционную систему Windows. Немного творческого подхода при программировании — и вы сможете укротить Windows API и дать своим приложениям то, чего им не хватает!
Общие сведения о библиотеках динамической компоновки (DLL)
Рано или поздно вам потребуется сделать нечто, выходящее за пределы возможностей Visual Basic — или, по крайней мере, требующее очень сложного кода. В такой ситуации у вас есть три выхода:
О написать нужный фрагмент на другом языке (например, С) и вызвать его из Visual Basic;
О управлять работой другого приложения средствами OLE Automation;
О воспользоваться готовыми библиотеками программ, образующими интерфейс Windows API.
ПРИМЕЧАНИЕWindows API представляет собой набор DLL-библиотек с тысячами подпрограмм, которые программист может вызывать из своих приложений. Эти библиотеки являются важной частью операционной системы Windows.
В этом уроке мы обсудим последний вариант, потому что тонкости программирования на С и C++ выходят за рамки этой книги, a OLE Automation лучше не трогать до тех пор, пока вы не освоите основы программирования на Visual Basic. Чтобы воспользоваться услугами Windows API, необходимо подключить к вашей программе библиотеки динамической компоновки, или DLL. DLL представляют собой компоненты приложений, объединяющие несколько функций в файле, который связывается с программой и используется ею во время выполнения. Функции DLL не внедряются в приложение. DLL представляет собой откомпилированный код (наподобие классов, описанных в уроке 4). Такая возможность чрезвычайно полезна, если вы хотите воспользоваться чьим-то готовым кодом или уменьшить размер своей программы. Операционная система Windows работает по тому же принципу. Она состоит из множества DLL и нескольких программ, которые пользуются услугами этих DLL. Примером могут служить диалоговые окна Open/Save, появляющиеся при открытии и закрытии документов. Вы замечали, что во всех приложениях эти окна выглядят одинаково? Здесь нет ничего удивительного, поскольку они действительно совпадают. Диалоговое окно вызывается из DLL, поэтому им можно пользоваться в любой программе.
Многие программисты-новички считают Windows API чем-то вроде большого «черного ящика», который трудно понять и еще труднее использовать. В действительности API представляет собой набор DLL, обеспечивающих функциональные возможности операционной системы Windows. Заранее откомпилированные средства API находятся в вашем полном распоряжении — при условии, что вы знаете, что вам требуется, где найти нужную функцию и как ее вызвать. Одни DLL находятся в подкаталоге \Windows\System, другие — в основном каталоге Windows или других его подкаталогах.
Win32, последняя версия этого API, состоит исключительно из 32-разрядных функций. Они обрабатывают информацию намного эффективнее, чем существовавшие раньше 16-разрядные версии. Для обеспечения обратной совместимости также был сохранен 16-разрядный код API, вызываемый старыми приложениями. API можно рассматривать как набор функций в DLL с документированными правилами вызова.
Правила вызова DLL
Вызов DLL может выглядеть довольно сложно (особенно если вы незнакомы с типами данных языка С). К счастью, DLL, составляющие Windows API, хорошо документированы, а код для объявления их функций включен в Visual Basic. Перед тем как пользоваться функцией DLL, ее необходимо объявить.
Чтобы сообщить Visual Basic о своем желании обратиться к DLL, вы объявляете процедуру и указываете типы ее параметров. После этого ваша программа может вызвать нужную функцию DLL. На основании объявления Visual Basic поймет, как передавать информацию в DLL и получать результаты. Windows API содержит чрезвычайно мощный и полезный набор функций, но им не хватает устойчивости. Всего одна ошибка в типе параметра — и ваша программа начинает работать со сбоями, неожиданно завершается и даже «подвешивает» систему. Все это может привести к неприятным последствиям, особенно если вы не привыкли часто сохранять свою работу. Нарушение правил вызова DLL чаще всего приводит к сбою системы,
ВНИМАНИЕПри работе с DLL сохраняйте свою работу перед каждым пробным запуском. Ошибка при вызове DLL может нарушить работу компьютера, и вы потеряете все, что успели сделать!
Перед обращением к Windows API следует определить, какая функция вам нужна. После того как ее имя станет известно, следует объявить ее в программе. Для этого применяется оператор Declare, например:
Private Declare Function sndPlaySound Lib "winmm.dll" Alias "sndPlaySoundA" (ByVal IpszSoundName As String, ByVal uFlags As Long) As Long Возможно, этот фрагмент выглядит устрашающе, но на самом деле понять его не так уж сложно — достаточно лишь знать правила вызова DLL. Здесь объявляется функция API с именем sndPlaySound, предназначенная для воспроизведения WAV-файла. Давайте рассмотрим этот фрагмент и выясним, что в нем происходит.
Смысл правил вызова DLL
Объявления различных функций API обладают рядом общих черт. Мы последовательно рассмотрим каждую из них, чтобы лучше понять смысл происходящего.
Выбор области видимости
Область видимости для функций API определяется точно так же, как и для любых других процедур, написанных на Visual Basic. Ключевое слово Public открывает функцию для всего приложения, а ключевое слово Private ограничивает доступ к ней текущей формой.
В приведенном выше фрагменте функция API объявляется закрытой по отношению к текущему модулю. Она ничем не отличается от других закрытых функций, написанных вами (см. урок 4), — разве что в данном случае вы не создаете функцию, а вызываете ее из своей программы.
Объявление DLL
Оператор Declare Function сообщает Visual Basic, что к приложению необходимо подключить функцию sndPlaySound из файла winmm.dll Ключевое слово Alias указывает имя функции, которое используется внутри DLL. Оно обеспечивает совместимость между стандартной 16-разрядной и 32-разрядной версиями API.
Типы параметров
Параметры могут передаваться по значению и по ссылке. При передаче по значению (ключевое слово ByVal) функция получает текущее значение переменной. Если же переменная передается по ссылке (ключевое слово ByRef), функция находит в памяти область, в которой хранится данная переменная, и берет ее значение прямо оттуда. Тем самым обеспечивается совместимость с языками С и C++, поскольку большая часть API написана именно на этих языках.
ВНИМАНИЕ
По умолчанию параметры передаются DLL по ссылке. Чтобы передать переменную по значению, необходимо указать ключевое слово ByVal. Если не сделать этого, значение будет передано по ссылке, и функция будет работать неверно. Скорее всего, это приведет к нарушению работы программы.
После оператора Alias следует список параметров, передаваемых функции при вызове. В нашем примере оба параметра, lpszSoundName и uFlags, передаются по значению (ByVal). Параметр lpszSoundName соответствует строковой переменной
с полным именем файла формата .WAV. Параметр uFlags передает функции набор особых флагов, используемых в ее работе. Не обращайте внимания на префиксы lpsz и u — они подчиняются соглашениям об именах, принятым в С и C++. Наконец, функция возвращает длинное целое (Long), с помощью которого сообщает о том, успешно ли прошел вызов.
Код возврата
В последней строке объявления задается тип значения, возвращаемого функцией. Поскольку работа большинства функций API не видна пользователю, с помощью кода возврата они сообщают программе, успешно ли была вызвана функция, а в случае неудачи — причины сбоя. Эта информация важна, поскольку при работе с API программа зависит от вызываемых в ней функций. Если вызов функции API по какой-либо причине закончится неудачей, а ваше приложение не проверит код возврата и продолжит работу, скорее всего, дело кончится аварийным завер-
шением.
Раз уж мы заговорили о кодах возврата, необходимо сделать одно замечание. Вы можете использовать коды возврата в своих функциях для передачи информации в вызывающую программу. Более того, это считается «хорошим тоном», поскольку позволяет вашему приложению обмениваться информацией с компонентами. Во время программирования никогда не следует полагать, что процедура нормально сработала, — обязательно проверяйте код возврата. При этом снижается вероятность того, что ваше приложение начнет работать неверно из-за присвоения переменной ошибочного значения (или вообще никакого значения). Функции Windows API похожи на функции и процедуры, которые вы создаете в своих собственных приложениях, но у них есть одно существенное отличие: их код был написан заранее. Вам остается лишь позаботиться о правильном объявлении функций, а также использовании правильных типов параметров при обмене информацией. Если вы не проявите достаточной осторожности, ваши программы будут часто «падать» или, что еще хуже, «подвешивать» операционную систему.
API Viewer
Теперь вы знаете основные принципы объявления DLL, и я покажу, как упростить
работу. В комплект Visual Basic входит вспомогательная программа API Viewer
(рис. 19.1).
Она содержит сведения о большинстве функций, типов переменных и констант
Windows API, позволяет быстро найти вызываемую функцию и выдает готовый
фрагмент, который включается в приложение.
Работа с API Viewer происходит так:
1. Из меню Start запустите программу API Text Viewer из программной группы Visual Basic.
2. В API Viewer выполните команду File > Load Text File.
Рис. 19.1. API Viewer 3. Выберите файл Win32.api и нажмите кнопку Open.
ПОДСКАЗКАAPI Viewer быстрее загружает информацию об API, если преобразовать текстовый файл в базу данных. Это стоит сделать в том случае, если вы собираетесь часто работать с API Viewer. Чтобы преобразовать текстовый файл в базу данных, выполните команду File > Convert Text to Database и укажите имя базы в открывшемся диалоговом окне (рис. 19.2).
4. Нажмите кнопку Yes, чтобы преобразовать файл в базу данных.
5. Когда преобразование будет закончено, выберите из списка API Type (в верхней части окна) строку Declares.
П0ДСКАЗКАAPI Viewer также можно вызвать из меню Add-Ins Visual Basic
Вы можете просмотреть весь список, хотя он достаточно длинен. Это неполное описание Windows API, но даже приведенной информации почти всегда хватает. Для ускорения поиска можно ввести начальные буквы интересующей вас темы -список автоматически прокручивается в нужное место.
6. Найдите функцию sndPlaySound. Выделите ее и нажмите кнопку Add, чтобы включить объявление функции в список Selected Items (рис. 19.3).
Рис. 19.2. Преобразование текстового файла в базу данных
Рис. 19.3. Объявление функции sndPlaySound
7. Когда функция появится в списке, нажмите кнопку Сору. Объявление функции копируется в буфер обмена.
8. Закройте API Viewer. Использование функций API в приложениях
Мы узнали, как с помощью API Viewer получить объявление функции API для ваших программ. Давайте посмотрим, каким образом объявления интегрируются с приложениями. Продолжим с того момента, на котором закончился предыдущий пример:
1. Создайте новый проект в Visual Basic. Если откроется окно Project Wizard, вы-, берите в нем значок Standard EXE.
2. Дважды щелкните на форме Form1 в окне проекта — открывается окно программы.
3. Перейдите к секции (General)(Declarations) и выполните команду Edit > Paste. Примерный вид окна программы показан на рис. 19.4.
Рис.19.4. Объявление функции API в окне программы
4, Вставьте перед Declare ключевое слово Private:
Private Declare Function sndPlaySound Lib "winmm.dll" _ Alias "sndPlaySoundA" (ByVal IpszSoundName As String, _ ByVal uFlags As Long) As Long
Обычно объявления функций API находятся в отдельном модуле. Тем не менее их также можно включить в форму или класс, но в этом случае перед Declare необходимо указать ключевое слово Private. Если этого не сделать, вы получите сообщение об ошибке.
5. Добавьте следующий фрагмент в процедуру события Form_Load() формы Form1: Private sub Form_Load()
Dim filename As String Dim rc As Long
filename = "c:\windows\media\tada.wav" rc = sndPlaySound(filename, 1) End Sub
ПРИМЕЧАНИЕВы можете изменить значение параметра filename и воспользоваться другим WAV-файлом. Приведенный выше фрагмент использован лишь в качестве примера. Также обратите внимание на то, что код возврата (rс) объявлен как длинное целое (Dim rc As Long). Тип переменной должен точно совпадать с типом, указанным в объявлении функции API. Параметры filename и flags также должны соответствовать объявлению. В своем примере я присвоил параметру flags значение 1.
6. Если все идет нормально, сохраните проект и запустите его.
ВНИМАНИЕВсегда сохраняйте проект перед запуском, если вы используете функции Windows API. Всего один неправильный параметр может привести к нарушению работы системы и потере всех несохраненных данных.
Если на вашем компьютере установлена звуковая карта, а звук включен, вы услышите знаменитое «Та-да!». Немного поэкспериментируйте с параметром filename и попробуйте изменить его, чтобы воспроизвести другие звуковые файлы. С помощью этой функции API можно добавить звуковое оформление к вновь создаваемым программам. Может быть, ваши приложения будут содержать звуковые инструкции по своей работе. Если вы пишете программу сохранения экрана (screen saver) или игру, использование звука становится почти что обязательным.
Изменение функции API
Функции API также могут вызываться из специально написанных функций. Последние называются оболочками, потому что их настоящая работа скрывается внутри вспомогательного кода. Например, если вы захотите отключать звук в своем приложении, создайте оболочку для функции API:
Public Sub PlayWav(filename As String, soundon As Boolean) Dim rc As long
If soundon = True Then
rc = sndPlaySound(filename, 1) End If End Sub
Эту полезную процедуру можно добавить в вашу библиотеку. При желании попробуйте инкапсулировать ее в специальном классе для работы с WAV-файлами. Глав-
ное, о чем следует помнить, — пользователь должен иметь возможность отключить звук. Хотя многие предпочитают работать со звуком, со временем это может надоесть. Значение, определяющее выбор пользователя, передается процедуре PlayWav. Кроме того, звук отключается и в том случае, если на вашем компьютере не установлены средства мультимедиа (хотя это маловероятно).
Создание класса WavePlayer
Вы научились воспроизводить WAV-файлы средствами Windows API. Следующим шагом станет создание универсального класса WavePlayer, который можно будет включать в будущие проекты. Класс достаточно прост, но его легко расширить, например добавить возможность управления громкостью звука. Наш класс Wave Player содержит два свойства и один метод. Он создается следующим образом:
1. Создайте новый проект командой File > New Project. Выберите тип проекта Standard EXE.
2. Задайте свойству Name формы Form1 значение frmMain, а свойству Caption — значение WavePlayer.
3. Создайте кнопку на форме frmMain. Задайте ее свойству Name значение cmdPlay, а свойству Caption — значение &Воспроизвести.
4. Включите в класс новый модуль класса. Для этого щелкните правой кнопкой мыши в окне проекта и выполните команду Add > Class Module из контекстного меню.
5. В открывшемся диалоговом окне Add Class Module выберите значок Class Module и нажмите кнопку Open.
6. В окне свойств задайте свойству Name созданного класса значение WavePlayer.
7. Включите следующий фрагмент в секцию (General)(Declarations) класса WavePlayer:
Option Explicit
Private mFilename As String Private mFlags As Long
Private Declare Function sndPlaySound Lib "winmm.dll" Alias "sndPlaySoundA" (ByVal IpszSoundName As String, _ ByVal uFlags As Long) As Long
8. Добавьте следующие процедуры, образующие интерфейс к свойству Filename:
Public Property Get Filename() As String
filename = mFilename End Property
Public Property Let Filename(ByVal vNewValue As String)
mFilename = vNewValue End Property
9. Добавьте следующие процедуры, образующие интерфейс к свойству Flags:
Public Property Get Flags() As Long
Flags = mFlags End Property
Public Property Let Flags(ByVal vNewValue As Long)
mFlags = vNewValue End Property
10. Добавьте следующий фрагмент единственного метода класса:
Public Sub Play() Dim rc As Long
rc = sndPlaySound(mFilename, mFlags) End Sub
11. Сохраните проект. Если вы создали программную библиотеку в уроке 4, сохраните файл WavePlayer.cls в каталоге библиотеки, что упростит его повторное использование в будущем. Оставьте файл проекта в исходном каталоге.
12. Откройте окно программы для frmMain и перейдите в секцию (General) (Declarations).
13. Чтобы воспользоваться объектом WavePlayer, необходимо создать ссылку на него. Добавьте следующий фрагмент:
Option Explicit
Private wav As WavePlayer
14. Ссылка на объект имеется, теперь мы должны создать экземпляр указанного класса. Лучше всего сделать это в процедуре события Form_Load. Вставьте в процедуру следующий фрагмент:
Private Sub Form_Load()
Set wav = New WavePlayer End Sub
15. При завершении работы приложения объект WavePlayer необходимо уничтожить. Для этого вы присваиваете ему Nothing и таким образом освобождаете занимаемую им память. Вставьте следующий фрагмент в процедуру события Form_Unload:
Private Sub Form_Unload(Cancel As Integer)
Set wav = Nothing End Sub 16. Наконец, вставьте следующий фрагмент в процедуру события cmPlay_Click. В нем объект WavePlayer непосредственно воспроизводит WAV-файл:
Private Sub cmdPlay_Click() With wav
.Filename = App.Path & "\Tada.wav" .Flags = 1 .Play End With End Sub
ПРИМЕЧАНИЕПеред запуском приложения скопируйте воспроизводимый файл (в нашем примере — tada.wav) в каталог проекта. При желании можно легко заменить этот файл любым другим, который вы хотите услышать.
7. Сохраните и запустите приложение.
Несмотря на свою простоту, этот пример показывает, как легко преобразовать автономную процедуру в объект с помощью модулей классов. Кроме того, класс WavePlayer можно будет использовать в ваших будущих проектах. Вам уже не придется запускать API Viewer или запоминать, какая функция воспроизводит WAV-зайлы. Достаточно включить класс WavePlayer в проект, и ваши приложения предстанут в совершенно новом свете!
Работа с системной панелью
Некоторые приложения, написанные для Windows 95 и Windows NT версии 4, ра-отают в системной панели - небольшой прямоугольной области, расположенной в правой части панели задач (см. рисунок). На системную панель часто помещаются приложения, управляющие различными устройствами
или работающие по расписанию. Скорее всего, на вашем компьютере в системной панели находятся часы и, возможно, значок с динамиком для регулировки громкости. 3 комплект Visual Basic входит пример, который компилируется в элемент ActiveX. Мы воспользуемся им для того, чтобы поместить приложение на системную панель.
Издание элемента SysTray
Перед тем как пользоваться элементом ActiveX в приложении, необходимо отком-шлировать его и сохранить в подкаталоге \System каталога Windows. Элемент троится так:
Откройте проект Systray.vbp из каталога \Cornmon\Tools\VB\Unsupprt\Systray на компакт-диске Visual Basic.
В окне проекта откройте папку Modules и дважды щелкните на строке mSysTray, чтобы открыть модуль в окне программы.
Просмотрите секцию (Global)(Declarations) модуля. Обратите внимание на объявления нескольких функций API, среди которых — CallWindowProc, GetWindowLong и Shell_NotifyIcon (рис. 19.5). Наряду с прочими они помещают значок программы на системную панель. Мы не будем подробно рассматривать тонкости их работы, однако вы можете просмотреть исходный текст программы и ознакомиться с комментариями, чтобы понять, как работает данный элемент.
Рис. 19.5. Объявления функций API
3. Выполните команду File > Make Systray.ocx.
4. В диалоговом окне Make Project укажите в поле Save In подкаталог \System каталога, в котором установлена система Windows. На большинстве компьютеров это будет C:\Windows\System.
5. Нажмите кнопку О К, чтобы завершить компиляцию элемента.
Элемент SysTray готов к работе.