LoginSignup
10
8

More than 3 years have passed since last update.

C++で作成したDLLでAPIではなくクラスを提供する方法

Last updated at Posted at 2020-10-22

はじめに

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作成手順

  1. 外部に提供するヘッダファイル(.h)の作成
    このヘッダには以下を定義する。

    • 提供したいクラス
      ここで定義するクラスのメンバ関数は純粋仮想関数とする。
    • 提供したいクラスを取得させるAPI
  2. 提供するクラスを継承したクラスを定義&実装
      1.のヘッダファイルに定義したクラスを継承して実装する。

  3. クラスを取得させるAPIを実装
      1.のヘッダファイルに定義したAPIを実装する。

外部に提供するヘッダの作成

dll_export_def.h
#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);

提供するクラスを継承したクラスを定義&実装

継承したクラスの定義は以下となります。

dll_class_impl.h
#include "dll_export_def.h"

class dll_class_impl : public dll_class
{
public:
    dll_class_impl();
    ~dll_class_impl();
    void print(void);
};

継承したクラスの実装は以下となります。

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_class_impl.cppの末尾に記載しました。

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で提供する方法を記載しました。
コードも置きましたので、拙い説明よりはコードを見たほうが早いかも。

やり方としては単純なのですが、意外に調べるのに時間がかかりましたので、私以外の誰かの助けになれば幸いです。

10
8
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
8