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ファイルを使用しなければならないという答えで大丈夫です。
プレーンなCエクスポートが必要な場合は、C++ではなくCプロジェクトを使用します。 C++ DLLは、すべてのC++ ism(名前空間など)の名前変換に依存しています。 C/C++-> Advancedの下のプロジェクト設定に移動して、コードをCとしてコンパイルできます。コンパイラスイッチ/ TPおよび/ TCに対応する「Compile As」オプションがあります。
本当にしたいのは、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つの方法のうち推奨される方法であると思います。
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")
);
C++の場合:
私は同じ問題に直面したばかりで、___stdcall
_(またはWINAPI
)and _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アーキテクチャで発生することに注意してください( msdn :on x64アーキテクチャーでは、慣例により、引数は可能な場合はレジスターで渡され、後続の引数はスタックで渡されます。
X86とx64の両方のプラットフォームをターゲットにしている場合、これは特に注意が必要です。
2つのソリューション
定義ファイルを使用します。ただし、これによりdefファイルの状態を維持する必要があります。
最も簡単な方法:マクロを定義します( msdn を参照):
#define EXPORTコメント(リンカー、「/ EXPORT:」__FUNCTION__ "=" __FUNCDNAME__)
次に、関数本体に次のプラグマを含めます。
_#pragma EXPORT
_
完全な例:
_ int WINAPI Test(void)
{
#pragma EXPORT
return 1;
}
_
これにより、x86の___stdcall
_規則を維持しながら、x86とx64の両方のターゲット用に装飾されていない関数がエクスポートされます。この場合、__declspec(dllexport)
is notが必要です。
まったく同じ問題がありました。私の解決策は、__declspec(dllexport)
の代わりにモジュール定義ファイル(.def)を使用してexports( http://msdn.Microsoft.com/en-us/ library/d91k01sh.aspx )。これがなぜ機能するのかわかりませんが、機能します