在 Delphi 中从 C++ DLL 调用函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10405991/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Calling functions from a c++ DLL in Delphi
提问by Steven smethurst
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. The test application build in VS2010 could call the c++ DLL and get the expected result.
然后我用 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;
}
Next I attempted to build a new project in Delphi 7 to call this C++ DLL. I used this tutorialto help me build the new project.
接下来我尝试在 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.
The result from the Delphi 7 project is 6155731When I expected 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.
Delphi 7 项目的结果是6155731当我预期5。我检查了结果的二进制文件,认为它可能与数据类型有关,但对我来说它看起来是随机的。当我重新编译/重新运行应用程序时,它每次都会得到相同的结果。
I do not know a lot about Delphi this is the first time I have deal with it and i find it confusing.
我对 Delphi 了解不多,这是我第一次处理它,我觉得它很混乱。
Any suggestion on what to check next?
关于接下来要检查的内容有什么建议吗?
回答by David Heffernan
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. The cdecl
calling convention passes parameters on the stack and so this mis-match explains why communications between the two modules fail.
您的代码使用默认的 Delphi 调用约定,即register
通过寄存器传递参数。该cdecl
调用约定传递堆栈,所以这种不匹配解释了为什么在两个模块之间的通信失败参数。
Some more comments:
还有一些评论:
The failure mode for LoadLibrary
is to return NULL
, that is 0
. Check that rather than the return value being >=32
.
的失败模式LoadLibrary
是返回NULL
,即0
。检查,而不是返回值是>=32
.
It's simpler to use implicit linking to import this function. Replace all the LoadLibrary
and GetProcAddress
code with this simple declaration:
使用隐式链接导入此函数更简单。用这个简单的声明替换所有LoadLibrary
和GetProcAddress
代码:
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.
系统加载程序将在您的可执行文件启动时解析此导入,因此您不必担心链接的细节。
回答by Steve Swallow
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. The above code doesn't work in this case, but use the name attribute of the external declaration to fix the problem:
在 RAD Studio Berlin 上,将 CLANG 编译器用于 C++ 部分,extern "C" 的 cdecl 函数将在其名称前面加上下划线,传统的 unix "C" 样式。上面的代码在这种情况下不起作用,而是使用外部声明的 name 属性来解决问题:
function DoMath(X, Y: Integer): Integer; cdecl; external 'exampleDLL.dll' name '_DoMath';
函数 DoMath(X, Y: Integer): Integer; cdecl; 外部“exampleDLL.dll”名称“_DoMath”;
Not tried it with other compilers, so it might be a general issue with 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.
没有在其他编译器上尝试过,所以这可能是 cdecl 的一个普遍问题。Windows API 不使用 cdecl,但使用与 Delphi 相同的调用约定,例如,DLL 函数的 Winapi.Windows 声明没有添加下划线。
Same true if using GetProcAddress, the correct call is GetProcAddress(hDLL, '_DoMath'); otherwise nil is returned.
如果使用 GetProcAddress 也是如此,正确的调用是 GetProcAddress(hDLL, '_DoMath'); 否则返回 nil。
Hope this helps anyone struggling to get Delphi talking to C++.
希望这可以帮助任何努力让 Delphi 与 C++ 交谈的人。