12

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プロジェクトの結果は、私が5を期待したときの6155731です。結果のバイナリをデータ型と関係があるのではないかと考えて確認しましたが、ランダムに見えます。アプリケーションを再コンパイル/再実行すると、毎回同じ結果が得られます。

Delphiについてはよくわかりませんが、これを扱ったのはこれが初めてで、混乱を招きます。

次に何をチェックするかについての提案はありますか?

4

2 に答える 2

18

呼び出し規約を指定する必要があります。この場合は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';

システムローダーは、実行可能ファイルの起動時にこのインポートを解決するため、リンクの詳細について心配する必要はありません。

于 2012-05-02T00:01:46.857 に答える
0

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++と通信するのに苦労している人に役立つことを願っています。

于 2016-11-30T10:56:54.577 に答える