WinApi
Программный код большинства примеров данной лекции можно найти в проектах, доступных для просмотра: DocOne6, DocTwo6.
Несколько слов об API, Win32, DLL
API (Application Programming Interface - интерфейс прикладных программ) - это множество функций, организованных, обычно, в виде DLL. Функции API позволяют организовать интерфейс между прикладной программой и средой, в которой работает эта программа. Вызов функций API позволяет программе получать доступ к ресурсам среды и управлять ее работой. Как правило, API задает стандарт взаимодействия среды и прикладной программы.
Win32 - это название интерфейса, ориентированного на 32-х разрядные приложения и реализованного на таких известных платформах как Windows 95, Windows 98, Windows NT, Windows CE. Функции, составляющие этот интерфейс, позволяют прикладной программе получать доступ к ресурсам операционной системы и управлять ее работой. Более ранние версии Windows используют интерфейс, известный как Win16. Конечно, не все функции, составляющие интерфейс Win32, реализованы в полной мере на всех платформах, так что вызов одной и той же функции под NT приведет к определенному результату, а под Windows 95 работает как вызов заглушки. Любое из приложений, работающее в среде Windows, прямо или косвенно вызывает функции, входящие в Win32 API.
Функции, составляющие Win32 интерфейс, организованы в виде нескольких динамически подключаемых библиотек ( DLL ) и исполняемых файлов. Говоря о Win32 API, следует в первую очередь упомянуть три основные библиотеки:
- Kernel32.dll. Эта библиотека предназначена для работы с объектами ядра операционной системы и ее функции позволяют управлять памятью и другими системными ресурсами.
- User32.dll. Здесь сосредоточены функции для управления окнами - основным видом объектов операционной системы. Обработка сообщений, работа с меню, таймерами, все это выполняют функции этой DLL.
- GDI32.dll. Эта библиотека, обеспечивающая графический интерфейс операционной системы (Graphics Device Interface). Функции управления выводом на экран дисплея, управления выводом принтера, функции для работы со шрифтами - все они входят в состав этой библиотеки.
Заметьте, Win API функции находятся не только в этих библиотеках. С другой стороны API функции не обязательно входят в состав Win32 интерфейса. Например, MAPI интерфейс (Messaging Application Programming Interface) составляют функции, предназначенные для обработки сообщений электронной почты, TAPI (Telephone API) - функции работы с телефонными сообщениями. MAPI, TAPI, также как и Win32 это некоторый набор функций, задающий определенный стандарт взаимодействия
Как мы уже говорили, функции, образующие API, обычно, организованы в виде DLL - динамически подключаемых библиотеках. Одно из достоинств DLL состоит в том, что, сколько бы приложений (процессов) не работало с функциями одной и той же DLL, код DLL существует в единственном экземпляре.
.VBA и Win32 API
Работая на VBA, неявно всегда приходится иметь дело с функциями Win32 API, только вызов их упрятан в вызываемых VBA функциях или методах объектов Office 2000. Так, например, при работе с объектом Shape так или иначе будут вызываться функции GDI32, обеспечивающие работу с графикой, при работе c функциями VBA.Interaction, например GetSettings, SaveSettings и другими, будет вызываться соответствующие функции работы с реестром Windows, хранящиеся в библиотеках User32 и advapi32. Такой косвенный вызов имеет свои преимущества, обеспечивая определенную безопасность в работе VBA программ. Но в ряде случаев VBA программисту необходим доступ ко всем возможностям операционной системы, предоставляемым Win32 API интерфейсом. Естественно, в этом случае он понимает, что на него ложится большая ответственность в обеспечении корректного вызова функций, поскольку ошибки в вызове могут привести к непредвиденным отказам в работе программы.
Вызов функций и оператор Declare
Элементы ActiveX, COM объекты могут экспонировать свой интерфейс, - свои свойства и методы. Это означает, что они уведомляют, предоставляют информацию клиентам о своем интерфейсе. Технически это обеспечивается тем, что эти объекты, наряду с DLL, сопровождаются TypeLib - библиотекой типов, в которой содержится в требуемом виде информация об интерфейсе объекта. В этом случае, для того чтобы начать работу с объектом, достаточно подключить ссылку на эту библиотеку в меню Tools|References в среде редактора VBE. Эта возможность не раз обсуждалась, когда речь шла о вызове, например, приложения Excel в документах Word. Напомним, что приложения Office 2000 представляют собой ActiveX объекты, построенные на основе COM технологии. Они явно экспонируют свой интерфейс, именно поэтому нет проблем при работе с такими приложениями, вызовами свойств и методов их многочисленных объектов. Библиотеки, составляющие Win32 интерфейс, не сопровождаются библиотеками типов TypeLib. Поэтому необходимо самому программисту уведомить VBA о том, где найти и как следует вызывать ту или иную функцию Win32 API Вызову каждой функции должен предшествовать оператор Declare, описывающий эту функцию. Этот оператор и сама схема вызова библиотечных функций используется при работе с любыми DLL, а не только с теми, которые содержат Win32 API функции. В общем случае в DLL могут храниться как функции, так и процедуры. Два варианта вызова этого оператора соответствуют ссылке на процедуру и на функцию, возвращающую значение. Первый вариант:
[Public | Private] Declare Sub имя Lib "имя-библиотеки" [Alias "псевдоним"] [([параметры])]
Во втором случае его синтаксис:
[Public | Private] Declare Function имя Lib "имя-библиотеки" [Alias "псевдоним"] [([параметры])] [As возвращаемый-тип]
В этих вызовах ключевые слова и параметры имеют следующий смысл:
- Ключевое слово Public используется, чтобы сделать объявляемую процедуру доступной всем модулям проекта; ключевое слово Private ограничивает доступ к объявленной процедуре лишь модулем, в котором она объявлена. Заметьте, в стандартном модуле можно использовать оба описателя, но в модуле класса разрешается использовать только описатель Private.
- Ключевое слово Sub в первом случае означает, что речь идет о процедуре; альтернативный ключ Function во втором случае указывает на функцию, возвращающую значение, которое может быть использовано в выражениях.
- Обязательный параметр имя является именем объявляемой процедуры или функции. Это имя используется при вызовах в VBA программах. Оно может совпадать или отличаться от того имени, под которым процедура (функция) хранится в DLL. Заметьте, для имен функций Win32 API в отличие от Win 16 имеет значение различие между прописными и строчными буквами.
- После обязательного ключевого слова Lib должно следовать в кавычках имя-библиотеки, содержащей объявляемую процедуру.
- Ключ Alias позволяет указать, что процедура имеет в DLL другое имя - " псевдоним ", благодаря чему можно избежать коллизии имен в программе. Если первый символ параметра " псевдоним " не является признаком числа ( # ), псевдоним понимается как имя входной точки DLL для данной процедуры; если же первый символ псевдонима - ( # ), следующие за ним число должно задавать порядковый номер входной точки процедуры в DLL. Дело в том, что процедура может иметь несколько точек входа.
- Необязательный список параметры задает список параметров, передаваемых процедуре при вызове.
- As возвращаемый-тип во втором варианте оператора задает тип значения, возвращаемого функцией; им может быть любой из базисных типов VBA (не допускаются только строки фиксированной длины), тип объекта или определенный пользователем тип.
- Список параметры - это список разделенных запятыми параметров процедуры; каждый элемент этого списка имеет вид:
[Optional] [ByVal | ByRef] [ParamArray] переменная[()] [As тип]
- Здесь ключ Optional означает, что данный параметр необязателен; при этом все следующие в списке параметры должны быть необязательными и сопровождаться этим же ключом Optional. Этот ключ нельзя применять, если среди параметров есть массив параметров ParamArray.
- Ключевые слова ByVal и ByRef указывают на то, что параметр передается по значению или по ссылке; по умолчанию в VBA предполагается передача значения по ссылке ( ByRef ).
- Ключевое слово ParamArray позволяет задать массив элементов типа Variant ; этот параметр должен быть последним в списке и не должен перед собой иметь ключей ByVal, ByRef или Optional ; такой массив позволяет передавать в процедуру произвольное (заранее неизвестное) число параметров.
- Переменная - произвольное допустимое в VBA имя переменной; пустые скобки () после имени переменной означают, что соответствующий параметр - массив.
- Необязательное определение As тип задает тип параметра, значения которого могут быть такими же, как и у описанного выше определения возвращаемый-тип. Задав произвольный тип ( As Any ), Вы исключите проверку при передаче параметра процедуре.
Вот пример задания оператора Declare для двух функций Win32 API:
Private Declare Function CreateRectRgn Lib "gdi32" Alias "CreateRectRgn" _ (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long Private Declare Function GetTempPath Lib "kernel32" _ Alias "GetTempPathA" (ByVal nBufferLength As Long, _ ByVal lpBuffer As String) As Long
Синтаксически оператор Declare прост и понятен, нужно указать библиотеку, имя функции, под которым она будет вызываться в VBA программе, ее имя (псевдоним) под которым она записана в библиотеке и параметры функции в привычном синтаксисе. Однако реальная жизнь не так проста и в организации вызова функций API есть много подводных камней. Дело в том, что DLL служат средством межязыкового взаимодействия. Сама DLL может быть разработана на одном языке, а вызываться в другом. Тогда возникает проблема правильной передачи параметров, поскольку может не быть точного соответствия между типами данных двух используемых языков. Функции Win32 API разработаны в ориентации на синтаксис языка C и C++. Поэтому при записи оператора Declare требуется корректно указать типы параметров, так чтобы они соответствовали типам, используемым в языке C. Еще одна проблема состоит в том, что помимо оператора Declare, в ряде случаев необходимо предварительно описать требуемые типы данных и константы, необходимые в процессе вызова функции. Так что, прежде чем вызвать функцию из DLL, необходимо корректно задать оператор Declare, описать необходимые типы и константы, - все это может быть не столь простой задачей.