【第9回】DLLの作り方と使い方

次回の記事

terapotan.hatenablog.jp

前回の記事

terapotan.hatenablog.jp

連載記事一覧

terapotan.hatenablog.jp

DLLとは?

ライブラリとは?

ゲーム、その他ソフトウェアを作成するには非常に多くの機能を実装しなければなりませんが、その中でも「文字を表示する」「音を再生する」「文字の入力を受け付ける」など多くの人が必ず欲しいと思うであろう機能が存在します。

ですが、ソフトウェアを作成するたびに機能を作るのは面倒ですし、なにより作業時間の無駄です。

そこでそのような機能を一つにまとめてしまいます。これをライブラリと呼びます。

DLLって何だ?

DLLとは、DynamincLinkLibraryの略でプログラム実行時にリンクされるライブラリのことです。

プログラム実行時にリンクするため、普通ライブラリは実行ファイルとは別のファイルとして保管されています。
Windowsであれば.dllという拡張子がついているはずです。

DLLにすると?

先ほども解説した通りライブラリを別のファイルとして保管するため、実行可能形式のプログラムのサイズを小さくすることが出来ますし、ライブラリだけ入れ替えるといったことも可能です。

しかし、DLLのバージョンアップによってDLLを使用しているプログラムが突然動かなくなってしまうといった欠点も存在します。

Column:動的リンクと動的ロードの違い

実は、次の節以降で解説しているのは動的ロードの方法であり動的リンクの方法ではありません。

動的リンクとは、プログラムが実行されたときにリンクする方式です。
動的ロードとは、プログラムが任意にライブラリをロードしたときにリンクする方式を指します。

どちらもプログラム実行以降にリンクするという点では共通していますが、動的リンクでは実行時にリンクしているのに対し動的ロードではプログラム実行後、任意のタイミングでリンクしています。

似ているので混同しないように注意してください。

DLLの作成方法

DLLプロジェクトを新規作成する

プロジェクトを新規作成する画面を開き、下の画像を参考にしてダイナミック リンク ライブラリ(DLL)を選択しOKを押します。 alt

DLLの内容を作成する

以下の内容のmain.cpp,main.hを新規作成したプロジェクトの中に追加します。
main.cpp

#include "stdafx.h"

#include "main.h"
#include <iostream>

DLLEXPORT int addNum(int a, int b) {
    return a + b;
}

main.h

#pragma once
#define DLLEXPORT extern "C" __declspec(dllexport)

DLLEXPORT int addNum(int a, int b);

ライブラリを使う人に関数を公開するには、関数の頭にextern "C" __declspec(dllexport)を付けます。(DLLの内部で使用する関数であれば付ける必要はありません。)

今回は入力する手間を省くために、#defineを使ってDLLEXPORTと入力しても同じになるようにしています。(DLLEXPORT int~は、extern "C" __declspec(dllexport) int~と同じ。)

DLLを作成する

プロジェクトをビルドすると、DLLを作成することが出来ます。

DLL開発中はDebug構成にしてビルドしますが、本番用のDLLを出力する場合はRelease構成にしてビルドしてください。

今回はRelease構成でビルドしてください。

テスト用のプロジェクトを新たに作成する

DLLプロジェクトが置かれているソリューションに「Windows コンソール アプリケーション」という種類のプロジェクトを新たに作成してください。
詳しいプロジェクト作成の方法については、第6回をご覧ください。

コードを入力する

以下の内容のConsoleApplication1.cppを新規作成したプロジェクトの中に追加します。
ConsoleApplication1.cpp

#include "pch.h"
#include <iostream>
#include <windows.h>

int main(void)
{
    HINSTANCE hDLL;
    int(*addNum)(int, int);//関数addNumへのポインタ
    int ans = 0;
    hDLL = LoadLibrary(L".././Release/main.dll");//文字列の先頭に`L`を付けないとコンパイルエラーが発生する

    if (hDLL == NULL) {
        std::cout << "main.dll is missing." << std::endl;
    }
    else if ((addNum = (int(*)(int, int))GetProcAddress(hDLL, "addNum")) == NULL) {
        std::cout << "GetProcAddress is failed" << std::endl;
    }
    else {
        ans = addNum(2, 3);
        std::cout << ans << std::endl;
        FreeLibrary(hDLL);
    }

    return 0;
}

LoadLibrary関数で前の節で作成したmain.dllを読み込む処理を行っています。(引数の文字列の先頭にLを付けないとコンパイルエラーになってしまいます。)
また、GetProcAddressで関数addNumへのアドレスを取得しelseの部分で実際に呼び出しています。(ans = addNum(2,3))
LoadLibrary,GetProcAddress関数共に処理に失敗するとNULLを返すためそれを利用してif-else ifではエラー処理を行っています。

関数を呼び出した後はFreeLibrary関数を実行して確保したメモリ領域を解放する必要があります。

プロジェクトをビルドして実行してみる

本プロジェクトをビルドし、Ctrl+F5を押して実行してみましょう。

5とコンソール画面に表示されていれば、作成したDLLを正しく呼び出せています。

Column:実行するとエラーが出る


上の手順に従ってプロジェクトを実行しても、上のようなエラーが出て実行できない場合があります。
これは、「Windows コンソール アプリケーション」のプロジェクトではなく「ダイナミック リンク ライブラリ(DLL)」のプロジェクトを実行しようとしていることが原因です。
第6回を参考にして、「スタートアッププロジェクト」を「Windows コンソール アプリケーション」のプロジェクトに変更してください。

次回予告

次回はスタティックリンクライブラリの作成方法について解説します。
7/18更新予定です。

次回の記事

terapotan.hatenablog.jp

前回の記事

terapotan.hatenablog.jp

連載記事一覧

terapotan.hatenablog.jp