Дата публикации статьи: 25.05.2004 08:09

Гергерт Сергей
Оптимизация вызова строковых API

    Тип String в VB - это на самом деле WideString, т.е. для хранения каждого символа используется не 1, а 2 байта, как требует стандарт Unicode. Два байта на символ - это 65535 комбинаций, для земных алфавитов хватает.
    API-функции, принимающие параметр String, как правило, существуют в двух вариантах: A и W (по последней букве). A - функция хочет получить 1-байтовую строку ANSI. W - функции нужна 2-байтовая строка Unicode. Практически все OLE-функции являются W, хотя и нет такой буквы в их окончании.
    Когда мы объявляем какую-либо функцию через Declare, и у этой функции есть параметр(ы) String, то в любом случае при вызове этой функции VB создаёт копию нашей строки при помощи StrConv(str, vbFromUnicode), и в функцию передаётся именно она. Когда функция возвращает результат, VB проделывает обратную операцию - StrConv(str, vbUnicode).
    При этом он никогда не спрашивает нас, нужно ли нам это. В большинстве случаев это действительно удобно (когда мы работает с функциями A), но как быть с W? При передаче им параметра конвертировать из Unicode не нужно, но нас никто не спрашивает. Один из способов дать W-функции верные данные - перед вызовом сделать StrConv(str, vbUnicode). Тогда получается:

  • мы делаем конверт в Unicode, получается как бы Double Unicode
  • VB перед вызовом функции делает vbFromUnicode, получается нормальный Unicode
  • W-апишка получает свою строку
  • на возвращённом результате VB проводит vbUnicode, получается "Double Unicode"
  • мы после вызова делаем vbFromUnicode и наконец получаем результат

    Страшно, правда? :) Вот был бы у оператора Declare модификатор Unicode, а? "Но только принца нет, где ж он подевался...". А раз так, обратим свои взоры на то, что именно ожидает увидеть API-функция в качестве своего параметра. Ожидает она увидеть указатель на массив байт, который, собственно, и является строкой. В результате описанных действий VB неявно передаёт ей указатель на копию строки, но мы ведь можем и сами. Для того и дадена нам функция StrPtr, чтобы указатель на строку получать. А раз так, то выводим правило:

  • Если функция требует строку ANSI, то её строковые параметры объявляются As String
  • Если функция требует строку Unicode, то её строковые параметры объявляются As Long, и в качестве значения туда передаётся StrPtr(str).

    В первом случае - два конверта (от них не денешься никуда). Во втором случае - ни одного. А было четыре.