1つの関数を公開するVS2010で新しいc ++ DLLプロジェクトを作成しました
#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;
}
次に、このC++ DLLを呼び出すためにDelphi7で新しいプロジェクトをビルドしようとしました。私は このチュートリアル を使用して新しいプロジェクトを構築しました。
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についてはよくわかりませんが、これを扱ったのはこれが初めてで、混乱を招きます。
次に何をチェックするかについての提案はありますか?
呼び出し規約を指定する必要があります。この場合はcdecl
です。
TMyFunction = function(X, Y: Integer): Integer; cdecl;
コードは、デフォルトのDelphi呼び出し規約であるregister
を使用し、レジスタを介してパラメータを渡します。 cdecl
呼び出し規約はスタック上のパラメーターを渡すため、この不一致により、2つのモジュール間の通信が失敗する理由が説明されます。
さらにいくつかのコメント:
LoadLibrary
の失敗モードは、NULL
、つまり0
を返すことです。戻り値が>=32
ではなく、それを確認してください。
この関数をインポートするには、暗黙的なリンクを使用する方が簡単です。すべてのLoadLibrary
およびGetProcAddress
コードを次の単純な宣言に置き換えます。
function DoMath(X, Y: Integer): Integer; cdecl; external 'exampleDLL.dll';
システムローダーは、実行可能ファイルの起動時にこのインポートを解決するため、リンクの詳細について心配する必要はありません。
RAD Studio Berlinでは、C++部分にCLANGコンパイラを使用すると、extern "C"であるcdecl関数の名前の前に、下線付きの従来のUNIX "C"スタイルが追加されます。この場合は機能しませんが、外部宣言のname属性を使用して問題を修正してください。
関数DoMath(X、Y:整数):整数; cdecl;外部 'exampleDLL.dll'名 '_DoMath';
他のコンパイラでは試していませんので、cdeclの一般的な問題である可能性があります。 Windows APIはcdeclを使用しませんが、Delphiと同じ呼び出し規約を使用するため、たとえば、DLL関数のWinapi.Windows宣言には、アンダースコアが追加されていません。
GetProcAddressを使用する場合も同様で、正しい呼び出しはGetProcAddress(hDLL、 '_ DoMath');です。それ以外の場合はnilが返されます。
これが、DelphiがC++と通信するのに苦労している人に役立つことを願っています。