はじめに
2020年現在、Windowsのアプリ開発をC++でする人はほとんどいないと思いますが、
C++で作成したクラスをdllで提供する方法を調べる機会がありましたので、備忘録を兼ねて記事に残します。
このエントリで身につくこと
- C++で作成したクラスをdllで提供する方法
活用事例について
この記事でC++で作成したdllをC#で使う方法を記載しました。
他社製のSDKを扱うクラスをC++で作成した場合などに、それをC#側から扱うというシーンを想定しています。
環境
このエントリを書いている私の環境は以下です。
- OS : Windows10 Pro x64 20H1(2004)
- Visual Studio Pro: 2019
コード一式
ここに置きました。
ざっくりポイント
- Aという抽象クラスを定義
クラスのメンバ関数は純粋仮想関数とする。 - Aという抽象クラスを継承したBというクラスを定義&実装
このBが実際に提供するクラスとなる。 - Aという抽象クラスのインスタンスを取得するAPIを定義&実装
取得するインスタンスはAを継承したBとする。
説明追記(2020.10.26追記)
@SaitoAtsushi さんにコメントいただき、私の理解と記載が不足していた部分について記載します。
ざっくりポイントでAという抽象クラスを定義している理由ですが、
dllが提供するクラスのサイズ変更に対応するためです。
抽象クラスを経由しない場合は、dllのクラスに変更を加えた場合にクラスのサイズが変更になります。
そうすると、dllを使う側も再ビルドが必要になります。
これを防ぐために抽象クラスを経由します。
ここに詳しい説明がありますので気になる人はご確認ください。
相手に提供するもの
- dll本体
- ヘッダファイル
dll作成手順
- 外部に提供するヘッダファイル(.h)の作成
このヘッダには以下を定義する。
- 提供したいクラス
ここで定義するクラスのメンバ関数は純粋仮想関数とする。 - 提供したいクラスを取得させるAPI
-
提供するクラスを継承したクラスを定義&実装
1.のヘッダファイルに定義したクラスを継承して実装する。 -
クラスを取得させるAPIを実装
1.のヘッダファイルに定義したAPIを実装する。
外部に提供するヘッダの作成
#ifdef DLL_EXPORT
#define DLL __declspec(dllexport)
#else
#define DLL __declspec(dllimport)
#endif
// 提供したいクラス
class dll_class
{
public:
// 提供するメンバ関数
virtual void print(void) = 0;
};
// 提供したいクラスの実態を取得させるAPI
extern "C" DLL dll_class* CreateInstance(void);
提供するクラスを継承したクラスを定義&実装
継承したクラスの定義は以下となります。
#include "dll_export_def.h"
class dll_class_impl : public dll_class
{
public:
dll_class_impl();
~dll_class_impl();
void print(void);
};
継承したクラスの実装は以下となります。
#include <stdio.h>
#define DLL_EXPORT
#include "dll_class_impl.h"
dll_class_impl::dll_class_impl()
{
printf("コンストラクタ\n");
}
dll_class_impl::~dll_class_impl()
{
printf("デストラクタ\n");
}
void dll_class_impl::print(void)
{
printf("print()\n");
}
クラスを取得させるAPIを実装
今回は面倒だったので、dll_class_impl.cppの末尾に記載しました。
#include <stdio.h>
#define DLL_EXPORT
#include "dll_class_impl.h"
dll_class_impl::dll_class_impl()
{
printf("コンストラクタ\n");
}
dll_class_impl::~dll_class_impl()
{
printf("デストラクタ\n");
}
void dll_class_impl::print(void)
{
printf("print()\n");
}
// クラスの実態を取得するAPI
DLL dll_class* CreateInstance(void)
{
return new dll_class_impl;
}
dllを使う
dllの作成ができたので、サクッとdllを使っていきましょう。
今回の目的はdllでクラスを提供する部分なので、dllを使う側はUIなしのコンソールプログラムにします。
ヘッダファイルは提供されているので、作成するのは以下のみです。
#include <Windows.h>
#include "dll_export_def.h"
typedef dll_class* (*FUNC)(void);
int main()
{
// dll名は"ClassInDLL.dll"
HMODULE hHandle = LoadLibrary(L"ClassInDLL.dll");;
FUNC func = (FUNC)GetProcAddress(hHandle, "CreateInstance");
dll_class * instance = func();
instance->print();
return 0;
}
実行すると以下が表示されるはずです。
コンストラクタ
print()
終わりに
サクっと作成したクラスをdllで提供する方法を記載しました。
コードも置きましたので、拙い説明よりはコードを見たほうが早いかも。
やり方としては単純なのですが、意外に調べるのに時間がかかりましたので、私以外の誰かの助けになれば幸いです。