質問
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 つのエントリだけを使用して、必要なオーバーロードされたバージョンを指定しますか?
編集:念のために言っておきますが、これは Windows 上で Visual Studio 2005 を使用しているものです。
編集:非def (__declspec) メソッドを答えとしてマークしました...これが私が望んでいたようにdefファイルを使用して問題を実際に解決しないことはわかっていますが、おそらくdefファイルを使用した(公式)解決策はないようです。ただし、オーバーロードされた関数と def ファイルがないことを誰かが知っている場合に備えて、質問は未解決のままにしておきます。
解決
コード自体で、__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 の正当な値である場合、またはデフォルトが存在しないか、デフォルトであるべきではない場合、これは機能しません。
編集 (アダム・ヘイル):__dllspec が正しくなかったため、__declspec を使用するように修正されたため、これを公式の回答としてマークできます...十分に近かったです。
編集 (グレアム):おっと、私のタイプミスを修正していただきありがとうございます。
他のヒント
関数のオーバーロードは、名前マングリング (リンカー エラー メッセージ内の不可解な関数名) に依存する C++ 機能です。
壊れた名前を def ファイルに書き込むことで、テスト プロジェクトをリンクして実行できます。
LIBRARY "TestDLL"
EXPORTS
?Foo@@YAXH@Z
?Foo@@YAXHH@Z
のために働くようです
void Foo( int x );
void Foo( int x, int y );
したがって、エラー メッセージから C++ 関数名をコピーし、def ファイルに書き込みます。しかし、本当の疑問は次のとおりです。__declspec(dllexport) を使用せずに def ファイルを使用するのはなぜですか?
壊れた名前は移植できません。VC++ 2008 でテストしました。
私も同様の問題を抱えていたので、これについても投稿したいと思いました。
普段使っている
extern "C" __declspec(dllexport) void Foo();
関数名をエクスポートしても問題ありません。それは いつもの .defファイルを必要とせずに、名前をマングルでエクスポートします。ただし、__stdcall関数や過負荷の関数名などのいくつかの例外があります。
__stdcallコンベンションを使用する関数を宣言した場合(多くのAPI関数に対して行われます)、
extern "C" __declspec(dllexport) void __stdcall Foo();
_foo@4のようなマングルネームをエクスポートします。この場合、エクスポートされた名前を内部マングル名に明示的にマッピングする必要がある場合があります。
A.分解されていない名前をエクスポートする方法。.def ファイルに追加します
----
EXPORTS
; Explicit exports can go here
Foo
-----
これは、内部関数 Foo に「最も一致するもの」を見つけてエクスポートしようとします。上記の場合は1つのfooのみがある場合、これはマッピングを作成します
Foo = _Foo@4
dumpbin /EXPORTS で確認できるように
関数名をオーバーロードした場合、EntryName [= internalName] Syntaxを使用してマングル名を指定して、.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" として宣言し、マングルされずにエクスポートすることです。見る ここ 詳細については。
DLL インターフェイスは C API であるため、必要なことを実行する公式の方法はありません。
コンパイラー自体は回避策としてマングルされた名前を使用するため、コード内であまり変更したくない場合は、マングルされた名前を使用する必要があります。
マングリング規則はコンパイラのリリースごとに変わる可能性があるため、言語やバージョンに依存せずにオーバーロードされた関数をエクスポートする方法はありません。
これが、ほとんどの WinXX 関数が *Ex や *2 などのおかしな名前を持つ理由の 1 つです。
EXPORTS 定義の構文は次のとおりです。
entryname[=internalname] [@ordinal [NONAME]] [PRIVATE] [DATA]
エントリ名 エクスポートする関数または変数の名前です。これは必須です。エクスポートする名前が DLL 内の名前と異なる場合は、internalname を使用して DLL 内のエクスポートの名前を指定します。
たとえば、DLL が関数 func1() をエクスポートし、それを func2() として使用したい場合は、次のように指定します。
EXPORTS
func2=func1
(Dependency walker を使用して) マングルされた名前を確認し、独自の関数名を指定するだけです。
ソース: http://msdn.microsoft.com/en-us/library/hyx1zcd3(v=vs.71).aspx
編集:これは動的 DLL で機能し、Dll 内の関数を明示的にフェッチするには GetProcAddress() を使用する必要があります。