チュートリアル:C++Builder アプリケーションでダイナミック リンク ライブラリを使用する

提供: RAD Studio
移動先: 案内検索

入門チュートリアル への移動


このチュートリアルでは、C++Builder アプリケーション(32 ビット Windows と 64 ビット Windows の両方)でダイナミック リンク ライブラリ(DLL)を使用する主な手順を説明します。

ダイナミック リンク ライブラリは、実行時に読み込むことができるルーチンの集合です。DLL の詳細は、「ライブラリとパッケージ」を参照してください。

以下の例には、整数のスタックを表すクラスをエクスポートする DLL が含まれています。このクラスは、DLL プロジェクトで宣言され実装されます。この例では、その DLL を Firemonkey アプリケーションに動的に読み込み、整数スタック クラスをインポートして使用するまでの範囲を扱います。

DLL からクラスをエクスポートするには:

  1. スタックの仮想基底クラスを宣言する C++ ヘッダー ファイルを記述します。この仮想基底クラスには、エクスポートされるクラスのメソッドを含める必要があります。このヘッダー ファイルは、DLL のクラスをインポートするプロジェクトにインクルードされます。
  2. スタックの仮想クラスから派生するクラスを宣言し、実装します。このクラスでスタック メソッドを実装します。以下のチュートリアルでは、スタックを整数用に実装したものを使用します。
  3. 整数スタック クラスのインスタンスを返すメソッドを実装し、エクスポートします。このメソッドは、DLL プロジェクトで宣言されたクラスのインスタンスを取得するために使用します。

手順とソース コード

このチュートリアルでは、ダイナミック リンク ライブラリを使用してクラスを作成し、エクスポートする手順を説明します。また、エクスポート済みのクラスをインポートして使用する方法も示します。

チュートリアルは 2 つの主要プロジェクトから構成されます。

  • StackLibrary.dll - これは DLL プロジェクトです。ここで整数のスタックを宣言して実装します。また、このプロジェクトには、スタック クラスのインスタンスを取得するためのメソッドも含めます。このメソッドは DLL からエクスポートされます。
  • TestStackLibrary - これは FireMonkey アプリケーションです。このプロジェクトでは、既に宣言された DLL を読み込み、公開されたメソッドをインポートします。このメソッドを呼び出すと、スタック クラスのインスタンスが取得されます。このプロジェクトでは、スタック クラスのメソッドをテストします。

2 つのプロジェクトを作成する手順は以下のとおりです。

ダイナミックリンク ライブラリ

  1. [ファイル|新規作成|その他...|C++Builder プロジェクト|ダイナミックリンク ライブラリ]を選択し、[OK]をクリックします。
  2. [新規ダイナミックリンク ライブラリ]ダイアログ ボックスが開くので、[ターゲット フレームワーク]として[FireMonkey]を選択し、[OK]をクリックします。
    メモ: この DLL ではフレームワークを使用しません。DLL を読み込むアプリケーションが Firemonkey アプリケーションである場合も同じです。
  3. [ファイル|すべて保存]を選択して、作成されたファイルの名前を次のように設定します。
    1. DLL プロジェクト内の C++ ファイル: StackExport.cpp
    2. プリコンパイル済みヘッダー ファイル: StackLibraryPCH1.h
    3. DLL プロジェクト: StackLibrary
  4. 必要に応じて[ファイル|新規作成|その他...|C++Builder プロジェクト|C++Builder ファイル|ヘッダー]または[ファイル|新規作成|その他...|C++Builder プロジェクト|C++Builder ファイル|CPP ファイル]を選択し、以下の 3 ファイルを StackLibrary プロジェクトに新しく追加します。
    • BaseStack.h には、スタック用の仮想基底クラスの定義が含まれます。このクラスは、DLL と Firemonkey プロジェクトの両方で使用されます。
    • StackOfInt.h には、Stack クラスの宣言が含まれます。Stack は、BaseStack の下位クラスで、整数のスタックを表します。
    • StackOfInt.cpp には、Stack クラスの実装が含まれます。
  5. 上で作成したファイルにコードを追加します。詳細はこの後のサブセクションで説明します。
  6. プロジェクトをビルドして StackLibrary.dll という DLL を生成します。

BaseStack.h

BaseStackStackStackOfInt.h で宣言)の仮想基底クラスです。そのため、メソッドの実装は必要ありません。

// Stack の仮想基底クラス
// StackLibrary.dll と TestStackLibrary.exe プロジェクトの両方で使用されるために宣言
class BaseStack {
public:
        virtual bool IsEmpty() = 0;
        virtual int Top() = 0;
        virtual int Pop() = 0;
        virtual void Push(int value) = 0;
};

StackOfInt.h

このファイルには Stack クラスが宣言されています。Stack は、整数のスタックを表すもので、Push、Pop、Top、IsEmpty というメソッドを宣言しています。

#include "BaseStack.h"

