1. ホーム
  2. winapi

[解決済み] dllexport を使って DLL から関数をエクスポートする

2022-10-08 15:13:19

質問

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

私は、ヘッダ、つまり .cpp ファイル、そして .def ファイル (絶対に必要な場合) を追加します。

エクスポートされる名前を 装飾なし . 最も標準的な呼び方である ( __stdcall ?). を使いたいのですが __declspec(dllexport) を使う必要がなく .def ファイルを使用する必要がありません。

例えば

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

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

リンカがアンダースコアや数字(バイト数?)を名前に付加してしまうのを避けるためです。

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

アップデイト

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

私はこのDLLをヘッダーファイルなしで使用したいので、多くの派手な #defines のような、呼び出し側でヘッダを使用できるようにするための多くの派手なものは必要ありません。

を使わなければならないという答えでOKです。 *.def ファイルを作成することです。

どのように解決するのですか?

プレーンな C エクスポートが必要な場合、C++ ではなく C プロジェクトを使用します。C++ DLL は、すべての C++ イズム (名前空間など...) のために名前マングリングに依存しています。プロジェクト設定の C/C++->Advanced に、コンパイラー スイッチの /TP と /TC に対応する "Compile As" というオプションがあるので、コードを C としてコンパイルすることができます。

lib の内部を記述するのに C++ を使用し、C++ の外部で使用するためにいくつかの関数をアンマングルでエクスポートしたい場合、以下の 2 番目のセクションを参照してください。

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

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

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

そして、エクスポートさせたい関数で LIBRARY_API :

LIBRARY_API int GetCoolInteger();

ライブラリビルドプロジェクトで 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 つの方法は同じタスクを異なる方法で達成しますが、2 つの方法のうち dllexport/dllimport が推奨される方法であると私は信じています。

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

LoadLibraryやGetProcAddressを使用したり、他の言語(.NETのPInvokeやPython/RのFFIなど)からインポートする必要がある場合、以下のように使用できます。 extern "C" を使用して、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")
);