この記事は、Al Mannarino氏のブログの抄訳です
Table of Contents
はじめに
このブログでは、C++Builderでスタティック(静的)ライブラリ (.Lib) およびダイナミック(動的)ライブラリ(. DLL)を作成および使用する方法について説明します。「ライブラリ」とは、プログラムで再利用可能なコンパイル済みのコードの集まりです。ライブラリには静的ライブラリと動的ライブラリの2種類があります。
スタティックライブラリ(.lib)とダイナミックライブラリ(.dll)の比較
スタティックライブラリ(.LIBファイル)(またはアーカイブ)には、コンパイル時にユーザーのプログラムにリンクされるコードが含まれています。生成された実行ファイルは、ライブラリコードの複数を保持します。
ダイナミックライブラリ(.dll)(または共有ライブラリ)には、複数のプログラムで共有されるように設計されたコードが含まれています。ライブラリ内のコンテンツは実行時にメモリにロードされ、各実行ファイルは、ライブラリの複製を保持しません。
スタティックライブラリとダイナミックライブラリの違いは何ですか?
スタティックライブラリ(.lib)は、複数のプログラムで再利用可能ですが、コンパイル時にプログラム内に組み込まれます。一方、 ダイナミックリンクライブラリ(.dll)または共有ライブラリは、実行ファイルの外部に別のファイルとして存在します。
C++BuilderによるDLLの作成と使用方法のチュートリアル
それでは早速、C++Builderでダイナミックリンクライブラリ(.DLL)を作成してみましょう。このブログでは、C++ Builder 11.3を使用しています。
C++BuilderでDLLプロジェクトを作成する
(1)[ファイル > 新規作成 > その他 > C++Builderプロジェクト > Windows > ダイナミックリンクライブラリ]を選択し、[ソースの種類] C++、[マルチスレッド]にチェックを入れる、[ターゲットプラットホーム] なし の設定で、プロジェクトを作成する
(2) 作成された新規プロジェクトを[ファイル > すべての保存]で保存する。(ここでは、Project1.cbprojはCppMyDLL.cbproj、File1.cppはCppMyDLL.cppとして保存)
(3) 作成されたCppMyDLL.cppファイル内には、以下のコードが最初から定義されています。
[crayon-673f6b5a95154527431755/]_libmain関数は、ダイナミック・ライブラリへのエントリーポイントです。ダイナミックライブラリがロードされると、_libmain関数が呼び出されます。この関数は通常、初期化コードを保持します。
(4) C++BuilderがインストールされていないコンピュータでもDLLとクライアント(.EXE)を使用できるようにするには、以下のように設定します。
- [プロジェクト | オプション | C++リンカ | 動的 RTLとリンクする] = False
- [プロジェクト | オプション | パッケージ | 実行時パッケージ | 実行時パッケージを使ってリンク] = False
(5) [プロジェクト | すべてのプロジェクト]でプロジェクトをビルドする
プロジェクトのビルドとリンクは成功するはずです!
(6) 手順 2で保存したフォルダ以下にWin32/Debugというフォルダが作成され、そのフォルダ内にCppMyDLL.dll(ダイナミックライブラリ)とCppMyDLL.lib(スタティックライブラリ)が作成されているはずです。スタティックライブラリ(.lib)は、ダイナミックライブラリ(.dll)から自動的に生成されます。
(7) DLLファイルの中身の情報を調べたいときに便利なツールとしてDependency Walkerがあります。
Dependency Walkerは、32ビットまたは64ビットのWindowsモジュール(exe、dll、ocx、sysなど)に対応した無料のユーティリティツールで、すべての依存モジュールの階層ツリーを調べることができます。Dependency Walkerは、以下のURLからダウンロードできます。
https://www.dependencywalker.com/
(8) 次に、CppMyDLL.cpp に関数を追加します。
[crayon-673f6b5a9515f246791425/]注記:DLLには、クライアントから隠されている関数と、クライアントに公開されている関数があります。
(9) Sum関数をクライアントから見えるようにするには、以下のコード例のように __declspec(dllexport) を追加します。
[crayon-673f6b5a95161671718381/](10) Dependency Walkerを使用して Sum関数を確認すると、@Sum$addと表示されます。
@<関数名> (ここでは、@Sum)の後に続く$addをを削除するには、以下のように関数にextern “C “を追加します。
[crayon-673f6b5a95163090559599/](11) extern “C “を追加してプロジェクトをビルドし、Dependency Walkerを使用してSum関数を確認すると、_Sumと表示されます。
(12) 関数名からアンダースコア(_)を削除するためには、__stdcallを追加し、この関数がstdcallの呼び出し規約を使用していることを示します。
[crayon-673f6b5a95167104567662/](13) __stdcallを追加してプロジェクトをビルドし、Dependency Walkerを使用してSum関数を確認すると、Sumと表示されます。
extern “C”と__stdcallを関数の定義に追加することで、ダイナミックリンクやスタティックリンクでは、複雑な文字(@Sum$add)ではなく関数名としてSumを使うことができるようになりました。
DLLを呼び出すためのC++Builder VCLプロジェクトを作成する
(14) DLLを呼び出すためのC++Builder VCLアプリケーションを作成します。 同じプロジェクトで、プロジェクトマネージャ内のProjectGroup1を右クリックし、[新しいプロジェクトを追加]を選択して、[C++ Builder > Windows VCLアプリケーション]を選択します。
(15) 作成された新規プロジェクトを[ファイル > すべての保存]でDLLプロジェクトと同じフォルダ内に保存する。(ここでは、Project1.cbprojはClientDLL.cbproj、Unit1.cppはuClient.cppとして保存)
(16) VCLフォームアプリケーションのプロジェクトをビルドします。
ClientDLL.cbproj プロジェクトでは、[プロジェクト > オプション > C++(共有オプション) > 最終出力ディレクトリ]の設定がデフォルトで.$(Platform)$(Config)になっているため、/Win32/Debug フォルダ内には、ClientDLL.exe、CppMyDLL.dll、CppMyDLL.LIB等のファイルが作成されていることがわかります。
(17) DLLプロジェクトと同様にVCLアプリケーションのプロジェクトでも以下のように設定します。
- [プロジェクト | オプション | C++リンカ | 動的 RTLとリンクする] = False
- [プロジェクト | オプション | パッケージ | 実行時パッケージ | 実行時パッケージを使ってリンク] = False
上記のオプション設定では、C++ Builderがインストールされていないコンピュータでも実行できるように、必要な依存関係をすべて実行ファイル(.EXE)内に追加します。
(18) 次にCppMyDLL.LIB ファイルへのスタティックリンクを試してみましょう。スタティックリンクとは、アプリケーションの起動時にDLLを必要とするものです。
スタティックリンクでは、CppMyDLL.LIB ファイルを .EXE ファイルの検索パスに追加する必要があります。 .LIBファイルには、.DLLファイルのすべての情報が含まれています。
[プロジェクト > プロジェクトに追加]で、CppMyDLL.LIBファイルを見つけて選択し、[開く]をクリックします。
プロジェクトマネージャ内のVCLアプリケーションプロジェクト(ClientDLL)に追加されたCppMyDLL.LIBファイルが表示されます。
注釈: .LIBファイルには、上述した.DLLファイルと同じ関数に関する情報が含まれています。
(19) スタティックライブラリ(.LIB)ファイルをClientプロジェクトに追加したので、次はDLL関数を呼び出すための関数プロトタイプを定義します。
[crayon-673f6b5a9516a869470019/]CppMyDLL.cppからこの関数プロトタイプをコピーして、次のようにクライアントアプリケーションのuClient.cppにコードを貼り付けます。
または、上記の関数プロトタイプを含むヘッダーファイル(.h)を作成し、クライアントアプリケーションにヘッダーファイルをインクルードします。
(20) これでクライアントアプリケーションに関数プロトタイプと.LIBファイルが追加され、Sum関数を呼び出せるようになりました。
(21) uClient.cpp(クライアントアプリケーション)のフォーム内にTButtonコンポーネントを配置します。
(22) Button1のOnClickイベントハンドラ内で Sum 関数を呼び出すコードを実装します。
[crayon-673f6b5a9516d721525328/](23) クライアントのプロジェクトをビルドして実行します。そしてフォームないのボタンをクリックします。
おめでとうございます!
これでCppMyDLL.LIBファイルを使用してスタティックリンクしたアプリケーションが実行できるようになりました!
Sum関数を呼び出した結果は、4 + 7 = 11となります。
注釈:クライアントアプリケーションは Sum 関数の本体を持たず、関数のプロトタイプのみを持ちます。本体は.DLLファイルの中にあります。そのため、このクライアントアプリケーションは、スタティックリンクが機能するように、アプリケーションの起動時に.DLLファイルが参照できるパスに配置し、利用できる状態にしておく必要があります。
クライアントアプリケーションが.DLLファイルを見つけられない場合、以下のようなエラーが発生します。
(24) 次にダイナミックリンクによって関数を呼び出すコードを実装してみましょう。
ダイナミックリンクでは、.LIBファイルも関数プロトタイプも必要ありませんが、関数プロトタイプをコメントとしてコードに残しておくと便利です。
ボタンクリックし、OnClickイベントハンドラ内でダイナミックリンクによって関数を呼び出すコード例は、以下の通りです。
[crayon-673f6b5a9516f362060617/]ダイナミックリンクでは、LoadLibraryを使用してDLLを手動でロードし、GetProcAddressを使用してSum関数を呼び出すことができます。DLL内の関数の呼び出しが不要になったら、FreeLibrary(MyDLL) を呼び出してDLLを解放します。
(24) スタティックリンク(.LIB)ファイルはもう必要ないので、プロジェクトからCppMyDll.LIBを削除します。
プロジェクトマネージャで、CppMyDll.lib を選択し、右クリックして、プロジェクトから削除します。
[はい]をクリックして、プロジェクトからCppMyDll.Libを削除します。
(25) アプリケーションをビルドして実行し、ボタンをクリックします。
おめでとうございます!
これでダイナミックリンクを使用してDLL内のSum関数を呼び出すことができました!
(26) 最後にプロジェクト マネージャで ProjectGroup1 を右クリックし、プロジェクトグループを CppDLL_PG として保存します。
C++Builder 11.3を使用したDLLの作成およびその使用方法を解説したチュートリアルは、以上となります。
まとめ
C++ Builderはスタティックライブラリとダイナミック(ランタイム)ライブラリの両方のリンクをサポートしています。
C++ Builderによる静的パッケージ(.Lib)のビルドに関する詳細は、以下のドキュメントを参照ください。
https://docwiki.embarcadero.com/RADStudio/Alexandria/ja/静的パッケージのビルド
C++ BuilderによるDLL(.dll)の作成に関する詳細は、以下のドキュメントを参照ください。
https://docwiki.embarcadero.com/RADStudio/Alexandria/ja/C%2B%2BBuilder_での_DLL_の作成
C++Builderをお持ちでなければ、こちらのWebサイトからC++Builder トライアル版(30日間 使用可能)がダウンロードいただけます。もしこのブログをご覧いただき、ご興味がございましたらC++Builderをインストールし、スタティックライブラリとダイナミックリンクライブラリの作成をお試しください。