Обработка ошибок, возникающих при вызове функций Win32 API
Как мы уже говорили ранее, не бывает программ без ошибок. Если ошибка возникает при выполнении кода процедур и функций VBA, - ошибка периода выполнения (run time error), - то появляется окно сообщения об ошибке. Если ошибка периода выполнения появляется при работе функции Win32 API, то прерывания работы программы не происходит, окно сообщения об ошибке не появляется. Вместо этого функция возвращает значение 0 в качестве результата, свидетельствующее об ошибке периода выполнения. Тем не менее, большинство функций Win32 API сохраняют информацию о возникшей ошибке. Эту информацию можно получить стандартным способом, используя VBA объект Err. Свойство LastDLLErr этого объекта возвращает номер последней ошибки, возникшей в DLL. К сожалению, сам по себе номер мало что говорит. Необходимо знать описание ошибки, соответствующее этому номеру. Частично причину ошибки можно понять по имени константы, которую можно найти в уже неоднократно упоминавшемся файле Win32API.txt, используемом в API Viewer. Опять-таки, к сожалению, возможные значения констант приводятся независимо от функций, в которых они возникают. И, несмотря на то, что все такие константы начинаются со слова ERROR найти константу по ее значению не так то просто. Можно, конечно, воспользоваться возможностью создания базы данных по текстовому файлу и организовать специальный запрос, позволяющий найти имя константы по ее значению. Естественно, что лучше всего иметь полную информацию об используемых функциях Win32 API, включающую, в том числе, и сведения о возможных ошибках периода выполнения данных функций. Эту информацию можно найти, если под рукой есть подходящая литература, например, справочник программиста Win32, или поискать на упоминавшемся сервере Microsoft для разработчиков.
Естественно, что пример ошибки времени выполнения в процессе работы DLL у нас уже под рукой. Нам и изобретать его не было необходимости. Как Вы помните, в последнем примере мы сетовали на возникновение подобной ошибки в процессе поиска описателя окна по его заголовку при вызове функции FindWindowW, работающей в Unicode кодировке.
Давайте вернемся к этому примеру и попробуем обработать эту ошибку. В раздел объявлений ранее созданного модуля Unicode мы добавили объявление констант и функций и теперь он выглядит так:
Option Explicit
Public Const ERROR_INVALID_NAME = 123&
'Объявление вызываемых функций Public Declare Function FindWindowA Lib "user32" _ (ByVal lpClassName As String, ByVal lpWindowName As String) As Long 'Функции в Unicode кодировке 'Тип string заменен на Any. Передача аргумента по ссылке Public Declare Function FindWindowW Lib "user32" _ (lpClassName As Any, lpWindowName As Any) As Long
Public Declare Function GetWindowText Lib "user32" Alias "GetWindowTextW" _ (ByVal hwnd As Long, lpString As Any, ByVal cch As Long) As Long
Public Declare Function SetWindowText Lib "user32" Alias "SetWindowTextW" _ (ByVal hwnd As Long, lpString As Any) As Long
Public Declare Function GetActiveWindow Lib "user32" () As Long
Public ArCapt() As Byte 'Объявление динамического массива
Приведем теперь процедуру, в которой вызывается функция FindWindowW, приводящая к ошибке периода выполнения:
Пример 6.6.
(html, txt)
Приведем результаты печати , появившиеся в окне отладки при выполнении этой процедуры:
DocOne6 - Microsoft Word Не корректно задано имя при вызове Unicode FindWindowW функции! Microsoft Visual Basic - DocOne6 [running] - [Unicode (Code)] Не корректно задано имя при вызове Unicode FindWindowW функции!
Прокомментируем теперь работу программы и полученные результаты:
- Вначале мы попытались найти окно с заведомо существующим заголовком, - окно документа, содержащего тестовые примеры. В процессе работы функции Win32 API FindWindowW возникла ошибка периода выполнения, функция вернула нулевой результат. Ошибка была обработана, и как показывает константа ERROR_INVALID_NAME, причиной является ошибка в задании имени (передаваемый формат в виде массива байтов не годится для цели поиска и сравнения строк), о чем свидетельствует отладочная информация.
- Далее проводится еще один эксперимент на ту же тему.Для активного окна находится заголовок, используя функцию GetWindowTextW, возвращающую строку в виде массива байтов. Тут же этот массив используется для поиска окна по заголовку. Однако ничего не помогает и снова при поиске окна возникает ошибка. Она обрабатывается, о чем выдается соответствующее сообщение.
Давайте вернемся к этому примеру и попробуем обработать эту ошибку. В раздел объявлений ранее созданного модуля Unicode мы добавили объявление констант и функций и теперь он выглядит так:
Option Explicit
Public Const ERROR_INVALID_NAME = 123&
'Объявление вызываемых функций Public Declare Function FindWindowA Lib "user32" _ (ByVal lpClassName As String, ByVal lpWindowName As String) As Long 'Функции в Unicode кодировке 'Тип string заменен на Any. Передача аргумента по ссылке Public Declare Function FindWindowW Lib "user32" _ (lpClassName As Any, lpWindowName As Any) As Long
Public Declare Function GetWindowText Lib "user32" Alias "GetWindowTextW" _ (ByVal hwnd As Long, lpString As Any, ByVal cch As Long) As Long
Public Declare Function SetWindowText Lib "user32" Alias "SetWindowTextW" _ (ByVal hwnd As Long, lpString As Any) As Long
Public Declare Function GetActiveWindow Lib "user32" () As Long
Public ArCapt() As Byte 'Объявление динамического массива
Приведем теперь процедуру, в которой вызывается функция FindWindowW, приводящая к ошибке периода выполнения:
Public Sub WorkWithApiErr() Dim Res As Long Dim capt As String 'Заголовок Dim HandleW As Long 'Описатель окна
'Поиск окна по заголовку capt = "DocOne6 - Microsoft Word" ArCapt = capt & vbNullChar Debug.Print ArCapt HandleW = FindWindowW(0&, ArCapt(0)) If HandleW > 0 Then 'OK Debug.Print HandleW Else: MsgBox ("Не могу корректно вызвать UniCode FindWindowW") If Err.LastDllError = ERROR_INVALID_NAME Then Debug.Print "Не корректно задано имя при вызове Unicode FindWindowW функции!" End If End If 'Еще один эксперимент: вначале получим заголовок активного окна, 'затем найдем окно по заголовку, работая в Unicode кодировке. HandleW = GetActiveWindow()
'Получить заголовок окна ArCapt = VBA.String$(128, vbNullChar)
Res = GetWindowText(HandleW, ArCapt(0), 128) If Res > 0 Then 'OK Debug.Print ArCapt Else: MsgBox ("не получен заголовок окна") End If