При работе с внешними библиотеками, написанными на C++, в среде Delphi могут возникать различные проблемы, связанные с различиями в соглашениях вызова функций и управлением памятью. В данной статье рассмотрим типичную проблему, с которой сталкиваются разработчики при использовании кодирующей функции из C++ DLL в Delphi 6, и предложим решение.
Описание проблемы
Разработчику необходимо использовать функцию кодирования из внешней C++ библиотеки DLL в проекте на Delphi 6. Функция имеет следующий прототип:
long <Function Name>(char *Data, long &Apply, char *ReturnVal, long &Size);
где Data - входные данные, Apply - булево значение (по умолчанию: FALSE), ReturnVal - возвращаемое значение из DLL, а Size - длина возвращаемого значения.
В коде Delphi 6 разработчик написал следующий код для вызова этой функции:
implementation
const
EncoderDLL = '<DLL NAME>';
FunctionName = 'FUNCTION NAME';
var
_TEST : function(const Data: PChar; Apply: PInteger; stOutput: PChar; iSize: PInteger): integer; stdcall;
...
...
var
stInput, stOutput: string;
iLength, i1, iResult: integer;
hnd: THandle;
begin
iLength := 0;
i1 := 0;
stInput := Trim(edtInput.Text);
hnd := SafeLoadLibrary(EncoderDLL);
if hnd > 0 then
begin
@_TEST := GetProcAddress(hnd, FunctionName);
if @_TEST <> nil then
begin
iResult := _TEST(PChar(stInput), @i1, PChar(stOutput), @iLength); // Ошибка: Access Violation
end;
end;
FreeLibrary(hnd);
end;
При вызове функции возникает ошибка доступа (Access Violation) на строке с комментарием "Ошибка". Если вместо PChar в объявлении функции использовать string, то ошибка доступа появляется при освобождении библиотеки, но при этом значение параметра iLength заполняется корректно.
Решение проблемы
Для решения проблемы необходимо учесть следующие моменты:
Параметр Apply в C++ является ссылкой, и в Delphi он должен быть объявлен как var Apply: Integer.
Соглашение о вызовах (calling convention) должно соответствовать тому, что используется в C++ DLL. В данном случае, если в документации C++ не указано иное, следует использовать stdcall.
Необходимо выделить память для stOutput перед вызовом функции. Для этого следует использовать двойной вызов функции: сначала узнать необходимый размер буфера, затем выделить память и вызвать функцию снова для получения результата.
Вызов функции должен быть выполнен дважды: первый раз для получения размера возвращаемого значения, второй раз для получения самого значения, после выделения соответствующего буфера.
Пример кода с учетом этих моментов:
implementation
const
EncoderDLL = '<DLL NAME>';
FunctionName = 'FUNCTION NAME';
var
_TEST: function(const Data: PChar; var Apply: Longint; ReturnVal: PChar; var Size: Longint): Longint; stdcall;
...
...
var
stInput, stOutput: string;
iLength, i1, iResult: integer;
hnd: THandle;
buffer: array of Char;
begin
iLength := 0;
i1 := 0;
stInput := Trim(edtInput.Text);
hnd := SafeLoadLibrary(EncoderDLL);
if hnd > 0 then
begin
@_TEST := GetProcAddress(hnd, FunctionName);
if @_TEST <> nil then
begin
iResult := _TEST(PChar(stInput), @i1, nil, @iLength); // Первый вызов для получения размера
SetLength(buffer, iLength); // Выделение памяти
iResult := _TEST(PChar(stInput), @i1, @buffer[0], @iLength); // Второй вызов для получения результата
stOutput := buffer;
end;
end;
FreeLibrary(hnd);
end;
Таким образом, учитывая особенности работы с внешними библиотеками и правильно выделив память для возвращаемых данных, можно успешно интегрировать C++ DLL в проект на Delphi 6.
Проблема связана с интеграцией C++ библиотеки DLL в среду Delphi 6, где возникли трудности с кодированием и соглашениями вызова функций при работе с внешними функциями, требующими корректного управления памятью и вызова по правильным соглашениям вызовов.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.