简体   繁体   English

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

[英]Calling functions from a c++ DLL in Delphi

I created a new c++ DLL project in VS2010 that exposes 1 function 我在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 ; 
}

I then created a C++ application with VS2010 to test this DLL. 然后我用VS2010创建了一个C ++应用程序来测试这个DLL。 The test application build in VS2010 could call the c++ DLL and get the expected result. 在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;
}

Next I attempted to build a new project in Delphi 7 to call this C++ DLL. 接下来,我尝试在Delphi 7中构建一个新项目来调用这个C ++ DLL。 I used this tutorial to help me build the new project. 我用这个教程来帮助我构建新项目。

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.

The result from the Delphi 7 project is 6155731 When I expected 5 . Delphi 7项目的结果是6155731当我预计5 I checked the binary of the result thinking it might have something to do with a data type but it looks random to me. 我检查了结果的二进制文件,认为它可能与数据类型有关,但对我来说它看起来是随机的。 When I recompile/rerun the application it gets the same result every time. 当我重新编译/重新运行应用程序时,它每次都会得到相同的结果。

I do not know a lot about Delphi this is the first time I have deal with it and i find it confusing. 我不太了解德尔福,这是我第一次处理它,我发现它令人困惑。

Any suggestion on what to check next? 关于接下来要检查什么的任何建议?

You need to specify the calling convention, which in this case is cdecl : 您需要指定调用约定,在本例中为cdecl

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

Your code uses the default Delphi calling convention which is register and passes parameters through registers. 您的代码使用默认的Delphi调用约定,该约定是register并通过register传递参数。 The cdecl calling convention passes parameters on the stack and so this mis-match explains why communications between the two modules fail. cdecl调用约定传递堆栈上的参数,因此这种不匹配解释了为什么两个模块之间的通信失败。


Some more comments: 还有一些评论:

The failure mode for LoadLibrary is to return NULL , that is 0 . LoadLibrary的失败模式是返回NULL ,即0 Check that rather than the return value being >=32 . 检查返回值是否>=32

It's simpler to use implicit linking to import this function. 使用隐式链接导入此函数更简单。 Replace all the LoadLibrary and GetProcAddress code with this simple declaration: 使用以下简单声明替换所有LoadLibraryGetProcAddress代码:

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

The system loader will resolve this import when your executable starts so you don't have to worry about the details of linking. 当您的可执行文件启动时,系统加载程序将解析此导入,因此您不必担心链接的详细信息。

On RAD Studio Berlin, using CLANG compiler for the C++ part, a cdecl function which is extern "C" will have its name prepended with an underscore, traditional unix "C" style. 在RAD Studio Berlin上,使用CLANG编译器作为C ++部分,一个外部“C”的cdecl函数的名称前缀为下划线,传统的unix“C”样式。 The above code doesn't work in this case, but use the name attribute of the external declaration to fix the problem: 上面的代码在这种情况下不起作用,但使用外部声明的name属性来解决问题:

function DoMath(X, Y: Integer): Integer; function DoMath(X,Y:Integer):整数; cdecl; CDECL; external 'exampleDLL.dll' name '_DoMath'; 外部'exampleDLL.dll'名称'_DoMath';

Not tried it with other compilers, so it might be a general issue with cdecl. 没有尝试过其他编译器,所以它可能是cdecl的一般问题。 The Windows API does not use cdecl, but uses same calling convention as Delphi so, for example, the Winapi.Windows declarations of DLL functions do not have the underscore added. Windows API不使用cdecl,但使用与Delphi相同的调用约定,因此,例如,DLL函数的Winapi.Windows声明没有添加下划线。

Same true if using GetProcAddress, the correct call is GetProcAddress(hDLL, '_DoMath'); 如果使用GetProcAddress,则同样如此,正确的调用是GetProcAddress(hDLL,'_ DoMath'); otherwise nil is returned. 否则返回nil。

Hope this helps anyone struggling to get Delphi talking to C++. 希望这有助于任何努力让Delphi与C ++交流的人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM