C/C++ DLLの作成
C/C++ DLL の作成と実行
このページの DLL 作成は、__declspec(dllexport) 修飾子 を使用した手順となる。
また、MSDN:VC++ → チュートリアル : ダイナミック リンク ライブラリ (DLL: Dynamic Link Library) の作成と使用を実践した物に過ぎない。
感想:一般的に、.def 利用より簡単と言われる __declspec(dllexport) エクスポートであるが、それでも面倒極まりない。
この頁も投稿積み上げ式 Wiki となっている。下から見ないと意味不明です。 これは、この Wiki が段落単位個別編集に対応しないことに 50%(以上) 起因します。 以前と比べ少しずつ機能向上してると思うのですが、編集時、編集個所へスクロール…って、正直ページ行数が多いとやってらんないです。 (by 管理人) … 追伸 … (だれに?) それから、ページ内リンクアドレスも HTML 表示して調べないとわかんないのもちょっとね。まぁ、そういうもん思って>使えば良いだけ。何より、livedoor wiki は、気楽につかえるのが Good!
アプリケーションの実行
動作確認状況
※特定Application の動作状況については、別項 C/C++ DLL::Export/Import 作成、移動しました。
- 同一ソリューションから dlltest.dll の呼出:成功
- 別ソリューションからの 作成済み dlltest.dll(.h .ilk .lib 有り):失敗
- 単独動作確認の準備
- フォルダ作成 -> dlltest.dll/readdll.exe の 2file のみコピー
- 内容が cmd のみの bat file 作成。
※exe file ダブルクリックでは、すぐ終了して「なんだかわかんないの〜」状態となる。同一フォルダから コマンドプロンプトを表示すると、カレントディレクトリがフォルダ位置となって好都合。 - コマンドプロンプトから、readdll.exe を実行
※正常動作確認 OK!
- dlltest.dll のエクスポート関数名
- ?fn_a@fn_class@fn_nmspace@@SAHHH@Z
- ?fn_b@fn_class@fn_nmspace@@SAHHH@Z
- ?fn_c@fn_class@fn_nmspace@@SAHHH@Z
- ?fn_d@fn_class@fn_nmspace@@SAHHH@Z
- この手順で作成されるファイル(project を除く)
- debug フォルダ
※何故かデバッグフォルダのみ作成。リリースモードでコンパイルする手順が別にあると思われる。- dlltest.dll, dlltest.exp, dlltest.ilk, dlltest.lib, dlltest.pdb
- readdll.exe, readdll.ilk, readdll.pdb
- 手動追加 file :dlltest.h, readdll.bat(中身は cmd のみ)
- debug フォルダ
※外部に readdll project 作成で dlltest の呼出を試みたが撃沈。以下エラーログの一部
(new readdll から、既存 dlltest project への参照が出来ない。というか、dll project ソースを参照しなければいけないこと自体おかしいので、やり方が異なると思われる。この場合、多分、dlltest.h と関連づける処理手順が別途あると思われる)
〜 readdll.obj : error LNK2019: unresolved external symbol "public: static int __cdecl fn_nmspace::fn_class::fn_d(int,int)" (?fn_d@fn_class@fn_nmspace@@SAHHH@Z) referenced in function _main 〜 同 ?fn_c@… ?fn_b@… ?fn_a@… 同 error LNK2019: 表示 c:\ … \readdll.exe : fatal error LNK1120: 4 unresolved externals Build log was saved at "file:// c:\ … \BuildLog.htm" 〜※C++ 以外で利用可能では無い関数名となるようである。
( ?fn_… の '?' は特に不味い…と思われる)
▲上へ
D. アプリケーションの作成と実行
readdll.exe の実行:スタートアッププロジェクトへ設定が必要
- 実行手順::呼出側プログラムの実行
- 作成済み DllProject(dlltest) が、呼出側 Application Project(readdll) で、既定Project となっている必要がある。
a ) ソリューション エクスプローラ -> 呼出側 Application Project(readdll)を選択
b ) -> Project -> スタートアップ プロジェクトに設定 クリック。 - 呼出側 Application Project の実行
Debug -> デバッグなしで開始
これにより呼出側 Application readdll.exe を実行できる。
※呼出側 Application 実行で、作成済み DllFile の関数(Class)が呼び出され、期待する動作が確認出来れば OK ということになる。
※基本的に、実行するだけなら 呼App と dllfile のみ1個のフォルダへ保存し実行可能なら OK である。dll file を別の App から呼び出したい場合、関連 .h file も同一フォルダへコピーする必要がある。(…ようだ…)
▲上へ
DLL と実行Application の作成
C. DLL 機能をコンソール アプリケーションで使用する
※目的:readdll.cpp 編集、参照設定、readdll.exe を作成する。(DLL を参照するプログラムを作成 (1) の続き)
Source file Name(.cpp) : readdll.cpp
… edit …
// readdll.cpp // dlltest.lib と一緒にコンパイルする #include <iostream> #include "dlltest.h" using namespace std; int main() { cout << "fn_a(x+y)=" << fn_nmspace::fn_class::fn_a(10,20) << endl; cout << "fn_b(x-y)=" << fn_nmspace::fn_class::fn_b(10,20) << endl; cout << "fn_c(x*y)=" << fn_nmspace::fn_class::fn_c(10,20) << endl; cout << "fn_d(x/y)=" << fn_nmspace::fn_class::fn_d(10,20) << endl; return 0; } // readdll project のプロパティへ以下参照設定を行う // 1.1 add reference -> add dlltest project // 1.2 dlltest.h path // 1.3 dlltest.dll pathApplication file Name(.exe) : readdll.exe
- 作成手順::呼出側プログラムの作成
- 新しい参照の追加(readdll project のプロパティ操作)
a ) B. で作成した Project(readdll) 名選択::Project -> Referrences...
b ) -> プロパティページ -> 共通プロパティ -> References
c ) -> Add New Reference... Button 押下
※Add Reference ダイアログ表示。ここでは現在参照可能な全てのライブラリが表示され、そのプロジェクトタブには、現在のソリューション内すべてのプロジェクト、およびそれらに含まれるすべてのライブラリが表示さる。- Add Reference(参照の追加)
a ) プロジェクトタブ内の作成済みプロジェクト名dlltest を選択し [OK]押下。
※ここでは既に参照可能なプロジェクト名が表示されていることを前提とする。目的の DLL を含むプロジェクトが表示されない場合 手順 A. へ戻ってやり直しとなる。
b ) References に dlltest が追加されたことを確認。 - DLL HederFile 参照設定::.h Path 設定
※インクルード ディレクトリ パスを変更する必要有り。
a ) プロパティ ページ -> 構成プロパティ -> C/C++ -> General
b ) -> Additional Include Directories へ .h(ヘッダーファイル)の Path 入力
※右側[...] -> 新しい行 -> [...] で追加、又は、("Path")直接入力。 - DLL Path 環境変数の設定::****.dll へ Path設定
a ) プロパティ ページ -> 構成プロパティ -> Debugging
b ) -> Environment へ PATH=<path to *dll_name*.dll file> 入力
※<path to *dll_name*.dll file> が実際の場所に置き換わる…らしいが…。
※巧くいかない場合、右側[...] から直接選択するのが間違いない。
※これで指定した .h file の 指定 Class を利用可能になる。(…らしい…)
- Add Reference(参照の追加)
- 呼出側プログラムの作成
- ソース編集後
a ) Build -> ソリューションのビルド
※実行可能ファイルがコンパイル(Build)され(readdll.exe)完成。
- ソース編集後
※まだ、readdll.exe を実行しようとすると、意味不明のダイアログが現れ実効不能。
※MS では、コンパイルのことを何時の頃からビルドと呼ぶようになっている。
▲上へ
B. DLL を参照するプログラムを作成 (1)
※目的:空の dlltest.cpp 作成Solution Name : dlltest
Project Name : readdll
Source file Name(.cpp) : readdll.cpp … empty file
Header file : stdafx.h
Source file : stdafx.cpp
- 作成手順::呼出側 Project と 空の Sorce を作成
- 通常のプロジェクト作成の手順を行う。
- 新規プロジェクト作成
a ) -> win32 -> Win32 Console Application
b ) -> プロジェクト名(readdll) -> ソリューション(S) ソリューションに追加
※この時点で「ソリューションのディレクトリを作成」が GrayOut
※これにより、DLL と同じソリューションに新しい Project(readdll) が追加され、DLL Project(dlltest) と呼出側 Application Project(readdll) を同時編集することができる。(別作成でも良いと思うが、始めはこの方が便利) - ウィザード起動
a ) Console application -> Additional options:Precompiled header のチェック解除。 - 新規Project が DLL と同一ソリューションに追加作成される。
※合わせて、プロジェクト名.cpp(readdll.cpp) と名前のついた 空(Console Application の最低限コード自動生成)の C++ ソースが自動作成される。
- 新規プロジェクト作成
▲上へ
A. DLL の作成手順
※目的:dlltest.dll と、dlltest.h 作成Solution Name : dlltest
Project Name : dlltest
Header file Name : dlltest.h
// dlltest.h namespace fn_nmspace { class fn_class { public: static __declspec(dllexport) int fn_a(int x,int y); // fn_a static __declspec(dllexport) int fn_b(int x,int y); // fn_b static __declspec(dllexport) int fn_c(int x,int y); // fn_c static __declspec(dllexport) int fn_d(int x,int y); // fn_d }; }Source file Name(.cpp) : dlltest.cpp
// dlltest.cpp #include "dlltest.h" #include <stdexcept> using namespace std; namespace fn_nmspace { int fn_class::fn_a(int x, int y) { return x + y; } int fn_class::fn_b(int x, int y) { return x - y; } int fn_class::fn_c(int x, int y) { return x * y; } int fn_class::fn_d(int x, int y) { if (y == 0) { return 0; } else { return x / y; } } } // 関数名と実行時の戻り値を検証できれば、内容は何でも良い。Dll file Name(.dll) : dlltest.dll
- 作成手順(3)::DLL側作成
- DLL プロジェクトの作成
- Win32 Console Application を作成する手順を取る。
※プロジェクト(dlltest)名入力の他、ソリューション名(dlltest)の入力も行う。
※ソリューションのディレクトリを作成をチェック - ウィザードから DLL -> 追加のオプション 空のプロジェクト
※DLL 選択不可なら「コンソール アプリケーション」。後変更可。
- Win32 Console Application を作成する手順を取る。
- DLL にクラスを追加
- 新しい Class の .h file 追加
a ) プロジェクト -> 新しい項目の追加
b ) -> コード -> ヘッダー ファイル (.h) 選択
c ) 名前を付けて追加(dlltest) -> 空の .h ファイルを作成
※困ったことに .h ファイル作成の知識が必要となる。
※.h file といっても実際は Class を定義したファイル。
※メソッドを DLL でエクスポートし、他のアプリケーションで利用するには、__declspec(dllexport) 修飾子 又は、.def file の知識が必要。
参考用語:: dllexport , dllimport
- 新しい Class の .h file 追加
- 新しいクラスのソースファイルを作成
- ソースファイルの追加
a ) プロジェクト -> 新しい項目の追加
b ) -> コード -> C++ ファイル (.cpp)
c ) 名前(dlltest.cpp)を付け 空の .cpp ソースファイルを作成
※作成した ****.h file は #include "****.h" としてインクルードしプログラムを記述する。
※ここで作成するプログラムは、Class 定義に基づいて外部から呼び出される Class ベンバの外部定義となる。(こうした場合、コンパイルで作成される実行実態部分は、通常 Class 内インライン定義にしない…らしい…)
- ソースファイルの追加
- プロジェクトを DLL にビルドする
- 設定の確認
a ) プロジェクト -> …プロパティ...
b ) 構成プロパティ -> 全般
c ) -> 構成の種類 が ダイナミック ライブラリ (.dll) であることを確認
※ウィザードで DLL が選択できない場合、ここを変更する。 - コンパイルする。
a ) ビルド -> ソリューションのビルド
※これで、他のプログラムから呼び出し可能な DLL (dlltest.dll)が完成する。
- 設定の確認
※この説明は、MSDN のチュートリアルの説明手順を手短にまとめた物。
※参考本等見ると Win32 Console Application ではなく、Win32 Project を選択するよう記載された物もある。
※DLL 作成は、VC++ 2005 Express Edition 標準インストール状態で可能。PSDK や、各種設定変更などしなくて良い。(…必要との思い込みから、散々弄った後で気づく…)
※dll project の新規作成時に ウィザードで dll が選択出来ない場合、project名 -> プロパティページ -> 構成プロパティ -> General -> Configuration Type を Dynamic Library(dll) へ変更することが出来る。また、この段階ですでに「ソリューションのビルド」が可能な状態のはずであるが、巧く dll へコンパイルされない場合や構文以外のコンパイルエラーが出る場合 Application(exe) のままになってないかをここで確認する。
▲上へ
リンク
内部リンク
- C/C++ C++/CLI C# 関連
- VC++ 2005 Express のインストール
- C/C++ の簡単なプログラム例
- 変数・定数
- プログラムの分割/ダイナミックリンクライブラリ など
- その他
- C/C++ その他::書式文字/ESC code など
- VB2005リファレンス(覚え書き)
- SQL文:SQLステートメント
- VBA(VisualBasic for Applications)
▲上へ
外部リンク
- MSDN
- http://msdn2.microsoft.com/ja-jp/library/e6w9eycd(...
- http://msdn2.microsoft.com/ja-jp/library/ms235636(...
チュートリアル : ダイナミック リンク ライブラリ (DLL: Dynamic Link Library) の作成と使用
- http://msdn2.microsoft.com/ja-jp/library/ms235636(...
- http://msdn2.microsoft.com/ja-jp/library/ms173251....
- Visual C++ プロジェクトに対して作成されるファイルの種類
- http://msdn2.microsoft.com/ja-jp/library/e6w9eycd(...
くるくる、くるくる、くるくる、くる・・・
あっちに行けというので、あっちへ行くと、
また、詳しくはあっちへ行けというので、またあっちへ行く。
そして、また・・・
むかっ!!
M・*・D・N
▲上へ
独り言
ここまでだいぶ脱線したが、C/C++ 利用目的の本筋へようやく戻れた。
DLL とは?
分割コンパイルとも異なる。単純に Console Appoication へ引数を渡して処理させるのとも異なる。
矢鱈と手が込んでいるが、UNIX系でも(もう少し高度で)同様のシステムがあるが dll とは呼ばない。
一連の関連性の高い処理をまとめ、ライブラリ(Class?)として登録、これを複数のプログラム(Application)から、定められた手順に基づき共有、利用可能としたもの…となるらしい。
実際の実行時は、各プログラムの一部としてダイナミックにリンクされ、実行部分や使用メモリまで共有されるわけではなく、あくまでリンク先アプリケーションの一部となる。(OS のメモリ管理や安全性、動作プログラム間のセキュリティ仕様上、原則この仕様は変更不可。実際は擬似的データ共有のようなことは可能ではあるが…メモリ共有は通常 OS が許さない。Win32であってもこれは同じ。)
要は、複数のアプリケーションで同一 DLL を呼び出した場合、呼出アプリケーションの数だけ DLL サイズのメモリを消費するが、仕様であるということ。同一 DLL を一個起動し、それを共有出来るとすれば DLL ではないということになる。
今回やってみて、予想以上に厄介で面倒くさいシステムであることがわかった。
以下 DLL を呼び出すための手順は、… Export … と呼ばれる手順だが、… DEF … と呼ばれる手順もある。
その違いは、呼出側プログラムと呼び出される DLL 間のプログラム名、関数名、変数名など名前解決(???違うかも???)を行う手段が異なる…といった所らしいが、より汎用的な DLL を作成する場合 … DEF … を使ったほうがどうも確実らしい(…らしい…)。
要するに、C++ で作成された DLL は、C++ での呼出しの場合問題無いが、他の言語、例えば C C# VB Pascal Java … その他、DLL 呼出機能を有した スクリプト言語 からの呼出しは案外難しい場合が多いらしい。
また、これを難しくする要因として受け渡しするデータタイプの整合性もあるようだ。
そうは言っても、何からでも”バンバン ”呼出可能な DLL も有るようである。要は、いろいろやってみて覚えていくしかないのである。
※かなりローカルなアプリケーションの英語版マニュアルしかない仕様の、さらに C言語風スクリプトから呼出したいっていうのだから … ハードル高! … しかし出来なきゃ意味がない。
▲上へ
2008年05月18日(日) 19:48:50 Modified by cafeboy1