Возвращаем строку из функции C++ библиотеки в VBA

Автор: Alex. Опубликовано в Программирование . просмотров: 836

Рейтинг:  0 / 5

Звезда не активнаЗвезда не активнаЗвезда не активнаЗвезда не активнаЗвезда не активна
 

Не так давно передо мной встал вопрос, как из VBA (Visual Basic for Application), который используется в Excel, Word и других приложениях офиса от Microsoft, вызвать функцию из библиотеки, написанной на C++, и получить на выходе строку. Причём строка должна содержать кириллические символы и всё это должно работать как на 32-битных, так и на 64-битных системах Windows. Предлагаю здесь найденное решение, которое одинаково хорошо работает со всеми версиями офиса.

После изучения документации стало понятно, что для типа String в VBA используется кодировка ASCII. Причём первые 4 байта - это длина строки. Т.е. можно конечно создать строку, которую поймёт Excel, и вернуть её, но я нигде не нашёл ничего про соглашение, кто должен удалять созданный буфер со строкой и каким образом. Поэтому так лучше не делать.

Лучше возвращать строки так, как это принято делать в C: наша функция должна принять на вход указатель на буфер, куда нужно скопировать строку, и размер этого буфера, затем скопировать строку в буфер и вернуть количество скопированных символов. Причём, если нам передали длину буфера 0, то мы должны только посчитать и вернуть размер строки, не записывая ничего в буфер. Вот как это будет выглядеть (пример сделан на Embarcadero C++Builder):

extern "C" int __export _stdcall get_text(char text_out[], int text_len)
{
    //Предположим, что мы работаем со строками с помощью AnsiString.
    AnsiString s = "Тестовая строка с русскими буквами!";
    //Вычисляем размер возвращаемой строки.
    int len = text_len == 0 || s.Length() < text_len ? s.Length() : text_len;
    if (text_len != 0 && text_out)
        //Если указаны указатель на буфер и его размер, то копируем в буфер в строку.
        strncpy(text_out, s.c_str(), len);
    //Возвращаем длину скопированной строки.
    return len;
}

Вызвать такую функцию из VBA можно двумя способами. Первый способ – это когда мы примерно предполагаем, что длина строки не может быть больше какого-то размера. Или нам достаточно считать только начало строки. В этом случае можем не запрашивать размер строки, а сразу пытаться получить строку нужного размера. Макрос в Excel будет выглядеть так:

Declare Function get_text Lib "C:\Projects\LibTest\Win32\Debug\Project2.dll" (ByVal text_out As String, ByVal text_len As Integer) As Integer
Dim text_out As String
Dim text_len As Integer
 
Sub Макрос1()
    'Создаём строку длиной 1000 символов.
    text_out = Space(1000)
    'Вызываем функцию и в ответ получаем количество символов в возвращённой строке.
    text_len = get_text(text_out, Len(text_out))
    'Обрезаем строку до нужной длины.
    text_out = Left(text_out, text_len)
    'Выводим результат в ячейку.
    ActiveCell.FormulaR1C1 = text_out
End Sub

Второй способ – это определение размера строки и только потом её получение. В этом случае нашу функцию get_text придётся вызывать два раза. Вот пример макроса в Excel:

Sub Макрос2()
    'Чтобы функция только посчитала длину строки, передаём в последнем параметре 0.
    text_len = get_text(text_out, 0)
    'Создаём строку определённой длины.
    text_out = Space(text_len)
    'Вызываем функцию, чтобы получить строку.
    text_len = get_text(text_out, text_len)
    'Выводим результат в ячейку.
    ActiveCell.FormulaR1C1 = text_out
End Sub

Tags: VBA C++ Учебники по программированию Visual Basic

Добавить комментарий


Защитный код
Обновить