none
呼び出し規約と”LNK2001: 外部シンボル "_TIFFOpen@8" は未解決です。”との関係 RRS feed

  • 質問

  • 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" は未解決です。
    とリンクエラーになります。

    どのように対応したら、良いでしょうか。
    2013年11月29日 2:38

回答

  • 定義と宣言があっていないと C2440 や、LNK2001 が発生します。

    Cのオープンソースと、MFCのDLLのコンパイルオプションをそろえるか、TIFFOpen の宣言に __cdecl をつければ解決すると思いますよ。


    とっちゃん@わんくま同盟, Microsoft MVP for Visual C++ (Oct 2005-) http://blogs.wankuma.com/tocchann/

    • 回答としてマーク クサキ 2013年11月29日 6:32
    2013年11月29日 3:34

すべての返信

  • 定義と宣言があっていないと C2440 や、LNK2001 が発生します。

    Cのオープンソースと、MFCのDLLのコンパイルオプションをそろえるか、TIFFOpen の宣言に __cdecl をつければ解決すると思いますよ。


    とっちゃん@わんくま同盟, Microsoft MVP for Visual C++ (Oct 2005-) http://blogs.wankuma.com/tocchann/

    • 回答としてマーク クサキ 2013年11月29日 6:32
    2013年11月29日 3:34
  • とっちゃんさんの発言通りかと思われますが、
    異なるケースも想定できますので補足しておきます。例えば、

    <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などがありますが、関係なさそうなので割愛。

    2013年11月29日 5:02
  • 宣言に __cdecl をつけ上手くいきました。
    個々の関数に、色々呼び出し規約を設定できるのですね。
    ありがとうございました。

    2013年11月29日 6:32
  • ごめんなさい、ちょっと教えてください。

    昔、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@わんくま同盟

    2013年12月5日 13:13
  • 開発は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個程度ですので、
    今回の回答で、あまり時間をかけずに上手く行きました。

    2013年12月9日 6:17