トップ回答者
呼び出し規約と”LNK2001: 外部シンボル "_TIFFOpen@8" は未解決です。”との関係

質問
-
VS2008です。
MFCのDLL を作って仕事をしています。
昔、VB6.0から利用していたので、__stdcallとしています。
現在は、C#、または、C++から利用できれば良いです。
CのオープンソースをMFCのDLLの形で利用しようとしていますが、
DLLを呼び出し規約__cdeclでコンパイルし、
呼び出し側のテストプログラムも、__cdeclとしたら、チャント動いています。
しかし、Cのオープンソースを呼び出し規約__stdcallでコンパイルすると、
error C2440: '関数' : 'int (__stdcall *)(const void *,const void *)' から 'int (__cdecl *)(const void *,const void *)' に変換できません。
とコンパイルエラーになります。
逆に、利用側だけを、__stdcall とすると
error LNK2001: 外部シンボル "_TIFFOpen@8" は未解決です。
とリンクエラーになります。
どのように対応したら、良いでしょうか。
回答
すべての返信
-
とっちゃんさんの発言通りかと思われますが、
異なるケースも想定できますので補足しておきます。例えば、<Dll_Export_func.h>
#define EXPORTS_FUNC __declspec(dllexport) // 呼び出し規約定義なし
void EXPORTS_FUNC func(); //exportされる関数の様に、コード上関数の呼び出し規約の修飾が「されていない」場合は
1.DLLがコンパイルされてfunc()の実体がDLLに実装されるとき。
2.EXEがコンパイルされてDLLのfunc()関数を参照するとき。の各ケースのコンパイルオプション /Gd、/Gz によって関数名称は次の様に変換されます。
3.__cdecl (/Gd)の場合、関数funcのシンボル名は _func
4.__stdcall(/Gz)の場合、関数funcのシンボル名は _func@4 // 4は引数のByte数ただし以下の場合を除きます。
5.__cdecl、__stdcallなどが明示的に指定されている場合はその規約が採用される。
6.DLL側の、defファイルで公開するシンボル名称が明示的に指定されている場合、
その定義されたシンボル名称でexportされる。
7.C++からC言語関数を参照する場合は、extern "C"と定義して、
C言語呼び出し規約で呼び出します。わけですね。
ほかにも/Grだとか、thiscallなどがありますが、関係なさそうなので割愛。 -
ごめんなさい、ちょっと教えてください。
昔、VB6.0から利用していたので、__stdcallとしています。
現在は、C#、または、C++から利用できれば良いです。CのオープンソースをMFCのDLLの形で利用しようとしていますが、
DLLを呼び出し規約__cdeclでコンパイルし、
呼び出し側のテストプログラムも、__cdeclとしたら、チャント動いています。しかし、Cのオープンソースを呼び出し規約__stdcallでコンパイルすると、
error C2440: '関数' : 'int (__stdcall *)(const void *,const void *)' から 'int (__cdecl *)(const void *,const void *)' に変換できません。
とコンパイルエラーになります。逆に、利用側だけを、__stdcall とすると
error LNK2001: 外部シンボル "_TIFFOpen@8" は未解決です。
とリンクエラーになります。どのように対応したら、良いでしょうか。
この文章から、次のように理解しました。
- C のオープンソースを利用して、DLL を作成する(改変可能である)
- これまでは VB6.0 から利用していた →__stdcall を使用していた
- 現在は C#/C++ から利用したい
- DLL の宣言を「__cdecl」、呼び出し側を「__cdecl」とした →動作する
- DLL の宣言を「__stdcall」、呼び出し側を「__cdecl」とした →error C2440
- DLL の宣言を「__cdecl」、呼び出し側を「__stdcall」とした →error LNK2001
何故、DLL の宣言と、呼び出し側の宣言を異なるものとしたのでしょうか。
あるいは、宣言を異なるものとして、何をしたいのでしょうか。何か実現したいことがあって、実現する方法を探して、呼び出し規約を変更することを思いついた/行き着いたのだと思います。その経過が、この投稿からは全く見えません。「何か書いてあるのを変えてみた」様にしか見えません。いったい、何をしようとしているのでしょう?
または、最後に「どのように対応したら、良いでしょうか」と書かれていますが、どのような目標に向けて対応したいのでしょうか。単に「ビルドできるようにしたい」であれば、DLL の宣言と呼び出し側の宣言を __cdecl にすれば動作することが確認できているのですから、尋ねなければならないことではありません。いったい、何を質問したかったのでしょう?なお、「昔、VB6.0から利用していたので、__stdcallとしています」から、「C# から呼び出せるようにするには、どの宣言にすればよいのだろう?」という疑問があるように見受けられます。これについては、DllImportAttribute の CallingConvention を指定することで、宣言側に合わせられます。
Jitta@わんくま同盟
-
開発は3段階になっています。
1.ユーザーインターフェースのC#やVB6.0
2.これまで、自分で作っているDLL: VB6.0で利用できるよう__stdcallにしていた。
3.今回のオープンソースのDLL
今回は2と3の関係になります。
今回のオープンソースのDLLを利用するためのテスト用のMFCのExeのプログラムを作りました。
この2つの関係では、
・DLL の宣言を「__cdecl」、呼び出し側を「__cdecl」とした →動作する
・DLL の宣言を「__stdcall」、呼び出し側を「__cdecl」とした →error C2440
・DLL の宣言を「__cdecl」、呼び出し側を「__stdcall」とした →error LNK2001
となります。
> 何故、DLL の宣言と、呼び出し側の宣言を異なるものとしたのでしょうか。
> あるいは、宣言を異なるものとして、何をしたいのでしょうか。
オープンソースのDLLは__cdeclでしかコンパイルできません。
これまで、自分で作っているDLLは__stdcallでコンパイルしています。
なぜというか、単にオープンソースのDLLを利用したいだけです。
開発ツールのCのデフォルトが__cdeclですので、
必要なら、この際自分のDLLを__cdeclに変えることも考えました。
ただ、__stdcallが新しい規格で少し速いということも書かれていまして、
また、__stdcallがC#のデフォルトでもありますし、そのままが良いかなというところです。
そういう中で、利用しているプログラムは10個程度ですので、
今回の回答で、あまり時間をかけずに上手く行きました。