// BaseStack の実装
// ここでは BaseStack のメソッドが実装されている
class Stack : public BaseStack {
private:
        // items はスタック内の整数を保持している
        int* items;
        // スタック内の整数の数を表す
        int count;
public:
        // Stack のコンストラクタ
        Stack();
        // Stack のデストラクタ
        ~Stack();
        // スタックが空かどうかを確認する
        bool IsEmpty();
        // スタックの先頭を返す
        int Top();
        // スタックの先頭を返し、その要素をスタックから削除する
        int Pop();
        // スタック内に <code>value</code> を挿入する
        void Push(int value);
};

StackOfInt.cpp

#include "StackOfInt.h"
#define MAX_SIZE 30
#define INDEX_OUT_OF_BOUNDS_EXCEPTION 1
#define EMPTY_STACK 2

// Stack のコンストラクタ
Stack::Stack() {
        count = 0;
        items = new int[MAX_SIZE];
}

// Stack のデストラクタ
Stack::~Stack() {
        delete[]items;
}

// スタックの先頭を返す
int Stack::Top() {
        if (count > 0) {
                return items[count - 1];
        }
        else {
                throw EMPTY_STACK;
        }
}

// スタックの先頭を返し、その要素をスタックから削除する
int Stack::Pop() {
        if (count > 0) {
                return items[--count];
        }
        else {
                throw EMPTY_STACK;
        }
}

// スタックが空かどうかを確認する
bool Stack::IsEmpty() {
        if (count == 0) {
                return true;
        }
        else {
                return false;
        }
}

// スタック内に <code>value</code> を挿入する
void Stack::Push(int value) {
        if (count < MAX_SIZE) {
                items[count++] = value;
        }
        else {
                throw INDEX_OUT_OF_BOUNDS_EXCEPTION;
        }
}

StackExport.cpp

このファイルでは、GetClassInstance 関数をエクスポートしています。この関数は Stack のインスタンスを返します。

// パラメータまたは関数結果として String オブジェクト (またはネストした
// String が含まれている構造体やクラス) を渡す関数を DLL でエクスポートする場合は、
// DLL プロジェクトにもその DLL を使用する他のあらゆるプロジェクトにもライブラリ
// MEMMGR.LIB を追加する必要がある。また、その DLL を使用する他のプロジェクトで、
// DLL からエクスポートされる TObject 派生でない任意のクラスに対する
// new 演算または delete 演算を実行する場合も、MEMMGR.LIB を使用する必要が
// ある。プロジェクトに MEMMGR.LIB を追加すると、DLL とその呼び出し元の EXE が
// メモリ マネージャとして BORLNDMM.DLL を使用するようになる。これらの場合、
// BORLNDMM.DLL ファイルを作成対象の DLL と一緒に配置しなければならない。
//
// BORLNDMM.DLL を使用しないようにするには、"char *" 型または ShortString 型の
// パラメータを使って文字列情報を渡す。
//
// DLL で RTL の動的バージョンを使用する場合、MEMMGR.LIB は暗黙的に
// 追加されるため、明示的に追加する必要はない。

#pragma hdrstop
#pragma argsused

#include "StackOfInt.h"

// DLL からメソッドをエクスポートするための構文は次のとおり:
//
//   extern "C" __declspec(dllexport) void* __stdcall GetClassInstance();
//
// このメソッドは Stack のインスタンスを返す:

extern "C" __declspec(dllexport) void* __stdcall GetClassInstance() {
        return static_cast<void*>(new Stack);
}

extern "C" int _libmain(unsigned long reason) {
        return 1;
}

FireMonkey アプリケーション

  1. [プロジェクト マネージャ]でプロジェクト グループを右クリックし、[新規追加|マルチデバイス フォーム - C++Builder]を選択します。
  2. [ファイル|すべて保存]を選択して、作成されたファイルの名前を次のように設定します。
    1. Firemonkey プロジェクト: TestStackLibrary
    2. Firemonkey プロジェクト内の C++ ユニット: TestUnit
    メモ: この後のコードでは、これらのファイルがライブラリ ファイルを保存したのと同じフォルダに保存されたものと想定しています。
  3. TestStackLibrary フォームに以下のコンポーネントを追加します。
    1. TButton コントロールを 5 つ追加します。以下の名前を付け、それぞれに応じたラベルを設定します。
      • LoadButton
      • PushButton
      • TopButton
      • PopButton
      • IsEmptyButton
    2. それぞれのボタンに OnClick イベント ハンドラを追加します。イベント ハンドラのコードは後で追加することができます。
    3. TEdit コントロールを 1 つ追加します。
      Firemonkey フォームの外観は次のようになります。
      StackForm.png
  4. TestUnit.h に次の 2 つの private メンバを追加します。
    • BaseStack* stack(スタックのインスタンスを保持するためのもの)
    • void __fastcall EnableButtons()
    この後の「TestUnit.h」を参照してください。
  5. に示すように TestUnit.cpp を実装します。

