从Delphi中的c ++ DLL调用函数

时间:2012-05-01 23:58:54

标签: c++ delphi dll delphi-7

我在VS2010中创建了一个新的c ++ DLL项目,它暴露了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 ; 
}

然后我用VS2010创建了一个C ++应用程序来测试这个DLL。在VS2010中构建的测试应用程序可以调用c ++ DLL并获得预期的结果。

#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。我使用this tutorial来帮助我构建新项目。

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 个答案:

答案 0 :(得分:18)

您需要指定调用约定,在本例中为cdecl

TMyFunction = function(X, Y: Integer): Integer; cdecl;

您的代码使用默认的Delphi调用约定register并通过寄存器传递参数。 cdecl调用约定传递堆栈上的参数,因此这种不匹配解释了为什么两个模块之间的通信失败。


更多评论:

LoadLibrary的失败模式是返回NULL,即0。检查而不是返回值是>=32

使用隐式链接导入此功能更简单。使用以下简单声明替换所有LoadLibraryGetProcAddress代码:

function DoMath(X, Y: Integer): Integer; cdecl; external 'exampleDLL.dll';

系统加载程序将在您的可执行文件启动时解析此导入,因此您不必担心链接的详细信息。

答案 1 :(得分:0)

在RAD Studio Berlin上,使用CLANG编译器编写C ++部分,cdecl函数是extern&#34; C&#34;将其名称加上下划线,传统的unix&#34; C&#34;样式。上面的代码在这种情况下不起作用,但使用外部声明的name属性来解决问题:

function DoMath(X,Y:Integer):整数; CDECL;外部&#39; exampleDLL.dll&#39;姓名&#39; _DoMath&#39;;

没有尝试过其他编译器,所以它可能是cdecl的一般问题。 Windows API不使用cdecl,但使用与Delphi相同的调用约定,因此,例如,DLL函数的Winapi.Windows声明没有添加下划线。

如果使用GetProcAddress,则同样如此,正确的调用是GetProcAddress(hDLL,&#39; _DoMath&#39;);否则返回nil。

希望这有助于任何努力让Delphi与C ++交流的人。