1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【C++】動的リンクライブラリを作成して再利用可能なコードを書こう

1
Posted at

開発を行っていると、同じ処理を何度も書いていたり、機能を追加するたびに実行ファイルが大きくなるといった悩みが発生します。
動的リンクライブラリという仕組みを使用することにより、共通の機能をひとつのファイルにまとめ、複数のプロジェクトで同じコードを呼び出すことが可能となります。
本記事では動的リンクライブラリの作成方法とサンプルコードを紹介します。

動的リンクライブラリ/Dynamic Link Libraryとは

動的リンクライブラリとは、プログラムの一部機能を外部ファイルとして分離し、必要なときに呼び出すことができる仕組みのことです。
静的なライブラリとは異なり実行時にリンクを行うため、ビルドしたexeのサイズが小さくなる傾向があり、ライブラリの更新時にもアプリケーションの再ビルドが必要ではないため、ライブラリの差し替えのみでよいといったメリットがあります。
その反面、デバッグが難解になったり、バージョンの問題が起こりやすいといったデメリットも存在します。
一般的に英語名のDynamic Link Libraryの頭文字を取ってDLLと略されます。
以降本記事では動的リンクライブラリのことをDLLと呼ぶこととします。

想定環境

  • Windows10以降
  • Visual Studio 2022

前提知識

  • C++の基本構文をある程度知っていること。

DLL側のセットアップ方法

Visual Studio 2022を開いて、新しいプロジェクトの作成をクリックします。
スクリーンショット (47).png
ダイナミックリンクライブラリ(DLL)のテンプレートを選択して、右下の次へをクリックしてください。
スクリーンショット (48).png
プロジェクト名と作成するディレクトリを選択して、右下の作成をクリックしてください。
スクリーンショット (49).png
画面右のソリューションエクスプローラーの自身のプロジェクト(本記事ではDllTest)を右クリックし、プロパティをクリックしてプロパティウィンドウを開いてください。
スクリーンショット (51).png
プロパティウィンドウの左側から構成プロパティ->C/C++->プリコンパイル済みヘッダーをクリックし、ウィンドウ右のプリコンパイル済みヘッダープリコンパイル済みヘッダーを使用しないを選択してウィンドウ下の適用をクリックしてください。
自身のプロジェクトでプリコンパイル済みヘッダーを使用する場合は、この作業は行わなくて構いません。
スクリーンショット (52).png
これで、ひな型となるプロジェクトのセットアップが完了しました。
スクリーンショット (50).png

DLL側の作成手順

まずは、DLL側のサンプルコードを記述します。

ハローワールドを出力する関数をDLLで作成するサンプルコード

MyPrint.h
#pragma once
//--------------------------------------------------------------
// @file MyPrint.h
// @brief PrintHelloWorldを出力するDLLのヘッダーファイル
//--------------------------------------------------------------
extern "C" {
    //--------------------------------------------------------------
    // コンソールにハローワールドを出力する関数
    //--------------------------------------------------------------
    __declspec(dllexport) void PrintHelloWorld();
}
Myprint.cpp
//--------------------------------------------------------------
// @file  MyPrint.cpp
// @brief PrintHelloWorldを出力するDLLの実装ファイル
//--------------------------------------------------------------
#include <iostream>
#include "MyPrint.h"
//--------------------------------------------------------------
// @brief コンソールにハローワールドを出力する関数
//--------------------------------------------------------------
void PrintHelloWorld() {
	std::cout << "Hello, World!" << std::endl;
}

__declspec()は、関数や変数の宣言に属性を追加したい場合につけます。
dllexportは、DLLから関数や変数を外部に公開する際に付属させる属性となります。

MyPrint.h
//--------------------------------------------------------------
    // コンソールにハローワールドを出力する関数
    //--------------------------------------------------------------
    __declspec(dllexport) void PrintHelloWorld();

C++名前修飾/name manglingという仕組みで関数名を内部的に変換するのですが、DLLを呼び出す際に、変換された後の名前を使用しなくてはなくなります。
extern "C" {}で囲んでやることでC言語形式での公開、つまり内部的な名前の変換を行わないようにし、呼び出し側でも同じ関数名で呼び出すことができます。

