none
DLLエクスポート関数のキーワードについて

    質問

  • DLLからエクスポートする関数があり、そのヘッダーファイルをインポートする側でも使用したいので、
    その場合、

    エクスポート側は、__declspec(dllexport) int funcA()
    インポートする側は、__declspec(dllimport) int funcA()

    とする必要があると思うのですが、次のようなヘッダーファイルをインポート側で使用しても問題ありませんでした。

    #ifdef __cplusplus
    extern "C" {
    #endif	
    
    __declspec(dllexport)  int fnDLLExportTestDLL3(void);
    
    #ifdef __cplusplus
    }
    #endif
    し

    質問です。

    インポート側では、__declspec(dllimport) を使用せず、__declspec(dllexport) のキーワードのままでも
    問題ないのでしょうか。

    ※ちなみに、上記を「__declspec(dllimport) 」に変更したところ、コンパイル時にdllリンクが一貫していませんと警告が出ただけで、
    ※dumpbinで確認したところ、エクスポートされていました。

    2018年2月23日 4:54

すべての返信

  • >インポート側では、__declspec(dllimport) を使用せず、__declspec(dllexport) のキーワードのままでも問題ないのでしょうか。

    掲載されたソースだけでは判定できません。
    一般的には問題となるはずですが、
    公開側DLLと、呼び出し側との見解が(なにもしなくても)一致する場合もありえます。

    以下は、暇なら読んでみてください。
    ややざっばに説明すると、まず、関数をコンパイルすると、
    そのときの(コード上の宣言、コンパイラオプション)設定次第で、その関数の「呼び出し規約」が変化します。
    「呼び出し規約」とは

      (A) 当該関数の「引き数順位及びスタックの取扱い方法」
      (B) 当該関数の「関数名称の装飾方法」(コードに記述した関数名を装飾した、別の名前で*.obj内に記録されます)

    の両方について規定します。
    DLLの公開側と呼び出し側は当該関数について完全に一致した「呼び出し規約」を取らなければなりません。
    で、

    1.コンパイラによってどの様な「引数の呼び出し規約」が採用されたのか
        1.1 各関数に対して、__cdecl、__fastcall、__stdcall等がコードで指定されている場合は
          その呼び出し規約によりスタック扱いと関数名の装飾が行われます。
        1.2 上記1.1の指定がなされなかった場合はコンパイルオプション(/Gd/Gr/Gz)によって指定された形式になります。
        1.3 上記1.1の指定がなされなかった場合で、C++言語でコンパイルされた場合は
              もっと複雑な名前になります(規則はあります)。
        (1.4) クラス、及びクラスメンバ関数も名称が装飾されます(本件では関係なし)。

    2.エスポートされるときに「外部に公開する名称 = 外部から使うときの名称」が指定されたかどうか(*.def等で)。
      指定されれば、その名称で呼び出せます。指定されなければ、1.で決められた「装飾された名称」が使用されます。

    3.インポート(使う側)は、2.で指定された、公開された名称と呼び出し規約を指定すれば利用できます。

    以上のことがらを前提として掲載されたコードを見ると、

    #ifdef __cplusplus // もしC++言語でコンパイルされる場合は--->
    extern "C" {       // ここからは、C言語としてコンパイルします。
    #endif

    __declspec(dllexport)      // これに続く宣言は、DLLにリンクするときに自動的にエクスポートされます
     int fnDLLExportTestDLL3(void);  // 対象関数の宣言(呼び出し規約は省略されている)
    #ifdef __cplusplus
    }                  // ここまで、C言語としてコンパイルします。
    #endif

    と解釈されますが、これだけでは公開側の「呼び出し規約」の設定も、
    参照側の「呼び出し規約」の設定も一概に言えない状態です。
    プロジェクトの内容を調べて、当該関数について、

    (X) 「公開関数名称」は結局どうなっているのか
    (Y) 呼び出し側の「呼び出し規約」の設定はどうなっているか

    によるということになってしまうのです。

    2018年2月23日 6:51
  • たぶんこれですね。

    __declspec(dllimport) を使った関数呼び出しのインポート
    https://msdn.microsoft.com/ja-jp/library/zw3za17w.aspx

    dllの関数を呼び出すところでブレイクポイントを設定して、デバッグ/ウィンドウ/逆アセンブリ上でステップインしてみると、jmpの数が違うはずです。


    • 編集済み snao 2018年2月23日 9:17 デバッグ設定を追加
    2018年2月23日 8:13
  • 追加です。

    Releaseビルドのデフォルトである、exeの構成プロパティ --> c/c++ --> 最適化で、プログラム全体の最適化を「はい(/GL)」にしていると dllexportでもdllimportでも同じ動作になりました。リンク時にコードが生成されてしまうためのようです。


    • 編集済み snao 2018年2月25日 6:24 Releaseビルド追加
    2018年2月24日 5:19