2006 г.
Биллиг Владимир Арнольдович
Интернет-Университет Информационных Технологий, INTUIT.ru
Назад Оглавление Вперёд
Вызов функций Win32 API, работающих в Unicode кодировке
Уже говорилось, что функции API, работающие со строками, вызываются в ANSI кодировке. Сейчас мы попытаемся объяснить причину этого факта, а, с другой стороны, покажем, как можно вызывать функции Win32 API, использующие Unicode кодировку. Заметим, что это может быть важным не столько для функций Win32 API, сколько для других внешних функций, которые могут существовать в кодировке Unicode и не иметь ANSI варианта. Начнем с объяснения ситуации, - почему в VBA внешние функции вызываются в кодировке ANSI. Следует понимать, что строки VBA хранятся в Unicode кодировке и передача строк при вызове внутренних функций внутри VBA происходит в кодировке Unicode. Однако VB и VBA предполагают, что внешний мир устроен по-другому и до сих пор использует кодировку ANSI. Поэтому всякий раз, когда вызываются внешние функции, при вызове происходит преобразование и строковая информация передается и возвращается в кодировке ANSI. По этой причине нельзя вызвать функцию в кодировке Unicode простым изменением псевдонима, задав у него окончание W. Покажем сейчас, как можно "обмануть" VBA, заставив его не выполнять указанных преобразований, что и позволит вызывать функции, корректно работающие в Unicode кодировке. Покажем также, что, как и всякий обман, не всегда все заканчивается благополучно. Тем не менее, с предлагаемым приемом полезно познакомиться. Решение задачи основывается на следующем:
- При вызове функции вместо строки используется массив байтов, хранящий копию строки. Напомним, что внутри VBA строка хранится в Unicode кодировке, поэтому и массив байтов будет хранить строку в этой кодировке.
- В операторе
Declare
необходимо тип String
изменить на тип Any
, что обеспечит отсутствие проверок и преобразований. - Если в операторе
Declare
для строкового параметра указан спецификатор ByVal
, то его необходимо удалить или изменить на ByRef
, явно указав передачу параметра по ссылке.
Остальные детали рассмотрим после приведения соответствующей программы. В качестве примера мы воспользуемся уже рассмотренными функциями:
FindWindow
, которая позволяет найти окно по его заголовку, вернув описатель окна в качестве результата,GetWindowText
, SetWindowText
, позволяющие получить и установить новый заголовок окна.
Заметим сразу же, что нам удалось успешно вызвать и корректно работать с двумя последними функциями в Unicode кодировке. Однако этот прием не работает при вызове функции FindWindowW
. Несмотря на все попытки, переданная для поиска строка заголовка не приводила к успешному завершению поиска. Но обо всем по порядку. Приведем вначале раздел объявлений модуля с именем Unicode, созданного для работы с этим примером:
Option Explicit
'Объявление вызываемых функций в Unicode кодировке
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 ArCapt() As Byte 'Объявление динамического массива
Все пояснения уже сделаны и поэтому приведем процедуру этого модуля, вызывающую функции API:
Пример 6: html, txt
Приведем результаты отладочной печати:
327894
Document1 - Microsoft Word
Document1 - Microsoft Word
NewDoc
Дадим краткие комментарии к тексту процедуры:
- Работа процедуры начинается с вызова функции API
FindWindowA
, работающей в кодировке ANSI. Она успешно находит окно, заголовок которого задан переменной Capt
. Функция возвращает его описатель. - На следующем шаге мы пытались решить эту же задачу, используя функцию API
FindWindowW
, работающую в кодировке Unicode. В тексте нашел отражение один из вариантов решения. К сожалению, поиск во всех случаях заканчивался неуспехом, хотя, как показал побайтный анализ в окне Watch и как показывает отладочная печать, массив байтов ArCapt
содержит Unicode копию строки заголовка - Полученный описатель окна использовался при вызове Unicode варианта функции
GetWindowTextW
. Функция корректно работала, используя ссылку на переданный ей массив байтов ArCapt
. - Затем, используя эту же технику, заголовок окна был изменен при вызове Unicode варианта функции
SetWindowTextW
. - Для контроля повторно была вызвана функция
GetWindowTextW
. Отладочная печать подтвердила корректность работы.
Неудача в вызове функции FindWindowW
может быть вызвана разными причинами. Вероятнее всего, при выполнении поиска и проведения операций сравнения строк, применяемый способ не корректен, или, по крайней мере, требует дополнительных уточнений, возможно связанных с длиной строки. С другой стороны, сам прием является некоторой уловкой. Существует более честный способ, хотя, возможно, и более трудоемкий. Для решения задачи можно создать библиотеку типов TypeLib
, содержащую описание функций Win32 API в Unicode кодировке, включить ссылку на эту библиотеку и вызывать функции без всяких уловок.
Назад Оглавление Вперёд