Опубликовано:
Исправлено:
Версия документа: 1
Строки BSTR
В языках программирования используются разные форматы для хранения строк, кто во что горазд. Для передачи строк в COM потребовался универсальный формат, не зависящий от языка программирования. Используй BSTR
во всех интерфейсах, доступных из скриптов (то есть унаследованных от IDispatch
).
BSTR
расшифровывается как «Basic STRing». Такое название строка получила потому, что в Visual Basic именно так хранятся строки.
Внутреннее устройство
Тип данных BSTR
Посмотрим, как объявляется BSTR
в заголовочных файлах. Открываем файл inc\win\wtypes.bi
:
Type BSTR As OLECHAR Ptr
Type LPBSTR As BSTR Ptr
Выходит, что BSTR
— это псевдоним для указателя на OLECHAR
. Что такое OLECHAR
выясняем в заголовочнике inc\win\wtypesbase.bi
:
Type OLECHAR As WCHAR
И вновь отсылка. Смотрим определение WCHAR
в файле inc\win\winnt.bi
:
Type WCHAR As wchar_t
Осталось выяснить, что такое wchar_t
. Определение этого типа находится в файле inc\crt\stddef.bi
и зависит от конкретной платформы:
#ifdef __FB_DOS__
' wchar_t в DOS — это беззнаковое 8‐битное целое
Type wchar_t As UByte
#elseif defined( __FB_WIN32__ ) or defined( __FB_CYGWIN__ )
' wchar_t в Windows — беззнаковое 16‐битное целое
Type wchar_t As UShort
#else
' wchar_t в Linux — знаковое 32‐битное целое
Type wchar_t As Long
#endif
Таким образом, на Windows BSTR
— это дополнительное имя для массива 16‐битных беззнаковых целых чисел. В данном случае эти числа интерпретируются как символы юникода в кодировке UTF-16 Little Endian.
Представление в памяти
BSTR
— это составной тип данных. В нём выделяют три части.
Часть | Размер | Описание |
---|---|---|
Префикс длины | SizeOf(UINT) | Количество занимаемых байт символов строки данных без учёта нулевого символа |
Строка данных | SizeOf(OLECHAR) * (число символов) | Массив символов юникода, может содержать в себе нулевые символы |
Окончание | SizeOf(OLECHAR) | Завершающий нулевой символ |
BSTR
— это указатель. Этот указатель ссылается не на префикс, а на первый символ строки данных.
Схематичное представление строки в памяти в соответствии с кодировкой UTF-16 Little Endian:
BSTR указывает на первый символ в массиве
│
↓ Символы строки «Привет, Мир!»
╔═══════════╦═════╤═════╤═════╤═════╤═════╤═════╤═════╤═════╤═════╤═════╤═════╤═════╦═════╗
║ 24 ║ П │ р │ и │ в │ е │ т │ , │ │ М │ и │ р │ ! ║ \0 ║
╚═══════════╩═════╧═════╧═════╧═════╧═════╧═════╧═════╧═════╧═════╧═════╧═════╧═════╩═════╝
Байты строки «Привет, Мир!»
╔══╤══╤══╤══╦══╤══╤══╤══╤══╤══╤══╤══╤══╤══╤══╤══╤══╤══╤══╤══╤══╤══╤══╤══╤══╤══╤══╤══╦══╤══╗
║24│ 0│ 0│ 0║31│ 4│64│ 4│56│ 4│50│ 4│53│ 4│66│ 4│44│ 0│32│ 0│28│ 4│56│ 4│64│ 4│33│ 0║ 0│ 0║
╚══╧══╧══╧══╩══╧══╧══╧══╧══╧══╧══╧══╧══╧══╧══╧══╧══╧══╧══╧══╧══╧══╧══╧══╧══╧══╧══╧══╩══╧══╝
↑ ↑
Префикс длины Завершающий нулевой символ
Такая реализация имеет ряд преимуществ:
- длину строки не нужно пересчитывать, она хранится в заголовке;
- строка может содержать нулевые символы где угодно;
- адрес строки можно без проблем передавать в WinAPI функции, где ожидается
WCHAR Ptr
.
Отличия WString и BSTR
Встроенная строка WString
и строка BSTR
похожи тем, что содержат в себе символы юникода. Однако у них есть некоторые отличия:
Отличия | WString | BSTR |
---|---|---|
Существование | Может существовать как в стэке (статическая строка), так и в куче (динамическая строка) | Существует только в куче |
Ограничение длины | Ограничена длиной буфера, а буфер — наличием свободной памяти | Ограничена 32‐битным числом, однако это число очень большое, и вряд ли понядобятся строки длиной 2 147 483 648 символов |
Начало строки | Может указывать на любой символ в буфере | Всегда указывает на первый символ в буфере |
Нулевые символы | Не может содержать в себе нулевые символы, так как нулевой символ является признаком конца строки | Может содержать в себе нулевые символы |
Конец строки | Определяется завершающим нулевым символом, не входящим в строку | Определяется длиной строки |
Совместимось | Несовместима с BSTR , так как не содержит в себе префикс длины | Совместима с WString , так как заканчивается нулевым символом |
Функции для работы с BSTR
Все функции располагаются в библиотеке OleAut32.dll
. Для их использования требуется подключить следующие заголовочные файлы:
#include "windows.bi"
#include "win\ole2.bi"
Нам потребуются функции:
Пункт | Функции |
---|---|
Создание строки | SysAllocString и SysAllocStringLen |
Удаление строки | SysFreeString |
Получение длины | SysStringLen и SysStringByteLen |
Объединение | VarBstrCat |
SysAllocString
Функция принимает на вход указатель на буфер WString
и возвращает BSTR
.
Declare Function SysAllocString( _
ByVal As Const Wstring Ptr _
)As BSTR
В случае успеха возвращает BSTR
, при ошибке — NULL
.
Пример
Dim b As BSTR = SysAllocString("Привет, мир!")
SysAllocStringLen
Функция SysAllocStringLen
принимает на вход указатель на буфер WString
и количество символов, копируемых из буфера в будущую BSTR
.
Declare Function SysAllocStringLen( _
ByVal As Const Wstring Ptr, _
ByVal As UINT _
)As BSTR
В случае успеха возвращает BSTR
, при ошибке — NULL
.
Пример
' Скопируется строка «Привет»
Dim b As BSTR = SysAllocStringLen("Привет, мир!", 6)
Обратное преобразование в WString
Достаточно привести переменную типа BSTR
к указателю на данные WString
:
Dim b As BSTR = SysAllocString("Привет, мир!")
Dim pW As WString Ptr = b
Print *pW
SysStringLen и SysStringByteLen
Функция SysStringLen
возвращает длину строки в символах без учёта завершающего нуля:
Declare Function SysStringLen( _
ByVal As BSTR _
)As UINT
Функция SysStringByteLen
возвращает занимаемую символами строки память в байтах без учёта завершающего нуля:
Declare Function SysStringLen( _
ByVal As BSTR _
)As UINT
Пример
Dim b As BSTR = SysAllocStringLen("Привет, мир!")
Print "Длина строки", SysStringLen(b) ' Выведет 12
Print "Количество байт", SysStringLen(b) ' Выведет 24
SysFreeString
Когда строка больше не нужна, её следует удалить функцией SysFreeString
.
Declare Sub SysFreeString( _
ByVal As BSTR _
)
Функция не возвращает значений.
Пример
Dim b As BSTR = SysAllocString("Привет, мир!")
SysFreeString(b)
Копирование BSTR
Встроенной функции, копирующей BSTR
нет, но её можно написать самостоятельно.
Dim b As BSTR = SysAllocString("Привет, мир!")
' Копирование
Dim copy As BSTR = SysAllocStringLen(b, SysStringLen(b))
VarBstrCat
Функция объединеняет две строки и возвращает результат выходящим параметром.
Declare Function VarBstrCat( _
ByVal bstrLeft As BSTR, _
ByVal bstrRight As BSTR, _
ByVal pbstrResult As LPBSTR _
)As HRESULT
В случае успеха возвращает S_OK
, в случае ошибки — ошибочный код HRESULT
.
Пример
Dim bstrLeft As BSTR = SysAllocString("Привет, ")
Dim bstrRight As BSTR = SysAllocString("мир!")
Dim bstrCombine As BSTR = NULL
VarBstrCat(b1, b2, @bstrCombine)
Dim pB As WString Ptr = bstrCombine
Print *pB ' Выведет «Привет, мир!»
' Когда строки не нужны, их следует уничтожить
SysFreeString(bstrLeft)
SysFreeString(bstrRight)
SysFreeString(bstrCombine)
Правила выделения памяти при передаче и возврате BSTR функциям
Когда ты создаёшь BSTR
и передаёшь их между функциями и объектами, ты должен заботиться об освобождении используемой памяти, чтобы избежать утечек. Когда BSTR
остается в объекте, объект сам освобождает память. Однако когда BSTR
выходит за пределы объекта, принимающая сторона берет на себя ответственность за управление памятью.
BSTR как возвращаемое значение функции
Функция имеет тип данных BSTR
.
Правило: память под строку выделяет функция, уничтожить память должна вызывающая сторона.
Пример
Где‐то объявлена функция:
Declare Function GetStringValue( _
ByVal Value As Integer _
)As BSTR
Использующий функцию код:
' Получаем строку
Dim b As BSTR = GetStringValue(265)
' Мы должны сами убить строку
SysFreeString(b)
BSTR как входящий параметр функции
Такие параметры часто помечают атрибутом in
и имеют тип данных BSTR
.
Правило: память под строку выделяет вызывающая сторона, функция может только читать содержимое строки. Если функция внутри себя где‐нибудь сохраняет строку, она создаёт её копию.
Пример
Где‐то объявлена функция:
Declare Function MyWebBrowser.PutStatusText( _
ByVal b As BSTR _
)As HRESULT
Использующий функцию код:
' Сами выделяем память под строку
Dim bstrStatus As BSTR = SysAllocString("Успешно")
If bstrStatus <> NULL Then
' Передаём строку в функцию
pBrowser->PutStatusText(bstrStatus)
' Сами очищаем память
SysFreeString(bstrStatus)
End If
BSTR как выходящий параметр функции
Такие параметры часто помечены атрибутом out
и имеют типы данных BSTR Ptr
или LPBSTR
. Если выходящий параметр трактуется как возвращаемое значение, то дополнительно указывается атрибут retval
.
Выходящий параметр функция должна вернуть по указателю.
Правило: память под строку выделяет функция, уничтожить память должна вызывающая сторона.
Пример
Где‐то объявлена функция:
Declare Function MyWebBrowser.GetStatusText( _
ByVal pbstr As BSTR Ptr _
)As HRESULT
Использующий функцию код:
Dim bstrStatus As BSTR
pBrowser->GetStatusText(@bstrStatus)
' Мы сами удаляем строку, которую вернула функция
SysFreeString(bstrStatus)
BSTR как входящий и выходящий параметр функции
Такие параметры часто помечены атрибутом in, out
и имеют типы данных BSTR Ptr
или LPBSTR
. Если выходящий параметр трактуется как возвращаемое значение, то дополнительно указывается атрибут retval
.
В этом случае входяще‐выходящий параметр функция также должна вернуть по указателю.
Общее правило: память под строку создаёт вызывающая сторона, функция может освободить занятую память и выделить новую. После возврата управления ответственность за освобождение памяти несет вызывающая сторона.
Пример
Реализация функции:
Function MyWebBrowser.GetStatusText( _
ByVal InputString As BSTR, _
ByVal ppReturnString As BSTR Ptr _
)As HRESULT
' Нам нужна строка InputString, сохраняем её себе
this.CopyInputString = SysAllocStringLen(InputString, SysStringLen(InputString))
' Проверяем, что переданный параметр указывает на переменную
If ppReturnString = NULL Then
Return E_POINTER
End If
' Выделяем память под строку
*ppReturnString = SysAllocString("Успешно")
' Проверяем что память для строки выделена
If *ppReturnString = NULL Then
Return E_OUTOFMEMORY
End If
Return S_OK
End Function