Вызов функций из C++ DLL в Delphi
Я создал новый C++ DLL проект в VS2010, который предоставляет 1 функцию
#include "stdafx.h"
#define DllImport extern "C" __declspec( dllimport )
#define DllExport extern "C" __declspec( dllexport )
DllExport int DoMath( int a, int b) {
return a + b ;
}
Затем я создал приложение C++ с VS2010, чтобы проверить эту DLL. Сборка тестового приложения в VS2010 может вызвать библиотеку C++ и получить ожидаемый результат.
#include "stdafx.h"
#include <windows.h>
typedef int (*DoMath)(int, int) ;
int _tmain(int argc, _TCHAR* argv[])
{
HMODULE hMod = LoadLibrary ("exampleDLL.dll");
if (NULL != hMod) {
DoMath mf1 = (DoMath) GetProcAddress(hMod,"DoMath");
if( mf1 != NULL ) {
printf ("DoMath(8,7)==%d \n", mf1(8,7) );
} else {
printf ("GetProcAddress Failed \n");
}
FreeLibrary(hMod);
} else {
printf ("LoadLibrary failed\n");
return 1;
}
return 0;
}
Затем я попытался создать новый проект в Delphi 7, чтобы вызвать эту C++ DLL. Я использовал этот учебник, чтобы помочь мне построить новый проект.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TmyFunction = function(X,Y: Integer):Integer;
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
procedure FormShow(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
hDll: THandle;
end;
var
Form1: TForm1;
fDoMath : TmyFunction;
implementation
{$R *.dfm}
procedure TForm1.FormShow(Sender: TObject);
begin
hDll := LoadLibrary('exampleDLL.dll');
if HDll >= 32 then { success }
begin
fDoMath := GetProcAddress(hDll, 'DoMath');
end
else
MessageDlg('Error: could not find exampleDLL.DLL', mtError, [mbOk], 0)
end;
procedure TForm1.Button1Click(Sender: TObject);
var i: Integer;
begin
i := fDoMath(2,3);
edit1.Text := IntToStr(i);
end;
end.
Результат проекта Delphi 7 - 6155731 Когда я ожидал 5. Я проверил двоичный файл результата, думая, что он может иметь отношение к типу данных, но для меня это выглядит случайным образом. Когда я перекомпилирую / перезапускаю приложение, оно каждый раз получает один и тот же результат.
Я не знаю много о Delphi, это первый раз, когда я имею дело с этим, и я нахожу это запутанным.
Любое предложение о том, что проверить дальше?
2 ответа
Вам необходимо указать соглашение о вызовах, которое в данном случае cdecl
:
TMyFunction = function(X, Y: Integer): Integer; cdecl;
Ваш код использует соглашение о вызовах Delphi по умолчанию, которое register
и передает параметры через регистры. cdecl
соглашение о вызовах передает параметры в стек, и поэтому это несоответствие объясняет, почему происходит сбой связи между двумя модулями.
Еще несколько комментариев:
Режим сбоя для LoadLibrary
это вернуться NULL
, то есть 0
, Проверьте это, а не возвращаемое значение >=32
,
Проще использовать неявные ссылки для импорта этой функции. Заменить все LoadLibrary
а также GetProcAddress
код с этим простым объявлением:
function DoMath(X, Y: Integer): Integer; cdecl; external 'exampleDLL.dll';
Системный загрузчик разрешит этот импорт при запуске исполняемого файла, поэтому вам не придется беспокоиться о деталях компоновки.
В RAD Studio Berlin, с использованием компилятора CLANG для части C++, функции cdecl, которая находится за пределами "C", будет добавлено имя в традиционном стиле Unix "C" с подчеркиванием. Приведенный выше код в этом случае не работает, но для устранения проблемы используйте атрибут name внешнего объявления:
function DoMath (X, Y: Integer): Integer; Cdecl; внешнее 'exampleDLL.dll' имя '_DoMath';
Не пробовал с другими компиляторами, так что это может быть общая проблема с cdecl. Windows API не использует cdecl, но использует то же соглашение о вызовах, что и Delphi, поэтому, например, в объявлениях функций DLL Winapi.Windows не добавлено подчеркивание.
То же верно, если используется GetProcAddress, правильный вызов - GetProcAddress(hDLL, '_DoMath'); в противном случае ноль возвращается.
Надеюсь, это поможет любому, кто пытается заставить Delphi общаться с C++.