C/C++ DLL::Export/Import
C/C++ DLL エクスポート/インポート
※文中 msdn....(青い方のサイト) の MS サイトは4月で終了し、msdn2...(白い方)への移行に伴い、リンク切れ多発。他頁を含め直す気無いのでご了承ください。C/C++ DLLの作成ページを目一杯書いたが、あまりにハードルが高い上、行数3倍書いても足りない気がしてきたのでページ分けしました。
具体的なエクスポート方法
C 言語の実行形式で使う C++ 関数のエクスポート
http://msdn.microsoft.com/library/ja/default.asp?u...
C++ で作成された DLL 関数の宣言(名前の装飾)に extern "C" を指定(C リンケージ)することで C言語で作成された物として、他の言語(C言語など)から関数呼出を可能とする。但し、C言語に無い機能は使用できなくなる。
// C++ で作成した DLL 関数を 他言語から呼出可とする extern "C" __declspec( dllexport ) int functionCpp(int a);
※ソリューションのビルド時の注意点:dll project -> 構成プロパティ -> Linker -> Input -> Module Definition File : ****.def を設定しないと .def ファイルを使った function名のエクスポートを行わず、勿論 ****.lib の出力も行われないので注意。
※肝心なところが明記されておらず、command プロンプト実行時に /DEF オプションを選択するよう記載されているに留まっている。
※このページ自体もようやく見つけ出した。MSDN 白バージョンにメニューがあるのにリンク切れなのは何故?
memo:
/DEF | EXPORTS | E | Ordina | Hint | Function | Entry Point | ***.LIB |
---|---|---|---|---|---|---|---|
無 | fn_a @1 NONAME | - | - | - | - | - | 無 |
有 | (名前省略) | - | - | - | - | - | 有 |
有 | fn_a @1 NONAME | 0# | 1(0x0001) | N/A | N/A | 0x00011078 | 有 |
有 | fn_a @1 | C | 1(0x0001) | 0(0x0000) | fn_1 | 0x00011078 | |
有 | fn_a @2 | C | 2(0x0002) | 0(0x0000) | fn_1 | 0x00011078 | |
有 | fn_a fn_b | C C | 1(0x0001) 2(0x0002) | 0(0x0000) 1(0x0001) | fn_a fn_b | 0x00011078 0x00011087 | |
※一部確認省略有り※Entry Point は関数により都度変わる |
※ツールに、Dependency Walker 2.2 を使用。(感謝!)
memo2:
※関数名については、.map file を見ることでも確認できるが、 .map も、 /MAP オプション指定が必要で、.def 同様 Linker -> Debugging -> Map Exports:Yes(/MAPINGO:EXPORTS) へ変更する必要あり。(訂正:同 Debugging -> Generate Map File:Yes(/MAP) とする?同じ結果となるようだが…)
.map file
〜 0002:00000360 ?fn_a@@YAHHH@Z 10011360 f test.obj 0002:000003a0 ?fn_b@@YAHHH@Z 100113a0 f test.obj 0002:000003e0 ?fn_c@@YAHHH@Z 100113e0 f test.obj 0002:00000420 ?fn_d@@YAHHH@Z 10011420 f test.obj 〜※.def file を通す前の関数名が出てくる。
※MSDN:VC++リンカオプション→/MAP (マップ ファイルの生成) 参照
▲上へ
DEF ファイルを使った DLL からのエクスポート
http://msdn.microsoft.com/library/ja/default.asp?u...
sample (.def)
;// dlldeftest.def ;// comment 行は、セミコロン、ダブルスラッシュどちらもエラー出ず。 ;修正 def のコメントは ";"(セミコロン) である。 ;"//", "/* 〜 */" 何れもエラー。意味無い行は無視されリンク可。 LIBRARY dlldeftest ;// 必須 VERSION 1.1 ;// 任意 DESCRIPTION "モジュール定義" ;// 任意 EXPORTS ;// 必須 ;// function ordinal noname? ;// ------------------------- fn_a @1 NONAME fn_b @2 NONAME fn_c @3 NONAME fn_d @4 NONAME ;// ------------------------- ;// 関数名必須 ;// 序数、NONAME は使い方に合わせ任意
sample (.cpp/.c)
// dlldeftest.cpp // (DEFを使ったエクスポート例) #include <windows.h> #include <iostream> // このソースなら無しで ok(仕様が c だからと思う) // extern "C" に関連する記述のようである。詳細不明 // .cpp でビルドするが無くてもビルド可能である。 // 但し、無いと C++ 以外からのリンク不可可能性大 /* BOOL APIENTRY DllMain( HANDLE hdDLL, DWORD dwReason, LPVOID lpReserved ) { return TRUE; } */ // 又は・・・ /* BOOL WINAPI DllMain(HINSTANCE hdDLL, DWORD dwReason, LPVOID lpReserved) { return TRUE; } */ // 上記記述の有無に関わらず、以下 C言語準拠程度のソースでは、 // .cpp でも C と同様に利用可能な DLL ができる模様(動作未確認) // ※Class など使用すると E が C++ となり絶対無理となると思われる。 int fn_a(int x,int y) { return (x+y); } int fn_b(int x,int y) { return (x-y); } int fn_c(int x,int y) { return (x*y); } int fn_d(int x,int y) { if (y==0) { return 0; } else { return (x/y); } }
▲上へ
__declspec(dllexport) を使った DLL からのエクスポート
http://msdn.microsoft.com/library/ja/default.asp?u...
・・・作成中・・・
▲上へ
エクスポートとは?
エクスポートとは、DLL 作成時、呼び出し側アプリケーションへどのように利用可能な関数名、変数名などを知らせるかを定義する手続き…のようなもの(…と思う…)。
具体的には、他言語で作成された DLL の関数名、変数名などは、全ての言語で共通利用出来る訳ではない。C++ で Class や namespace 、複雑な構造体とか… 同様の呼び出し名を、C や VB Java などでも利用出来無いと困るのである。
こうした問題を DLL 作成時に適切なエクスポート方法を選択し解決する必要がある。(…それ以外にも理由は有ると思うが…)
エクスポート方法を選択する
DLL ファイルをエクスポートする場合、.DEF file を使う方法、 __declspec(dllexport) を使う方法がある。
- __declspec(dllexport)
- アプリケーションと一緒に管理可能な DLL を作成する。
- 必要に応じアプリケーションのリコンパイルが可能。
- .DEF file を使うのが面倒…など
- .DEF file
- 利用アプリケーション(作成言語を含む)を限定せず、不特定多数へ公開される DLL 作成。
- 利用アプリケーションの管理不可能な場合。(DLL改版でも、何ら変更無く動作しないと困る場合)
- 選択基準(2)
- 呼出側プログラム言語
- 同一言語のみ利用 … .DEF file / __declspec(dllexport)
- 言語を問わず利用可能にしたい … .DEF file
- 呼出側プログラムのリコンパイル可否
- リコンパイル可能 … .DEF file / __declspec(dllexport)
- リコンパイル不可能 … .DEF file
- DLL 作成方法
- 言語固有(C++ Class など)機能をつかう … .DEF file / __declspec(dllexport)
- 従来同様 C言語仕様で記述する … .DEF file / __declspec(dllexport)
- エクスポートの手間と DLL の汎用性
- 簡単にエクスポートしたい … __declspec(dllexport)
- 手間が掛かっても汎用性重視 … .DEF file
- 呼出側プログラム言語
※など考慮する。「C++ エクスポート方式の使い分け」で、自ずと決定する。
▲上へ
C++ エクスポート方式の使い分け
制限の低い DLL 作成では .DEF を使うのが基本と思われる。但し .DEF を使いこなすのは簡単ではない。
.DEF を使わず簡単にエクスポートをする方策もある。但し、各種制限もある。
モジュール定義 (.def) ファイルを使う
1)柔軟な 共有DLL 作成が可能
不特定多数のプログラムから利用されることを想定し、且つ、公開することを想定した DLL の作成に適する。
.def file はエクスポート序数を制御可能。(※単に物を数える際の読み上げ数値が基数、物に番号を割振った数値を序数…と言う様である。言い回しが正しくない、又は、誤解釈の可能性もあるので注意)
DLL に関数(エクスポート関数)を追加する場合、その関数に高い序数値を割当て可能である。これは呼出側アプリケーションを何ら変更することなく改版(関数追加)された DLL をそのまま利用可能に出来る。(新しい関数を含む新しいインポート ライブラリとリンクし直す必要がない。)(アプリケーション変更不要、新しい関数が使えないだけ・・・)
2)NONAME 属性を使って、関数をエクスポートできる
NONAME 属性は、DLL のエクスポート テーブル内の序数のみを配置する。
多数のエクスポート関数を含む DLL の場合、NONAME 属性を使うと、DLL file サイズを抑制できる。
※モジュール定義文の記述は、「モジュール定義ステートメントに関する規則」を参照。
※序数値によるエクスポートの詳細は、「名前ではなく序数値による DLL 関数のエクスポート」を参照。
※難しくて解からん!MSDN 再確認!
3)手続きが煩雑(面倒)である
C++ ファイル内の関数をエクスポートする場合、コンパイラが関数名を装飾しないよう extern "C" を使って標準の C リンケージでエクスポート関数を定義するか、・・・.def ファイルに装飾名を記述する必要がある。
.def file内に装飾名を配置する場合、装飾名を取得するには、DUMPBIN Tool や /MAP Linkar Option を使う。
▲上へ
関数の定義に __declspec(dllexport) キーワードを使用する
1)比較的簡単に利用できる
__declspec(dllexport) は、.def file のメンテナンスやエクスポート関数の装飾名の取得など面倒な配慮が必要ないこと。
2)利用範囲が限定される
内輪で利用する分には問題無いが、不特定多数へ配布する DLL 作成不可。利用プログラムの管理可能範囲に限定。(自分で制御するアプリケーションに使用できるよう DLL をデザインする場合などに適する。)
DLL 改版に伴い、実行アプリケーションも同じくリビルドする必要が発生することがある。(新しいエクスポートで DLL をリビルドする場合、別Verのコンパイラを使い再コンパイルする際に、エクスポートされた C++ 関数の装飾名が変更される可能性があることから、アプリケーションもリビルドする必要がある可能性あり。)
※__declspec(dllexport) を使って DLL をリコンパイルし、呼出側プログラムが正常に動作しなくなった場合「プログラムもリコンパイルしなさい!」ってことと思われる。
※且つ、同一バージョンの VC++ 利用が推奨らしい。(…MS 拡張と思われる…)
▲上へ
リンク
内部リンク
- C/C++ C++/CLI C# 関連
- VC++ 2005 Express のインストール
- C/C++ の簡単なプログラム例
- 変数・定数
- プログラムの分割/ダイナミックリンクライブラリ など
- その他
- C/C++ その他::書式文字/ESC code など
- VB2005リファレンス(覚え書き)
- SQL文:SQLステートメント
- VBA(VisualBasic for Applications)
外部リンク
▲上へ
独り言とかメモとか …
アプリケーションの実行
動作確認状況
- 特定アプリケーションでの動作確認
- … これから準備 …
※たぶんこの方法では出来ない気がする…
- … これから準備 …
*** 一時的メモ ********************************************
Script 定義
※呼出側 Script の追加ヘッダー
#import "inportFileName.***" // ライブラリ名 double functionName(double a, int b); // 定義済み関数名
inportFileName.*** の内容
※DLL 本体に相当
double functionName(double x, int y) { double ret; ret = 〜 処理 〜 return(ret); }
**********************************************************
※DLL 組込側の定義例を記載。同ソフトの Script ならこれで動く。
※これに置き換えて、今回作成した DLL から期待値を読み出せれば OK なのだが…
※あまりにも簡単すぎるような気がする…
※やはり、functionName が見つからないと出る。多分、extern "C" あたりがキーワードになると思われる。
*** DLL 側変更? *****************************************
#define DllExport extern "C" __declspec(dllexport) DllExport int __stdcall functionName(double a, int b)
また、テスト作成した DLL が Class のため ClassName::functionName(); となること。
さらに、namespace SampleDll {} などとなってるのも多分不味い気がする。
SampleDll::ClassName::functionName(double a, int b);
…でなければ関数名を呼び出せないのではないか?勿論これで関数呼び出しするとエラーとなる。
※それではと、呼び出し側の定義ごと SampleDll::ClassName::functionName() とやったら、さすがに "::" はコンパイルエラーの嵐となった。
※結局、呼び出し側の規則に合う形式で、DLL を作成する必要が当然のようにあるのであろう。
**********************************************************
▲上へ
2008年05月18日(日) 19:49:43 Modified by cafeboy1