これでプロジェクトを実行することができます。メイン ウィンドウで[Load Library]をクリックしてライブラリを読み込みます。ライブラリを手動で読み込んだ後は、フォームの残りのボタンは動的に読み込まれたそのライブラリを使って動作します。

TestUnit.h

//---------------------------------------------------------------------------

#ifndef TestUnitH
#define TestUnitH
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <FMX.Controls.hpp>
#include <FMX.Edit.hpp>
#include <FMX.StdCtrls.hpp>
#include <FMX.Types.hpp>
#include <FMX.Forms.hpp>
//---------------------------------------------------------------------------
#include "BaseStack.h"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE で管理されるコンポーネント
        TButton *PushButton;
        TButton *PopButton;
        TButton *TopButton;
        TButton *IsEmptyButton;
        TButton *LoadButton;
        TEdit *Edit1;
        void __fastcall PushButtonClick(TObject *Sender);
        void __fastcall PopButtonClick(TObject *Sender);
        void __fastcall TopButtonClick(TObject *Sender);
        void __fastcall IsEmptyButtonClick(TObject *Sender);
        void __fastcall LoadButtonClick(TObject *Sender);
private:	// ユーザー宣言
        BaseStack* stack;
        void __fastcall EnableButtons();
public:		// ユーザー宣言
        __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

TestUnit.cpp

// ---------------------------------------------------------------------------

#include <fmx.h>
#pragma hdrstop

#include "TestUnit.h"

#include "StackOfInt.h"

// ---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
TForm1 *Form1;
// ダイナミックリンク ライブラリの名前を保持する
const wchar_t* library = L"StackLibrary.dll";
// (void*) を返す関数のポインタの型定義
typedef void* (*VoidReturnFunc)();
// インポートされた関数を宣言する構文
extern "C" __declspec(dllimport) void* __stdcall GetClassInstance();

// ---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {
}

// ---------------------------------------------------------------------------
// ライブラリが読み込まれたときにボタンを有効にするためのメソッド
void __fastcall TForm1::EnableButtons() {
        PushButton->Enabled = true;
        PopButton->Enabled = true;
        IsEmptyButton->Enabled = true;
        TopButton->Enabled = true;
}

// ---------------------------------------------------------------------------
// LoadButton の OnClick イベント ハンドラ
void __fastcall TForm1::LoadButtonClick(TObject *Sender) {
        // ライブラリを動的に読み込む
        HINSTANCE load = LoadLibrary(library);
        if (load) {
                ShowMessage("Library loaded!");
                EnableButtons();

                VoidReturnFunc myFunc;

                // GetProcAddress は、読み込んだメソッドのポインタを返す
                myFunc = (VoidReturnFunc)GetProcAddress(load, "GetClassInstance");

                // GetClassInstance メソッドを呼び出す
                stack = (BaseStack*) myFunc();

                // メモ:
                // BaseStack のメソッドは純粋仮想メソッドだが、myFunc() は Stack クラスのインスタンスを返す
                // 後ほどどこで stack->methodName() を呼び出そうと、実際には Stack::methodName() を呼び出すことになり、
                // BaseStack::methodName() を呼び出すわけではない
        }
        else {
                ShowMessage("Library not loaded!");
        }
}

// ---------------------------------------------------------------------------
// PushButton の OnCLick イベント ハンドラ
void __fastcall TForm1::PushButtonClick(TObject *Sender) {
        if (stack) {
                try {
                        int value = StrToInt(Edit1->Text);
                        stack->Push(value);
                        Edit1->Text = "";
                }
                catch (int) {
                        ShowMessage("Please insert an integer into the editbox!");
                }
        }
}

// ---------------------------------------------------------------------------
// IsEmptyButton の OnClick イベント ハンドラ
void __fastcall TForm1::IsEmptyButtonClick(TObject *Sender) {
        if (stack) {
                if (stack->IsEmpty()) {
                        ShowMessage("The stack is empty!");
                }
                else {
                        ShowMessage("The stack is not empty!");
                }
        }
}

// ---------------------------------------------------------------------------
// PopButton の OnClick イベント ハンドラ
void __fastcall TForm1::PopButtonClick(TObject *Sender) {
        if (stack) {
                try {
                        int value = stack->Pop();
                        ShowMessage("Popped " + IntToStr(value));
                }
                catch (int Exception) {
                        ShowMessage("Exception " + IntToStr(Exception));
                }
        }
}

// ---------------------------------------------------------------------------
// TopButton の OnClick イベント ハンドラ
void __fastcall TForm1::TopButtonClick(TObject *Sender) {
        if (stack) {
                try {
                        int value = stack->Top();
                        ShowMessage("The top of the stack is " + IntToStr(value));
                }
                catch (int Exception) {
                        ShowMessage("Exception " + IntToStr(Exception));
                }
        }
}
// ---------------------------------------------------------------------------

使用する API

関連項目