ライブラリの基礎 - DLLの作成(C/C++/MFC)

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
2020年4月23日 (木) 06:30時点におけるWiki (トーク | 投稿記録)による版 (Wiki がページ「DLLを作成する(C/C++/MFC)」を「ライブラリの基礎 - DLLの作成(C/C++/MFC)」に、リダイレクトを残さずに移動しました)
ナビゲーションに移動 検索に移動

概要

MFCを使ったDLLには、以下の2つの種類がある。

  1. 1つ目は"拡張DLL"で、DLLを使用するEXEやDLLもMFCで作成する時にのみ使うことができる。
  2. 2つ目は"MFCの共有DLLを使用"(Regular DLL)で、これは内部的にMFCそのものを持っているため、DLLを使用するEXEやDLLをMFCで作成しなくても使用できる。



DLLの作成方法(defファイルを作成する)

defファイルを作成する方法を記載する。
※C++でDLLを作成する場合は、defファイルを作成する方法を強く推奨する。

defファイルを作成しない方法では、DLLの関数をエクスポートするために"__declspec"キーワードを使用したが、関数のエクスポートは"defファイル"を利用することでも可能である。
"defファイル"によるエクスポートを採用した場合、 エクスポート関数名にキーワードを付ける必要は無い。

※ C#等で作成したEXEまたはDLLからMFC DLLを呼び出す場合、MFC DLLでは”defファイル”を使用すること
※ defファイルとは、DLLがエクスポートする関数を記述したファイルのことである。
  [追加]→[新しい項目]を押下して、[新しい項目の追加]ダイアログにて”xxx.def”を追加するとdefファイルが作成される。

[プロジェクト]メニューの[プロパティ]を押下して、プロパティダイアログを表示する。
プロパティダイアログの[リンカー]→[入力]の[モジュール定義ファイル]に使用するdefファイルの名前を記述する。

MFC DLL 01.png


defファイルを作成して、以下のようにエクスポートしたい関数を記載する。
※但し、"@1"等の序数値は記載しなくてもよい

 MainDLL.def
 LIBRARY MainDLL
 
 EXPORTS
         SampleFunc   @1
         TestFunc     @2


次に、"MainDLL.h"ファイルは以下のように記載する。

 MainDLL.h
 
 #pragma once
 
 #ifndef   _USRDLL
 #define   DLL_EXPORT   extern "C" __declspec(dllimport)
 #else
 #define   DLL_EXPORT
 #endif
 
 DLL_EXPORT int	     __stdcall SampleFunc(int *lp1, int *lp2);
 DLL_EXPORT double   __stdcall TestFunc(double *lp1, double *lp2);


最後に、"MainDLL.cpp"ファイルには以下のように記載する。

 MainDLL.cpp
 
 #include "Stdafx.h"
 #include "MainDLL.h"
 
 int __stdcall SampleFunc(int *lp1, int *lp2)
 {
    // 以下略
 }
 
 int __stdcall TestFunc(double *lp1, double *lp2)
 {
    // 以下略
 }



DLLの作成方法(defファイルを作成しない方法)

defファイルを作成しない方法を記載する。

Visual Studioの[プロジェクト]→[プロパティ]を選択する。
[C++]→[プリプロセッサの定義]に"DLL"プリプロセッサを追加する。(“Stdafx.h”ファイルに”DLL”プリプロセッサを記載してもよい)

まず、"MainDLL.h"ファイルの先頭に以下のソースコードを追加する。

 MainDLL.h // ファイルの先頭に以下を追加する。
 
 #ifndef __MAINDLL_H__
 #define __MAINDLL_H__
 
 #ifdef DLL
    #define DLL_EXPORT	__declspec(dllexport)
 #else
    #define DLL_EXPORT	__declspec(dllimport)
 #endif
 
 // 以下略
 
 #endif	//__MAINDLL_H__


次に、上記の"MainDLL.h"ファイルにエクスポートしたい関数を以下のように記載する。

 MainDLL.h
 
 #ifdef __cplusplus
    extern "C"
    {
 #endif
 
 DLL_EXPORT int WINAPI SampleFunc(CString &p_rcStr, CWnd *p_pcWnd);
 
 #ifdef __cplusplus
    }
 #endif


"MainDLL.cpp"ファイルの先頭に以下を記載する。

 MainDLL.cpp
 
 #include "StdAfx.h"
 #include "MainDLL.h"


"MainDLL.cpp"ファイルにて関数を作成する時は、以下のように記載する。

 MainDLL.cpp
 
 extern "C" int WINAPI SampleFunc(CString &p_rcStr, CWnd *p_pcWnd)
 {
    // 以下略
 }



Visual StudioでDLLを暗黙的リンクする方法

EXEファイルのプロジェクトに、DLLファイルを暗黙的リンクする方法を記載する。
例として、CppEXE.exeとCppDLL.dllが存在するものとして設定している。

  1. まず、CppEXE.exeのプロジェクトを起動する。
  2. [プロジェクト]メニュー - [CppEXEのプロパティ] - [構成プロパティ] - [C/C++] - [全般] - [追加のインクルードディレクトリ]項目に、
    CppDLL.dllのヘッダファイル(CppDLL.h)が存在するディレクトリを追加する。
  3. 次に、[構成プロパティ] - [リンカー] - [全般] - [追加のライブラリディレクトリ]項目に、
    DLLのライブラリファイル(CppDLL.lib)が存在するディレクトリを追加する。
  4. 最後に、[構成プロパティ] - [リンカー] - [入力] - [追加の依存ファイル]項目に、CppDLL.libと記述する。



DLLの暗黙的リンクと明示的リンクの違い

暗黙的(静的)リンク 明示的(動的)リンク
関数の宣言 DLLの関数に__declspecl(dllimport)を付けて宣言する。 DLLの関数を宣言せず、typedefする。
リンカー リンカーでライブラリファイルをリンクする必要がある。 リンカーでのリンクは不要である。
DLLの読み込み EXEの実行の準備段階でDLLを読み込む。
DLLが見つからない場合、EXEは実行されない。
LoadLibrary()関数でDLLを読み込む。
DLLが見つからない場合、NULLが返る。
DLL内の関数を実行するには、GetProcAddress()関数で関数アドレスを取得する必要がある。
関数との紐付け ライブラリファイルで、DLL内の関数名(序数も含む)が保持されている。
この関数名(あるいは序数)が一致しないとNULLが返る。
GetProcAddress()関数で、DLL内の関数名(あるいは序数)を指定する。
この関数名(または序数)が一致しないとNULLが返る。
暗黙的 / 明示的とは EXEの実行準備段階でDLLを自動で読み込むので、暗黙的という。 設計者がLoadLibrary()関数を使用してDLLを呼び出すので、明示的という。
静的 / 動的とは リンカーでリンクした時点で使用するDLLの種類や関数が決まるので、静的という。 LoadLibrary()関数とGetProcAddress()関数を使用してDLL内の関数を自由に使用できるので、動的という。