web-dev-qa-db-ja.com

dllexportを使用してDLLから関数をエクスポートする

C++ Windows DLLから関数をエクスポートする簡単な例が欲しいです。

ヘッダー、cppファイル、およびdefファイル(絶対に必要な場合)を確認したいと思います。

エクスポートされた名前をundecoratedにしたいと思います。最も標準的な呼び出し規則(__stdcall?)を使用したいと思います。 __ declspec(dllexport)を使用し、DEFファイルを使用する必要はありません。

例えば:

  //header
  extern "C"
  {
   __declspec(dllexport) int __stdcall foo(long bar);
  }

  //cpp
  int __stdcall foo(long bar)
  {
    return 0;
  }

リンカがアンダースコアや数字(バイト数?)を名前に追加しないようにしています。

同じヘッダーを使用してdllimportとdllexportをサポートしなくても構いません。 Cスタイルのグローバル関数だけで、C++クラスメソッドのエクスポートに関する情報は必要ありません。

[〜#〜] update [〜#〜]

呼び出し規約を含めない(およびextern "C"を使用する)と、好きなエクスポート名が得られますが、それはどういう意味ですか?どのようなデフォルトの呼び出し規約を取得しても、pinvoke(.NET)、declare(vb6)、およびGetProcAddressが期待するものは何ですか? (GetProcAddressの場合、呼び出し元が作成した関数ポインターに依存すると思います)。

これをDLLをヘッダーファイルなしで使用したいので、呼び出し元がヘッダーを使用できるようにするために多くの派手な#definesを実際に必要としません。

私はDEFファイルを使用しなければならないという答えで大丈夫です。

92
Aardvark

プレーンなCエクスポートが必要な場合は、C++ではなくCプロジェクトを使用します。 C++ DLLは、すべてのC++ ism(名前空間など)の名前変換に依存しています。 C/C++-> Advancedの下のプロジェクト設定に移動して、コードをCとしてコンパイルできます。コンパイラスイッチ/ TPおよび/ TCに対応する「Compile As」オプションがあります。

エクスポート/インポートDLL VC++のライブラリ

本当にしたいのは、DLLプロジェクトのすべてのソースファイルに含まれるヘッダーに条件付きマクロを定義することです。

#ifdef LIBRARY_EXPORTS
#    define LIBRARY_API __declspec(dllexport)
#else
#    define LIBRARY_API __declspec(dllimport)
#endif

次に、エクスポートする関数でLIBRARY_APIを使用します。

LIBRARY_API int GetCoolInteger();

ライブラリビルドプロジェクトでdefine LIBRARY_EXPORTSを作成します。これにより、DLLビルド用に関数がエクスポートされます。

LIBRARY_EXPORTSはDLLを使用するプロジェクトでは定義されないため、そのプロジェクトにライブラリのヘッダーファイルが含まれている場合、代わりにすべての関数がインポートされます。

ライブラリをクロスプラットフォームにする場合は、Windows以外の場合にLIBRARY_APIを何も定義しないでください:

#ifdef _WIN32
#    ifdef LIBRARY_EXPORTS
#        define LIBRARY_API __declspec(dllexport)
#    else
#        define LIBRARY_API __declspec(dllimport)
#    endif
#Elif
#    define LIBRARY_API
#endif

Dllexport/dllimportを使用する場合、DEFファイルを使用する必要はありません。DEFファイルを使用する場合、dllexport/dllimportを使用する必要はありません。 2つの方法は同じタスクを異なる方法で実行しますが、dllexport/dllimportが2つの方法のうち推奨される方法であると思います。

C++からのマングルされていない関数のエクスポートDLL LoadLibrary/PInvokeの場合

LoadLibraryとGetProcAddressを使用するためにこれが必要な場合、または.NETからPInvokeを実行する場合は、dllexportでextern "C"インラインを使用できます。そして、dllimportの代わりにGetProcAddressを使用しているため、上からifdefダンスを行う必要はありません。単純なdllexportだけです:

コード:

#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)

EXTERN_DLL_EXPORT int getEngineVersion() {
  return 1;
}

EXTERN_DLL_EXPORT void registerPlugin(Kernel &K) {
  K.getGraphicsServer().addGraphicsDriver(
    auto_ptr<GraphicsServer::GraphicsDriver>(new OpenGLGraphicsDriver())
  );
}

そして、Dumpbin/exportsでのエクスポートは次のようになります。

  Dump of file opengl_plugin.dll

  File Type: DLL

  Section contains the following exports for opengl_plugin.dll

    00000000 characteristics
    49866068 time date stamp Sun Feb 01 19:54:32 2009
        0.00 version
           1 ordinal base
           2 number of functions
           2 number of names

    ordinal hint RVA      name

          1    0 0001110E getEngineVersion = @ILT+265(_getEngineVersion)
          2    1 00011028 registerPlugin = @ILT+35(_registerPlugin)

したがって、このコードは正常に機能します。

m_hDLL = ::LoadLibrary(T"opengl_plugin.dll");

m_pfnGetEngineVersion = reinterpret_cast<fnGetEngineVersion *>(
  ::GetProcAddress(m_hDLL, "getEngineVersion")
);
m_pfnRegisterPlugin = reinterpret_cast<fnRegisterPlugin *>(
  ::GetProcAddress(m_hDLL, "registerPlugin")
);
121
joshperry

C++の場合:

私は同じ問題に直面したばかりで、___stdcall_(またはWINAPIand _extern "C"_の両方を使用すると問題が発生することに言及する価値があると思います。

ご存知のように、_extern "C"_は次のように装飾を削除します:

___declspec(dllexport) int Test(void)                        --> dumpbin : ?Test@@YaHXZ
_

装飾されていないシンボル名を取得します。

_extern "C" __declspec(dllexport) int Test(void)             --> dumpbin : Test
_

ただし、__stdcall_(=呼び出し規則を変更するマクロWINAPI)も名前を修飾するため、両方を使用すると次のようになります。

_   extern "C" __declspec(dllexport) int WINAPI Test(void)   --> dumpbin : _Test@0
_

シンボルが(_ @bytesで)装飾されているため、_extern "C"_の利点は失われます

___stdcall_規則はx64では無視されるため、このonlyはx86アーキテクチャで発生することに注意してください( msdnon x64アーキテクチャーでは、慣例により、引数は可能な場合はレジスターで渡され、後続の引数はスタックで渡されます

X86とx64の両方のプラットフォームをターゲットにしている場合、これは特に注意が必要です。


2つのソリューション

  1. 定義ファイルを使用します。ただし、これによりdefファイルの状態を維持する必要があります。

  2. 最も簡単な方法:マクロを定義します( msdn を参照):

#define EXPORTコメント(リンカー、「/ EXPORT:」__FUNCTION__ "=" __FUNCDNAME__)

次に、関数本体に次のプラグマを含めます。

_#pragma EXPORT
_

完全な例:

_ int WINAPI Test(void)
{
    #pragma EXPORT
    return 1;
}
_

これにより、x86の___stdcall_規則を維持しながら、x86とx64の両方のターゲット用に装飾されていない関数がエクスポートされます。この場合、__declspec(dllexport)is notが必要です。

23
Malick

まったく同じ問題がありました。私の解決策は、__declspec(dllexport)の代わりにモジュール定義ファイル(.def)を使用してexports( http://msdn.Microsoft.com/en-us/ library/d91k01sh.aspx )。これがなぜ機能するのかわかりませんが、機能します

3
Rytis I