VS2005 C++のコンソールで、DLLと
それを動的にリンクするプログラムを作っています。
DLL側で、
extern C __declspec(dllexport) int GetPlus(int a, int b);
int GetPlus(int a, int b){return (a+b);}
本体(DLLを呼ぶ)側で、
typedef int (*ProcGetPlus)(int, int);
とすると、問題なく希望どおりに動きました。よかった!
ここまで来るのもやっとだったので...。
//-----------------------------
次に、__stdcall を付けてみました。
DLL側で、
extern C __declspec(dllexport) int __stdcall GetPlus(int a, int b);
int __stdcall GetPlus(int a, int b){return (a+b);}
本体(DLLを呼ぶ)側で
extern C __declspec(dllimport) int __stdcall GetPlus(int a, int b);、
typedef int (*ProcGetPlus)(int, int);
とすると、すべてリビルドは成功するのですが、
実行すると LoadLibrary(...) は成功、しかし
GetProcAddress(...) から NULL が返って来て
ダメです。
//------------------------
1.typedef に __stdcall が付いていないのが原因だろうと考え
typedef int (*ProcGetPlus)(int, int);
typedef int __stdcall (*ProcGetPlus)(int, int); // 構文エラー
typedef int (__stdcall *ProcGetPlus)(int, int);
書き直してみたのですが、ビルドはできてもNULL が返って来ます。
2.本体側の、
PPLUS GetPlus;
GetPlus=(ProcGetPlus)GetProcAddress(hDll, GetPlus);
なども、1.といろいろ組み合わせて書き直してみたのですが、ダメでした。
どこが悪いのでしょうか? 原因を教えていただきたいので
投稿しました。 よろしくお願いします。
defファイルかいてる?
Dependency Walkerでスペルチェックしてごらん。
GetProcAddressが失敗するのは名前が違うからかもしれない。
質問文を訂正します。
2.本体側の、
PPLUS GetPlus;
は、
2.本体側の、
ProcGetPlus GetPlus;
です。
まだ解決していません。
DLL作成時にdefファイルを使わないと、DLLの関数名がGetPlusとは限りません。
VC6での方法ですが、基本的にはほぼ一緒ですので
http://rararahp.cool.ne.jp/vc/vctips/dll.htm
を参考にしてください。
VS2005の場合defファイルの指定は
メニューバーのプロジェクトのプロパティ
↓
ダイアログの、左のツリーの構成プロパティ
↓
リンカ
↓
入力
↓
右のリストビューのモジュール定義ファイル
でできます。
wclrp ( 'o') さんありがとうございます。
>> defファイルかいてる?
かいていません。見たこともありません。
defファイル はどこにあるのだろう?
>> Dependency Walkerでスペルチェックしてごらん
Dependency Walker はツールのようですが、も初めて聞く名前なので
調べてみます。
まずは、ソースコード上のスペルをもう一度確認してみます。
そーいう問題ぢゃなくって
ソースコード上の関数名 GetPlus であっても
GetProcAddress に渡すべきシンボル名が GetPlus ぢゃなくなる場合がある
って意味。
DLL 上では GetPlus@8 とかなっている可能性がある→
名前修飾による変化を Dep.Walker とか dumpbin とかで調べるか、または
def ファイルで名前修飾を自分で行うか、のどちらかを対処すべし
ということ
__stdcallの説明をよく読みましたか?
正に
> DLL 上では GetPlus@8 とかなっている可能性がある
という話が出ていますけれど。
説明を読むとわかりますが、実際には上の例とはちょっと違いますね。
あー、英語でもちゃんと読みましょうね。辞書を使って単語を引けば、
十分読めると思いますし。文法的にはそんなに難しい英文ではありません。
多分、義務教育の範囲で読めるはずです。
そもそも何故、__stdcallをつけたのかも良く分からないし。
何か意味があってやったのであれば、__stdcallを付けた意図も説明するべきかと。
まず、
http://rararahp.cool.ne.jp/vc/vctips/dll.htm
の
DEFファイルを使い、「暗黙的リンク」の方式で作ってみたらどうですか?
最初から難しい方法でトライしてもうまくいかないと思う。
DEF ファイルを作り実行するとうまく動きました。
1.Dependency Walker(以下DW) で調べると 関数名が _GetPlus@8 だった。リンクで
きない理由がわかった。GetProcAddress(_GetPlus@8); ではNULLが返らないが実行
途中で「仕様と異なる呼び出し」との内容のデバッグエラー。
2.DEF ファイルを使うと関数名が GetPlus に変更されているのがDWで確認できて、実
行もできた。
これで __stdcall 付きでもDLLの動的リンクが実現できた。とりあえず、目的は達
成しました。しかし、
extern C declspec(dllexport) int __stdcall GetPlus(int, int);
int __stdcall GetPlus(int a, int b){ return (a+b); }
のDLLのGetPlus.cpp於いて、
3.らららさんのHPだと、
1. extern C ... 2.DEF ファイルを使う
のどちらでもよいはずなのに、VS2005 VC++ で1.がだめだったのは不思議。
この場合だけなのだろうか?
そこで、
4.BC++Builder6 にソースコードをコピーして試した。
1.extern C ... では正常に実行できた。(そうだ、これでよい!)
2.DEF ファイルは何の役割を果たしていないことがわかった。
(コンパイラの仕様だから仕方がない。)
5.BC++Builder6 の場合DLLの関数名を DW で調べてみた。
DLLを作るGetPlus.cppのファイルで
extern 行をコメントした場合、しない場合の二通りビルドして関数名を調べた。
予想は、前者が_GetPlus@8, 後者が GetPlus だが、実際は、
関数名は共に GetPlus だった。
ヘルプファイルには、__stdcall とすると名前修飾して_GetPlus@8のようにするとあ
る。しかし、それがコンパイラー内部だけでそうするなのか、あるいは実行ファイル
(*.exe や *.dll)で 必ずそのようなバイナリコードを出力するものなのかは、ヘル
プに明示的に書いていない。
どうも、前者のようだが、本当はどちらなのだろう? 初心者には難しい。
はじめに質問投稿した目的は達成できましたが、おかしな点をご指摘いただければ幸い
です。皆さん回答ありがとうございました。
2-3日後にまとめを書いて終りにする予定です。
> ヘルプファイルには、__stdcall とすると名前修飾して_GetPlus@8のようにすると
> ある。しかし、それがコンパイラー内部だけでそうするなのか、あるいは実行ファイル
> (*.exe や *.dll)で 必ずそのようなバイナリコードを出力するものなのかは、
> ヘルプに明示的に書いていない。
> どうも、前者のようだが、本当はどちらなのだろう? 初心者には難しい。
この辺は、コンパイルされた物がどんな風にリンクされるとかそういう理屈も
必要になるので納得するまで調べるならそういう部分も勉強した方が良いと思います。
逆に言うとこの辺の云々をきちんと理解する為にはその辺の知識も必要という事に
なります。
で、既にやっている事は初心者云々と言うレベルではないと思いますので
地道に勉強するしかないですね。IDEが提供している簡単な方のリンク方法なら
その辺は全く意識する必要がないのですけれど、既にその先に進んでいるわけですし。
こういう部分を解決する為には単純にプログラミング言語の文法だけを理解して
いれば良いというわけにはいかなくなりますので、色々勉強される事をお勧めします。
> ヘルプファイルには、__stdcall とすると名前修飾して_GetPlus@8のようにするとあ
る。
> しかし、それがコンパイラー内部だけでそうするなのか、あるいは実行ファイル
> (*.exe や *.dll)で 必ずそのようなバイナリコードを出力するものなのかは、
> ヘルプに明示的に書いていない。
CやC++といった言語はその辺を規定していないし、WindowsもDLLの仕様としては名前を
定めていない。
その辺を規定するのはコンパイラとリンカの仕様なので、VC++とBC++では違うことも当
然あり得る。
defファイルを使わない方法でやってみるのは如何でしょう?より単純にできますよ。
AppWizardで、win32プロジェクトからアプリケーションの設定タブでDLLを選び、
追加のオプションでシンボルのエクスポートにチェックを入れて完了ボタンを押しま
す。(VS2003/2005で出来る事を確認)
それで生成されたソースを眺めてみて下さい。それで真の暗黙的リンクDLLの作り方と
使い方が分かると思います。とても小さなソースなので分かり易いはずです。
defファイルを書く方法は、今時あまりないかと思います。上記がより良い方法の為で
す。MSの公式情報(MSDNだったかな?)にも「インポートライブラリを使う暗黙的リンクが
多い」と書いてありました。
下らない事に時間をとられて大変でしょうが、未知の事に手を出す時はどんな作業でもそ
んなもんです。頑張って下さい。
> defファイルを使わない方法でやってみるのは如何でしょう?より単純にできますよ。
えーっと,スレッドをちゃんと読んでください。
VS2003でやってみましたが,defファイルを使う方法の代替にはまったくなりません。
# つーか,単に__declspec(dllexport)しているだけ。
>より単純にできますよ。
ヘッダを共有する場合(そもそもしないと駄目な気もするのだが)、__declspecの指
定がインポート側とエクスポート側で違うのでその辺りも考慮する必要があり、さ
らにコンパイラ依存の名前修飾が起きてしまっては、単純とは程遠いと思います。
>defファイルを書く方法は、今時あまりないかと思います。上記がより良い方法
の為です。
>MSの公式情報(MSDNだったかな?)にも「インポートライブラリを使う暗黙的リン
クが多い」と書いてありました。
MS「推奨」って事だと思いますが。
インポートライブラリではDLLの存在確認が出来ないなどの問題もあるので自前
でGetProcするのは必要とも言えますし、ライブラリファイルの互換性も有ります。
そもそもMSのDirectXでさえその方法で使うDLLの互換性に問題が出ています
(DirectX)。
ミスをすれば命取りだが、いつでも確実に柔軟に扱えるDEFファイルでのエクス
ポートの制御を捨てるのはどうかと思います。
それと普及率ですが、個人的にはエンドユーザ向けの普及率の高いDLLはDEF
ファイルなどで、名前修飾を避けるものが多いと思います。
そもそも動的にリンクするライブラリをそんなことしていては利点も半減では無いで
しょうか。
皆さんのアドバイスと教えていただいたくつかのHPの解説を読んで、
何とかDLLを作れるようになりました。最後の私のまとめとして、
HPに書いてあることとちょっと違っていた(記述がなかった)こと二つ
を書きます。
//------------------------------------------------
1.Digital Mars C/C++(エディタの日本語が文字化けするなど不便)の場合:
DEFファイルを使ってDLLを作ることができた。DMC では、
DEF ファイルが元もと、どのプロジェクトにも使われているが、
[Project Settings]->[Build]->[Linker]->[Imports/Exports]ダイアログで設定すると
すべて自動的にDEFファイルに書き込んでくれて、DLL内の関数名も自由に設定できる
ことがわかった。
この場合、ソースコードが省略できて、
DLLを呼ぶ側の extern C __declspec(dllimport) ... と、
呼ばれる(DLL)側の extern C __declspec(dllexport) ...,
さらに、#include <windows.h>,
DllMain(...){...}
が省略できる。
記述するのは、DLLを呼ぶ側の
typedef int (*ProcGetPlus)(int, int);
だけでよい。
DLLなのにまるで普通の関数のような簡単なソースコードになってしまい驚いた。
この点が、HPに書いてあることとちょっと違っていた。
さらに、DLLから別のDLLを呼ぶ(Importsする)設定もできて実現した。
DEFファイルをつかうと、あまりにもソースコードが簡単になった。
もしかしたら、DLLができているのではなくて、main()の *.exe の中に
DLL用の関数が普通の関数として取り込まれているだけではないかと心配になった。
そこで、デスクトップにmain()の *.exe を取り出して実行してみた。
DLLがあるときだけエラーが出ずに動いた。安心した!
//---------------------------------
2.GetProcAddress(hDll, GetPlus)の第二引数 GetPlus つまり、
DLLのバイナリコード上の関数の名前の調べ方。
Dependency Walker, dumpbin.exe, dump.exe の他に、
MAP ファイルからも読み取ることができる。
dumpbin.exe はMAPファイルを作成するためのソフトのような気がします。
dump.exe を除くと、これらは全部私にとって初めて活用するものばかりでした。
//----------------------------
投稿をしてまもなくwclrp ( 'o')さんから的確な指摘をいただいたこと、そして
BlueさんからDEFファイルの設定場所を具体的に教えていただいたことは、
その後の作業をスムーズに進めることに大変役立ちました。
連休中は、投稿後たくさんの方から回答をいただいたことを
思い出しながら感激した気持で過ごしました。回答をくださった、
wclrp ( 'o'), Blue, tetrapod, PATIO, ITO, シャノン, しん, YuO, ぬ
の、皆さんありがとうございました。
//----------------------------