MyPrint.h
extern "C" {
    //--------------------------------------------------------------
    // コンソールにハローワールドを出力する関数
    //--------------------------------------------------------------
    __declspec(dllexport) void PrintHelloWorld();
}

これでDLL側のコードが完成したので、ビルドを行います。
画面上部のGUIからビルド構成をReleaseに変更してください。
スクリーンショット (54).png
画面上部のビルドからソリューションのビルドをクリックします。
スクリーンショット (53).png
ビルドに成功したら、プロジェクトのx64->Releaseを開き、.dll.libファイルがあることを確認してください。
スクリーンショット (55).png

呼び出し側のセットアップ

Visual Studio 2022を開いて、新しいプロジェクトの作成をクリックします。
スクリーンショット (57).png
空のプロジェクトを選択して右下の次へをクリックします。
スクリーンショット (58).png
プロジェクト名を決めて、右下の作成をクリックします。
スクリーンショット (59).png
作成したソリューションのディレクトリにlibという名前のディレクトリを追加します。(後に正しくパスを指定すれば名前はなんでも構いません。)
スクリーンショット (60).png
ここに.libファイルを配置してください。
.dllをここに配置すると、VisualStudio 2022のデバッグ実行で挙動を確認することもできます。
スクリーンショット (69).png
includeという名前のディレクトリを作成します。(こちらも正しくパス指定できればどんな名前でも構いません。)
スクリーンショット (61).png
includeに先ほど作成したdllのヘッダーファイルを配置します。
使用側ではcppファイルは不必要です。
スクリーンショット (62).png
上部GUIのプロジェクトからプロパティをクリックしてプロパティウィンドウを開いてください。
スクリーンショット (63).png
ウィンドウ左側の構成プロパティ->C/C++を選択し、ウィンドウ右の追加のインクルードディレクトリからヘッダーファイルのある場所のパスを追加してください。
本記事の場合はincludeを選択します。
スクリーンショット (64).png
次に、プロパティウィンドウの左側から構成プロパティ->リンカーを選択し、ウィンドウ右から追加のライブラリディレクトリ.libを配置したパスを追加します。
本記事の場合はlibを選択します。
スクリーンショット (65).png
最後に、プロパティウィンドウの左側から構成プロパティ->リンカー->入力を選択し、右側の追加の依存ファイル.libファイルの名前を追加します。
本記事はDllTest.libと入力します。
スクリーンショット (66).png
プロパティウィンドウの右下の適用をクリックして変更を適用してください。
スクリーンショット (68).png
これでセットアップが完了となります。

呼び出し側のコード例

作成したDLLを呼び出すコード例

main.cpp
#include <iostream>
#include "MyPrint.h"   // DLL側で公開したヘッダーをインクルード

//-------------------------------------------------------------
// DLLからインポートする関数を宣言
//-------------------------------------------------------------
extern "C" {
    __declspec(dllimport) void PrintHelloWorld();
}

int main() {
    // DLLの関数を呼び出す
    PrintHelloWorld();
	system("pause");
    return 0;
}

先ほど、作成側ではdllexportの属性をつけましたが、呼び出し側ではdllimportの属性をつけ、使用したい関数を宣言します。
extern "C" {}で囲わなければ修飾された名前で関数を探しに行ってしまうため、呼び出し側でも囲う必要があります。

ビルドと実行

上部GUIから構成Releaseに変更します。
スクリーンショット (70).png
上部GUIのビルドからソリューションのビルドをクリックし、exeファイルを作成します。
スクリーンショット (71).png
エクスプローラーのx64->Releaseを開き、ディレクトリにコード中に使用した内容が記述されている.dllファイルを配置してください。
本記事ではDllTest.dllとなります。
スクリーンショット (72).png
exeファイルをクリックして実行を行ってください。
スクリーンショット (73).png

result
Hello, World!
続行するには何かキーを押してください . . .

.dllに記述した関数を呼び出すことに成功しました。

総括

  • DLLの仕組みを利用することで、共通の機能をひとつのファイルにまとめ、複数のプロジェクトで同じコードを呼び出すことが可能となる。
  • extern "C" {} で囲うことで、C++特有の名前の修飾を回避できる。
  • DLLの場合は、呼び出し側でcppファイルは不要となる。
1
1
0

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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?