Я пытаюсь создать DLL с помощью Visual C ++, который вызывается из программы Delphi 5. Программа Delphi передает запись, которая затем редактируется в DLL, а программа Delphi использует результаты.
Например, код Delphi похож на следующее:
Type dll_btvar = record
age : smallint;
name : array[0..11] of char;
value : Double;
end;
// Import the function from the dll
function foo(CVars : dll_btvar):integer; external 'example.dll';
// Call the dll
function callFoo(var passedVar:dll_btvar):integer;
begin
result := foo(passedVar);
// Use passedVar.value
end;
Пример кода C ++:
В примере .h:
#pragma once
#include "dllVar.h"extern "C" {
__declspec(dllexport) int foo(DLL_Var var);
}
В примере .cpp:
#include "example.h"int foo(DLL_Var var){
var.value = var.age + var.name[0];
return 0;
}
В dllVar.h:
#pragma once
#pragma pack(8)
extern "C" {
struct DLL_Var {
short age;
char name[12];
double value;
}
}
я использую #pragma pack(8)
поскольку это значение дало правильное выравнивание, чтобы переданная запись была правильно прочитана в DLL.
В примере кода при передаче возраста и имени я ожидаю, что значение будет установлено библиотекой DLL, которая затем может быть восстановлена в программе Delphi. Результатом будет какой-то код ошибки.
Использование идентичного кода в C ++ Builder 5 сработало, однако оно, конечно, устарело, и я не переместил весь код в моей DLL (и не хочу), только тот минимум, который вы видите здесь.
Я протестировал несколько способов, чтобы Delphi передавал адрес / указатель на dll, однако они ничего не изменили.
Прямо сейчас возвращаемое значение отправляется правильно, но поля записи (то есть значение) остаются неизменными.
Какие изменения мне нужно внести в Delphi или C ++, чтобы зафиксировать изменения в переданной записи? Я рад, что интенсивно работаю с C ++, но я бы предпочел свести изменения Delphi к минимуму, поскольку это старое программное обеспечение, которое я не хочу ломать.
function foo(CVars : dll_btvar):integer; external 'example.dll';
Проблема начинается здесь, в коде Delphi. Запись передается по значению. То есть переменная записи вызывающей стороны копируется в новую переменную, которая затем передается функции. Это означает, что изменения, вызываемые вызываемым пользователем в эту копию записи, не видны вызывающим абонентом. Поэтому вы должны передать параметр как var
параметр:
function foo(var CVars : dll_btvar):integer; external 'example.dll';
Следующая проблема — соглашение о вызовах. Вы должны использовать одно и то же соглашение о вызовах для обеих сторон. Ваш код использует по умолчанию register
соглашение на стороне Delphi, которое не поддерживается инструментами не Borland / Embarcadero. использование stdcall
или же cdecl
вместо. Давайте выберем cdecl
по умолчанию для большинства инструментов C ++:
function foo(var CVars : dll_btvar):integer; cdecl; external 'example.dll';
Чтобы обеспечить соответствие кода C ++, передайте аргумент по ссылке:
__declspec(dllexport) int __cdecl foo(DLL_Var &var);
Или явно используйте указатель:
__declspec(dllexport) int __cdecl foo(DLL_Var *var);
В последнем варианте, реализация должна быть изменена из-за использования указателя:
int foo(DLL_Var *var){
var->value = var->age + var->name[0];
return 0;
}
Использование идентичного кода в C ++ Builder 5 сработало.
Нет, это не так, потому что код Delphi в вашем вопросе не может изменить запись звонящего.
Других решений пока нет …