「ライブラリの基礎 - DLLの作成(C/C++/MFC)」の版間の差分
編集の要約なし |
|||
1行目: | 1行目: | ||
== 概要 == | == 概要 == | ||
MFC DLLには、以下の2つの種類がある。<br> | |||
* 拡張DLL | |||
*: DLLを使用するEXEやDLLもMFCで作成する時にのみ使用する。 | |||
* MFCの共有DLL(Regular DLL) | |||
*: 内部的にMFCそのものを持っているため、DLLを使用するEXEやDLLをMFCで作成しない時でも使用できる。 | |||
<br><br> | <br><br> | ||
== DLLの作成方法 | == DLLの作成方法 == | ||
==== defファイルを使用する ==== | |||
defファイルを作成する方法を記載する。<br> | defファイルを作成する方法を記載する。<br> | ||
<br> | <br> | ||
18行目: | 21行目: | ||
<br> | <br> | ||
<u>※注意</u><br> | <u>※注意</u><br> | ||
<u>C# | <u>C#にて作成したモジュール(EXEまたはDLL)からC++ DLLを呼び出す場合、C++ DLLではdefファイルを使用すること。</u><br> | ||
<br> | <br> | ||
defファイルを作成して、以下のようにエクスポートする関数を記載する。<br> | |||
'''※但し、"@1"等の序数値は記載しなくてもよい'''。<br> | '''※但し、"@1"等の序数値は記載しなくてもよい'''。<br> | ||
<source lang="c++"> | <source lang="c++"> | ||
65行目: | 68行目: | ||
} | } | ||
</source> | </source> | ||
<br> | |||
==== defファイルを使用しない ==== | |||
== | |||
defファイルを作成しない方法を記載する。<br> | defファイルを作成しない方法を記載する。<br> | ||
<br> | <br> | ||
126行目: | 128行目: | ||
<br><br> | <br><br> | ||
== Visual | == DLLの使用 == | ||
Visual StudioでDLLを作成している場合、プロジェクトから参照に追加することで使用できる。<br> | |||
他のIDE等でDLLを作成している場合、共有ライブラリとして使用するには、コンパイル時に以下の3つが必要となる。<br> | |||
* ヘッダーファイル (.h) | |||
* インポートライブラリ (.lib) | |||
* DLL | |||
<br> | |||
また、外部から呼び出せる関数が存在しない場合、インポートライブラリ(.lib)は作成されない。<br> | |||
<br><br> | |||
== インポートライブラリの作成 == | |||
インポートライブラリ(.lib)が提供されていない場合、DLLファイルから作成できる。<br> | |||
<br> | |||
まず、dumpbinを実行して、エクスポートされた全ての定義をファイルに出力する。<br> | |||
dumpbin /exports target.dll > exports.txt | |||
<br> | |||
1行目にLIBRARY DLL名、2行目にEXPORTSを記述をしたDEFファイルを作成する。<br> | |||
echo LIBRARY <Dllファイル名> > target.def | |||
echo EXPORTS >> target.def | |||
<br> | |||
DEFファイルに、最初に出力したファイルから関数の定義部分だけを追記する。<br> | |||
for /f "skip=19 tokens=4" %A in (exports.txt) do echo %A >> target.def | |||
<br> | |||
libコマンドを実行して、DEFファイルからインポートライブラリを作成する。<br> | |||
# 32bitを対象とする場合 | |||
lib /def:target.def /out:target.lib /machine:x86 | |||
# 64bitを対象とする場合 | |||
lib /def:target.def /out:target.lib /machine:x64 | |||
<br> | |||
インポートライブラリを参照するには、リンカーのオプションにて、[追加の依存ファイル]で指定する。<br> | |||
これは、/DYNAMICBASEオプションを指定することと同じことである。<br> | |||
または、ソースコード内において、#pragmaディレクティブで指定する。<br> | |||
<br><br> | |||
== DLLの暗黙的リンク == | |||
EXEファイルのプロジェクトに、DLLファイルを暗黙的リンクする方法を記載する。<br> | EXEファイルのプロジェクトに、DLLファイルを暗黙的リンクする方法を記載する。<br> | ||
例として、CppEXE.exeとCppDLL.dllが存在するものとして設定している。<br> | 例として、CppEXE.exeとCppDLL.dllが存在するものとして設定している。<br> | ||
155行目: | 192行目: | ||
|} | |} | ||
</center> | </center> | ||
<br><br> | |||
== dumpbin == | |||
DLLファイルのエクスポートされた関数名は、dumpbinで確認することができる。<br> | |||
<br> | |||
PowerShellまたはコマンドプロンプトを起動して、以下のコマンドを実行する。<br> | |||
この時、name項目に関数名が表示される。<br> | |||
dumpbin.exe /EXPORTS <DLLファイル名> | |||
# 出力 | |||
Microsoft (R) COFF/PE Dumper Version 9.00.30729.01 | |||
Copyright (C) Microsoft Corporation. All rights reserved. | |||
Dump of file <DLLファイル名> | |||
File Type: DLL | |||
Section contains the following exports for sample.dll | |||
00000000 characteristics | |||
49A74D91 time date stamp Fri Feb 27 01:18:57 2009 | |||
0.00 version | |||
1 ordinal base | |||
1 number of functions | |||
1 number of names | |||
ordinal hint RVA name | |||
1 0 00001034 FunctionName | |||
Summary | |||
1000 .data | |||
1000 .pdata | |||
1000 .rdata | |||
1000 .reloc | |||
1000 .text | |||
<br><br> | <br><br> | ||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:C]][[カテゴリ:C++]][[カテゴリ:MFC]] | [[カテゴリ:C]][[カテゴリ:C++]][[カテゴリ:MFC]] |
2020年10月17日 (土) 01:04時点における版
概要
MFC DLLには、以下の2つの種類がある。
- 拡張DLL
- DLLを使用するEXEやDLLもMFCで作成する時にのみ使用する。
- MFCの共有DLL(Regular DLL)
- 内部的にMFCそのものを持っているため、DLLを使用するEXEやDLLをMFCで作成しない時でも使用できる。
DLLの作成方法
defファイルを使用する
defファイルを作成する方法を記載する。
defファイルとは、DLLがエクスポートする関数を記述したファイルのことである。
[ソリューションエクスプローラ]を右クリック - [追加] - [新しい項目]を選択して、[新しい項目の追加]ダイアログにて"<ファイル名>.def"を追加すると、defファイルが作成される。
※ C++でDLLを作成する場合は、defファイルを作成する方法を強く推奨する。
[プロジェクト]メニューバー - [プロパティ]を選択して、[プロパティ]ダイアログを表示する。
[プロパティ]ダイアログの[リンカー] - [入力] - [モジュール定義ファイル]項目に使用するdefファイル名を記述する。
※注意
C#にて作成したモジュール(EXEまたはDLL)からC++ DLLを呼び出す場合、C++ DLLではdefファイルを使用すること。
defファイルを作成して、以下のようにエクスポートする関数を記載する。
※但し、"@1"等の序数値は記載しなくてもよい。
MainDLL.def
LIBRARY MainDLL
EXPORTS
SampleFunc @1
TestFunc @2
次に、"MainDLL.h"ファイルは以下のように記載する。
defファイルによるエクスポートを採用した場合、 エクスポート関数名にキーワードを付ける必要は無い。
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)
{
// 以下略
}
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)
{
// 以下略
}
DLLの使用
Visual StudioでDLLを作成している場合、プロジェクトから参照に追加することで使用できる。
他のIDE等でDLLを作成している場合、共有ライブラリとして使用するには、コンパイル時に以下の3つが必要となる。
- ヘッダーファイル (.h)
- インポートライブラリ (.lib)
- DLL
また、外部から呼び出せる関数が存在しない場合、インポートライブラリ(.lib)は作成されない。
インポートライブラリの作成
インポートライブラリ(.lib)が提供されていない場合、DLLファイルから作成できる。
まず、dumpbinを実行して、エクスポートされた全ての定義をファイルに出力する。
dumpbin /exports target.dll > exports.txt
1行目にLIBRARY DLL名、2行目にEXPORTSを記述をしたDEFファイルを作成する。
echo LIBRARY <Dllファイル名> > target.def echo EXPORTS >> target.def
DEFファイルに、最初に出力したファイルから関数の定義部分だけを追記する。
for /f "skip=19 tokens=4" %A in (exports.txt) do echo %A >> target.def
libコマンドを実行して、DEFファイルからインポートライブラリを作成する。
# 32bitを対象とする場合 lib /def:target.def /out:target.lib /machine:x86 # 64bitを対象とする場合 lib /def:target.def /out:target.lib /machine:x64
インポートライブラリを参照するには、リンカーのオプションにて、[追加の依存ファイル]で指定する。
これは、/DYNAMICBASEオプションを指定することと同じことである。
または、ソースコード内において、#pragmaディレクティブで指定する。
DLLの暗黙的リンク
EXEファイルのプロジェクトに、DLLファイルを暗黙的リンクする方法を記載する。
例として、CppEXE.exeとCppDLL.dllが存在するものとして設定している。
- まず、CppEXE.exeのプロジェクトを起動する。
- [プロジェクト]メニュー - [CppEXEのプロパティ] - [構成プロパティ] - [C/C++] - [全般] - [追加のインクルードディレクトリ]項目に、
CppDLL.dllのヘッダファイル(CppDLL.h)が存在するディレクトリを追加する。 - 次に、[構成プロパティ] - [リンカー] - [全般] - [追加のライブラリディレクトリ]項目に、
DLLのライブラリファイル(CppDLL.lib)が存在するディレクトリを追加する。 - 最後に、[構成プロパティ] - [リンカー] - [入力] - [追加の依存ファイル]項目に、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内の関数を自由に使用できるので、動的という。 |
dumpbin
DLLファイルのエクスポートされた関数名は、dumpbinで確認することができる。
PowerShellまたはコマンドプロンプトを起動して、以下のコマンドを実行する。
この時、name項目に関数名が表示される。
dumpbin.exe /EXPORTS <DLLファイル名> # 出力 Microsoft (R) COFF/PE Dumper Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file <DLLファイル名> File Type: DLL Section contains the following exports for sample.dll 00000000 characteristics 49A74D91 time date stamp Fri Feb 27 01:18:57 2009 0.00 version 1 ordinal base 1 number of functions 1 number of names ordinal hint RVA name 1 0 00001034 FunctionName Summary 1000 .data 1000 .pdata 1000 .rdata 1000 .reloc 1000 .text