DLL作成時にすべてのシンボルをエクスポートする
質問
VS2005で、DLLを作成し、すべてのシンボルを自動的にエクスポートしたいのですが、その際に
__declspec(dllexport)
を至る所に追加することなく、また、手作業で
.def
ファイルを手作業で作成する必要はありません。これを行う方法はあるのでしょうか?
どのように解決するのですか?
短い答え
あなたは、CMakeの新しいバージョン(任意のバージョンcmake-3.3.20150721-g9cd2f-win32-x86.exe以上)の助けを借りて、それを行うことができます。
現在、devブランチにあります。 その後、cmake-3.4のリリース版でこの機能が追加される予定です。
cmakeのdevへのリンクです。
技術を説明した記事へのリンク。
CMake の新しい export all 機能を使用して declspec() なしで Windows 上で dll を作成する。
サンプルプロジェクトへのリンクです。
cmake_windows_export_all_symbols
長い回答
注意してください。 以下のすべての情報は、MSVC コンパイラまたは Visual Studio に関連するものです。
Linux の gcc や Windows の MinGW gcc コンパイラのような他のコンパイラを使用する場合、エクスポートされないシンボルによるリンクエラーは発生しません。
Windows では、DLL からシンボルを明示的にエクスポートする必要があります。
これに関する詳細な情報は、リンク先で提供されています。
HowTo: DLL から C++ クラスをエクスポートする
MSVC (Visual Studioコンパイラ)でDLLからすべてのシンボルをエクスポートしたい場合、2つのオプションがあります。
- クラス/関数の定義でキーワード __declspec(dllexport) を使用する。
- モジュール定義 (.def) ファイルを作成し、DLL をビルドする際に .def ファイルを使用します。
1. クラス/関数の定義で、キーワード __declspec(dllexport) を使用する。
1.1. 使用したいクラスやメソッドに "__declspec(dllexport) / __declspec(dllimport)" マクロを追加してください。つまり、すべてのクラスをエクスポートしたい場合は、次のマクロをすべてのクラスに追加する必要があります。
これに関する詳細情報は、リンクによって提供されます。
DLLから__declspec(dllexport)を使ってエクスポートする
使用例("Project"を実際のプロジェクト名に置き換えてください)。
// ProjectExport.h
#ifndef __PROJECT_EXPORT_H
#define __PROJECT_EXPORT_H
#ifdef USEPROJECTLIBRARY
#ifdef PROJECTLIBRARY_EXPORTS
#define PROJECTAPI __declspec(dllexport)
#else
#define PROJECTAPI __declspec(dllimport)
#endif
#else
#define PROJECTAPI
#endif
#endif
次に、すべてのクラスに "PROJECTAPI" を追加します。 dll からシンボルをエクスポート/インポートしたい場合のみ、"USEPROJECTLIBRARY" を定義してください。 dllに対して"PROJECTLIBRARY_EXPORTS"を定義してください。
クラスのエクスポートの例です。
#include "ProjectExport.h"
namespace hello {
class PROJECTAPI Hello {}
}
関数のエクスポートの例です。
#include "ProjectExport.h"
PROJECTAPI void HelloWorld();
注意 は、"ProjectExport.h"ファイルをインクルードすることを忘れないでください。
1.2. C言語の関数としてエクスポートする。 C++コンパイラを使用してC言語で書かれたコードをコンパイルする場合、関数の前にextern "C"を追加して名前の混乱をなくすことができます。
C++のname manglingについての詳細は、リンク先で提供されています。
使用例です。
extern "C" __declspec(dllexport) void HelloWorld();
これについての詳細は、リンクで提供されています。
C 言語の実行ファイルで使用するために C++ 関数をエクスポートする
2. モジュール定義(.def)ファイルを作成し、DLLをビルドする際に.defファイルを使用する。
これに関する詳細な情報は、リンク先で提供されています。
さらに、.defファイルの作成方法について、3つのアプローチを説明します。
2.1. C関数のエクスポート
この場合、.defファイルに関数宣言を手作業で追加するだけでよいでしょう。
使用例です。
extern "C" void HelloWorld();
.defファイルの例(__cdecl命名規則)です。
EXPORTS
_HelloWorld
2.2. 静的ライブラリからシンボルをエクスポートする
user72260" さんの提案された方法を試してみました。
彼はこう言いました。
- まず、静的なライブラリを作成することができます。
- 次に、"dumpbin /LINKERMEMBER" を使用して、静的ライブラリからすべてのシンボルをエクスポートします。
- 出力をパースします。
- すべての結果を .def ファイルに格納します。
- .defファイルを使ってDLLを作成します。
私はこの方法を使いましたが、常に2つのビルド(1つは静的ライブラリとして、もう1つは動的ライブラリとして)を作成するのはあまり納得のいくものではありません。しかし、私は、このアプローチが本当にうまくいくことを認めざるを得ません。
2.3. .objファイルから、またはCMakeの助けを借りてシンボルをエクスポートします。
2.3.1. CMake使用時
重要なお知らせです。 クラスや関数へのエクスポートマクロは必要ありません!
重要なお知らせです。 /GLは使用できません( プログラム全体の最適化 ) を使用することはできません。
- "CMakeLists.txt"ファイルを元にCMakeプロジェクトを作成します。
- "CMakeLists.txt"ファイルに、以下の行を追加します。 set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)を追加します。
- その後、"CMake (cmake-gui)" の助けを借りて、Visual Studio プロジェクトを作成します。
- プロジェクトをコンパイルします。
使用例です。
ルートフォルダ
CMakeLists.txt (ルートフォルダ)
cmake_minimum_required(VERSION 2.6)
project(cmake_export_all)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(dir ${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${dir}/bin")
set(SOURCE_EXE main.cpp)
include_directories(foo)
add_executable(main ${SOURCE_EXE})
add_subdirectory(foo)
target_link_libraries(main foo)
main.cpp (ルートフォルダ)
#include "foo.h"
int main() {
HelloWorld();
return 0;
}
Foo フォルダ(ルートフォルダ / Foo フォルダ)
CMakeLists.txt (Fooフォルダー)
project(foo)
set(SOURCE_LIB foo.cpp)
add_library(foo SHARED ${SOURCE_LIB})
foo.h (Fooフォルダ)
void HelloWorld();
foo.cpp (Fooフォルダ)
#include <iostream>
void HelloWorld() {
std::cout << "Hello World!" << std::endl;
}
再度サンプルプロジェクトにリンクします。
cmake_windows_export_all_symbols
CMakeは、"2.2.Symbol from static library"と異なるアプローチを採用しています。Export symbols from static library"のアプローチと異なります。
それは次のようなものです。
1) .objファイルがDLLで使用される情報を含む "objects.txt"ファイルをビルドディレクトリに作成します。
2)DLLをコンパイルし、.objファイルを作成します。
3) "objects.txt"ファイルの情報を元に、.objファイルから全てのシンボルを抽出します。
使用例です。
DUMPBIN /SYMBOLS example.obj > log.txt
これについての詳細は、リンクで提供されています。
4) .objファイル情報から抽出したものをパースする。
私の意見では、.obj ファイルを解析するために、例えば "__cdecl/__fastcall", "SECTx/UNDEF" シンボル フィールド (3 列目 ), "External/static" シンボル フィールド (5 列目 ), "?", "?" 情報などの呼び出しコンベックスを使います。
CMake が .obj ファイルを正確にどのようにパースするかはわかりません。 しかし、CMake はオープンソースなので、興味があれば調べることができます。
CMake プロジェクトへのリンクです。
5) エクスポートしたシンボルを全て.defファイルにまとめます。
6) .defで作成したファイルを使用してDLLをリンクする。
4)~5)の手順、つまり.objファイルを解析して.defファイルを作成してからリンクし、.defファイルを使用することは、CMakeが"Pre-Link event"の助けを借りて行います。 Pre-Link event"が発生している間、あなたは好きなプログラムを呼び出すことができます。 つまり、"CMakeの使い方"Pre-Link event"の場合、.defファイルをどこに置くか、"obs.txt"ファイルをどこに置くかという情報と引数 "-E __create_def" でCMakeを呼び出すことになるわけですね。 この情報は、CMake Visusal Studioプロジェクトを "set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)" で作成し、そのプロジェクトファイル ".vcxproj" でDLを確認すれば、確認することができます。
もし "set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)" または "set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF)" なしでプロジェクトをコンパイルしようとしたら、シンボルが dll からエクスポートされないことが原因でリンク エラーが発生します。
これに関する詳細な情報は、リンクによって提供されます。
2.3.2. CMakeを使用しない場合
CMakeを使用しなくても、.objファイルをパースする小さなプログラムを自分で作成することができます。CMakeは、特にクロスプラットフォーム開発において、非常に有用なプログラムであることを認めざるを得ません。
関連
-
[解決済み】「The breakpoint will not currently be hit」を改善するには?このドキュメントにはシンボルが読み込まれていません。" という警告はどうすれば改善されますか?
-
[解決済み】識別子 "string "は未定義?
-
[解決済み】なぜ、サイズ8の初期化されていない値を使用するのでしょうか?
-
[解決済み] static_cast, dynamic_cast, const_cast, reinterpret_cast はいつ使うべきですか?
-
[解決済み] コードのすべてのセクションを折りたたむコマンド?
-
[解決済み] 仮想デストラクタはいつ使うのか?
-
[解決済み] Visual Studioで、既存のディレクトリツリーをプロジェクトに追加するにはどうすればよいですか?
-
[解決済み] Visual Studioでディレクトリ構造全体を「既存の項目を追加」するにはどうすればよいですか?
-
[解決済み】画像処理。コカ・コーラ缶」認識のためのアルゴリズム改良
-
[解決済み】Windowsのバッチファイル:.batと.cmdの違いは?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C++エラー。アーキテクチャ x86_64 に対して未定義のシンボル
-
[解決済み】C++でint型に無限大を設定する
-
[解決済み】C++でランダムな2倍数を生成する
-
[解決済み】「std::operator」で「operator<<」にマッチするものがない。
-
[解決済み] 式はクラス型を持つ必要があります。
-
[解決済み】オブジェクト引数のない非静的メンバ関数の呼び出し コンパイラーエラー
-
[解決済み】リンカーエラーです。"リンカ入力ファイルはリンクが行われていないため未使用"、そのファイル内の関数への未定義参照
-
[解決済み】エラー:free(): 次のサイズが無効です(fast)。
-
[解決済み】 while(cin) と while(cin >> num) の違いは何ですか?)
-
[解決済み】Eclipse IDEでC++エラー「nullptrはこのスコープで宣言されていません」が発生する件