11

C/C++ DLL を作成していて、このような .def ファイルを使用する前に実行した特定の関数をエクスポートしたい

LIBRARY "MyLib"
EXPORTS
  Foo
  Bar

たとえば、次のように定義されたコードを使用します。

int Foo(int a);
void Bar(int foo);

ただし、次のように Foo() のオーバーロードされたメソッドを宣言したい場合はどうなりますか。

int Foo(int a, int b);

def ファイルには完全なプロトタイプではなく関数名しかないため、オーバーロードされた関数をどのように処理するかわかりません。適切にプロトタイプ化された関数ポインターを LoadLibrary() に渡すときに、1 つのエントリを使用して、オーバーロードされたバージョンを指定しますか?

編集:明確にするために、これはVisual Studio 2005を使用したWindows上です

編集:非定義(__declspec)メソッドを答えとしてマークしました...これで実際にdefファイルを使用しても問題が解決しないことはわかっていますが、defファイルを使用した(公式の)解決策はないようです。ただし、オーバーロードされた関数と定義ファイルがないことを誰かが知っている場合に備えて、質問を開いたままにします。

4

6 に答える 6

11

関数のオーバーロードは、名前マングリング (リンカ エラー メッセージ内の不可解な関数名) に依存する C++ の機能です。

マングルされた名前を def ファイルに書き込むことで、テスト プロジェクトをリンクして実行することができます。

LIBRARY "TestDLL"
EXPORTS
    ?Foo@@YAXH@Z
    ?Foo@@YAXHH@Z

のために働くようです

void Foo( int x );
void Foo( int x, int y );

そのため、エラー メッセージから C++ 関数名をコピーして、def ファイルに書き込みます。ただし、実際の問題は次のとおりです。なぜ def ファイルを使用し、 __declspec(dllexport) を使用しないのですか?

壊れた名前は移植できません。VC++ 2008 でテストしました。

于 2008-08-25T14:42:03.720 に答える
10

コード自体で、__declspec(dllexport) を使用して、エクスポートする関数をマークします。例えば:

#define DllExport __declspec(dllexport)

int DllExport  Foo( int a ) {
  // implementation
}
int DllExport Foo( int a, int b ) {
  // implementation
}

これを行う場合、.def ファイルに関数をリストする必要はありません。

または、次のようなデフォルトのパラメーター値を使用できる場合があります。

int Foo( int a, int b = -1 )

これは、未使用であることを示すために使用できる b の値が存在することを前提としています。-1 が b の正当な値である場合、またはデフォルトが存在しないか存在すべきでない場合、これは機能しません。

編集(Adam Haile):__dllspecが正しくないため、__declspecを使用するように修正したため、これを公式の回答としてマークできました...十分に近かったです。

編集 (Graeme): おっと - タイプミスを修正してくれてありがとう!

于 2008-08-25T14:32:33.180 に答える
8

私も似たようなことがありましたので、こちらも投稿したいと思います。

  1. 通常使用

    extern "C" __declspec(dllexport) void Foo();
    

    関数名をエクスポートすることは問題ありません。通常 .def ファイルを必要とせずに名前を分解せずにエクスポートします。ただし、__stdcall 関数やオーバーロードされた関数名など、いくつかの例外があります。

  2. __stdcall 規則を使用する関数を宣言すると (多くの API 関数で行われているように)、

    extern "C" __declspec(dllexport) void __stdcall Foo();
    

    _Foo@4 のようなマングルされた名前をエクスポートします。この場合、エクスポートされた名前を内部のマングル名に明示的にマップする必要がある場合があります。

A. マングルされていない名前をエクスポートする方法。.def ファイルに追加

----
EXPORTS
    ; Explicit exports can go here

    Foo
-----

これは、内部関数 Foo の「最適な一致」を見つけてエクスポートしようとします。上記の foo が 1 つしかない場合、これによりマッピングが作成されます。

Foo = _Foo@4

dumpbin /EXPORTS で確認できるように

関数名をオーバーロードした場合は、entryname[=internalname] 構文を使用してマングルされた名前を指定して、.def ファイルで必要な関数を明示的に指定する必要がある場合があります。例えば

----
EXPORTS
    ; Explicit exports can go here

    Foo=_Foo@4
-----

B. .def ファイルの代わりに、#pragma を使用して「その場で」名前をエクスポートできます。

#pragma comment(linker, "/export:Foo=_Foo@4")

C. 3 番目の方法は、Foo の 1 つのバージョンのみを extern "C" として宣言し、マングルなしでエクスポートすることです。詳しくはこちらをご覧ください。

于 2008-12-04T21:03:01.843 に答える
3

dll インターフェースは C API であるため、希望することを行う公式の方法はありません。

コンパイラ自体は、マングルされた名前を回避策として使用するため、コードをあまり変更したくない場合は、名前のマングリングを使用する必要があります。

于 2008-08-27T11:11:20.340 に答える
2

マングリング規則はコンパイラのリリースごとに変更される可能性があるため、オーバーロードされた関数をエクスポートする言語やバージョンに依存しない方法はありません。

これが、ほとんどの WinXX 関数に *Ex や *2 などの変な名前が付いている理由の 1 つです。

于 2008-08-25T15:54:32.673 に答える
2

EXPORTS 定義の Systax は次のとおりです。

entryname[=internalname] [@ordinal [NONAME]] [PRIVATE] [DATA]

entrynameは、エクスポートする関数または変数の名前です。これは必須です。エクスポートする名前が DLL 内の名前と異なる場合は、DLL 内のエクスポートの名前を internalname で指定します。

たとえば、DLL が関数 func1() をエクスポートし、それを func2() として使用する場合は、次のように指定します。

EXPORTS
func2=func1

(Dependency walker を使用して) マングルされた名前を確認し、独自の関数名を指定するだけです。

ソース: http://msdn.microsoft.com/en-us/library/hyx1zcd3(v=vs.71).aspx

編集: これは動的 DLL で機能します。動的 DLL では、GetProcAddress() を使用して Dll の関数を明示的にフェッチする必要があります。

于 2013-03-21T06:15:10.647 に答える