DLL
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
DLL(Dynamic Link Library)は実行時にリンクされるライブラリ...
実行時にリンクされるので、うまい作り方ができればプラグイ...
(実行ファイルを変えることなく、DLLのみ差し替えれば機能変...
今回はVisual C++を使って、DLLを作ってみます
Visual C++を起動し、新規プロジェクトを作ります
&ref(DLL1.JPG);
とりあえずコンソールを指定しておきます
(後の設定で変更する)
ここでプロジェクト名は自由に決めていいのですが
SIZE(30){ COLOR(#ffaa00){プロジェクト名は覚えておいてくだ...
&ref(DLL2.JPG);
「完了」を押さずに「次へ」を押してください
&ref(DLL3.JPG);
ここでDLLを選択します
空のプロジェクトにも忘れずにチェックを入れてください
チェックを入れたら完了を押してください
以下のファイルを新規作成します
・DLLMain.cpp
・DLLHeader.h
・TestClass.cpp
・TestClass.h
&ref(DLL4.JPG);
---------------------------------------------------------...
DLLMain.cppです
// DLLMain.cpp
#include <windows.h>
#include <tchar.h>
// DLLのエントリーポイント
// DLL読み込み時やDLL解放時に呼び出され
// DLLの初期化や後処理を行う
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // DLL モジュールのハンドル
DWORD fdwReason, // 関数を呼び出す理由
LPVOID lpvReserved // 予約済み
)
{
switch(fdwReason)
{
// このDLLの呼び出し元のアプリケーション(プロセス)が
// 動的な呼び出し、またLoadLibrary関数で
// このDLLを読み込んだときに行う処理(初期化に使う)
case DLL_PROCESS_ATTACH:
{
// 初期化処理
MessageBox(NULL,_T("DLL ロード"),NULL,MB_OK);
}
break;
// このDLLの呼び出し元のアプリケーション(プロセス)が
// 終了したあるいはそこからFreeLibrary関数が呼ばれたと...
// このDLLはアンロードされる
// アンロードされる前に行う処理(後処理に使う)
case DLL_PROCESS_DETACH:
{
// 後処理
MessageBox(NULL,_T("DLL 解放"),NULL,MB_OK);
}
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
DLLのエントリーポイントです
プロセスやスレッドの初期化時と終了時、
また、LoadLibrary 関数と FreeLibrary 関数の呼び出し時に、
システム(OS)がDLLMain関数を呼び出します。
上記の方法は明示的な呼び出しですが、
動的な呼び出しのほうがより一般的な使い方です
動的な呼び出しとはDLLと関連付けられたアプリケーションが起...
(より正確にはアプリケーションで最初に書かれたDLL側の関数...
DLL_PROCESS_ATTACHは呼ばれ、呼び出し元のアプリケーション...
DLLもDLL_PROCESS_DETACHが呼ばれ自動的に破棄されます
使い方としてはDLLの初期化処理や後処理などを行うといいでし...
(実はDLLMain関数は書かなくてもビルドできます)
---------------------------------------------------------...
DLLHeader.hです
このヘッダーはこのDLLを呼び出すアプリケーションでも必要に...
// DLLHeader.h
#pragma once
#ifdef TESTDLL_EXPORTS // DLL側プロジェクトで定義されて...
#define _EXPORT __declspec(dllexport)
#else
#define _EXPORT __declspec(dllimport)
#endif /* TESTDLL_EXPORTS */
他のアプリケーションからDLL側の関数やクラスを呼び出すため...
DLL側でそれらの関数やクラスを一回エクスポートし
呼び出し側ではそれらの関数やクラスをインポートする必要が...
(逆に言えば呼び出し側で直接使わせたくない関数やクラスに...
エクスポートするには次のようなエクスポートキーワードを
エクスポートする関数やクラスの宣言に付けます
__declspec(dllexport)
インポートするには次のようなインポートキーワードを
インポートする関数やクラスの宣言に付けます
__declspec(dllimport)
普通に作ると同じ関数やクラス宣言なのに
エクスポート用とインポート用の
2つのヘッダーを作る必要がでてきてしまうのですが
エクスポート用とインポート用を共通のヘッダーで済ます方法...
ここでポイントは次の一行です
#ifdef TESTDLL_EXPORTS // DLL側プロジェクトで定義されて...
これはどこで定義(#define)されているのでしょうか?
答えはプロジェクトのプロパティ→プリプロセッサの定義です
ここに書かれているものは同一プロジェクト内で#defineされて...
&ref(DLL5.JPG);
DLLプロジェクトを作ると自動的に
SIZE(30){ COLOR(#ffaa00){「プロジェクト名(大文字)_EXPORTS...
という定義がされます
他のプロジェクトでは定義がされていないので
DLL側とDLL呼び出しアプリケーション側で切り替えを行います
#ifdef TESTDLL_EXPORTS // DLL側プロジェクトで定義されて...
#define _EXPORT __declspec(dllexport)
#else
#define _EXPORT __declspec(dllimport)
#endif /* TESTDLL_EXPORTS */
つまり、
共通の#defineした_EXPORTを使うことで
DLL側、呼び出し側でエクスポート、インポートの切り替えが行...
DLL側、呼び出し側共通のヘッダーとして扱うことができます
したがって(エクスポートorインポート)する関数やクラスに...
使い方はTestClass.hで説明します
---------------------------------------------------------...
TestClass.hです
// TestClass.h
#pragma once
#include "DLLHeader.h"
// class エクスポート(インポート)キーワード クラス名
class _EXPORT TestClass
{
public:
TestClass(void);
virtual ~TestClass(void);
};
// 関数の場合は先頭にエクスポート(インポート)キーワー...
_EXPORT void Test();
// エクスポートしない関数やクラスにはキーワードを付けない
void InOnlyDLLCall();
DLLHeader.hヘッダーのインクルードを忘れないでください
クラスをエクスポート(インポート)するには
class エクスポート(インポート)キーワード クラス名を指定...
関数をエクスポート(インポート)するには
先頭にエクスポート(インポート)キーワードを付けます
エクスポートしない関数やクラスは何もつけません
この場合、呼び出し側ではこの関数やクラスを使うことはでき...
(DLL側内では使える)
---------------------------------------------------------...
TestClass.cppです
// TestClass.cpp
#include "TestClass.h"
#include <tchar.h>
#include <windows.h>
TestClass::TestClass(void)
{
MessageBox(NULL,_T("DLLクラス呼び出し"),NULL,MB_OK);
}
TestClass::~TestClass(void)
{
}
void Test(){
InOnlyDLLCall();
}
void InOnlyDLLCall(){
MessageBox(NULL,_T("DLL関数呼び出し"),NULL,MB_OK);
}
特に気を付けることは何もありません
ではビルドしてみましょう
ビルドに成功すると
DLLプロジェクトのDebug(もしくはRelease)フォルダに
DLLファイルとLIBファイルが作成されます
&ref(DLL6.JPG);
---------------------------------------------------------...
では、早速今作ったDLLを使ってみましょう
新規のプロジェクトを作ります
DLLのヘッダーファイルと作成したLIBファイル、DLLファイルが...
今作ったプロジェクトのフォルダにコピーしてください
&ref(DLL8.JPG);
main.cppを作り、DLLの関数やクラスを使ってみます
&ref(DLL7.JPG);
#include "TestClass.h"
#pragma comment(lib,"TestDLL.lib")
int main(){
TestClass testclass;
Test();
// エクスポートしてないのでリンカエラーとなる
//InOnlyDLLCall();
return 0;
}
ビルド→実行して
次のメッセージボックスが出てきたら成功です
&ref(DLL11.JPG);
ここで気を付けてほしいのはInOnlyDLLCall関数はエクスポート...
呼び出そうとするとリンカーエラーとなります
試しにコメントをはずすしてビルドすると次のエラーが出てき...
&ref(DLL9.JPG);
error LNKはリンカーのエラーを示しています
void _cdecl InOnlyDLLCall()の横に気味の悪い文字列が出てき...
?InOnlyDLLCall@@YAXXZ
これは関数やクラスの名前修飾と呼ばれ
本当の関数の名前です
@以下は関数の戻り値、引数を示しており
同じ名前の関数で引数、戻り値が違う(オーバーロードされて...
正しい関数を呼び出しできるのはこれのおかげです
ちなみに、名前修飾の付け方はコンパイラによって異なります
実は作成したLIBファイルにはDLLからエクスポートされた関数...
そのために、その情報を元にDLLの実装を実行時にリンクさせ
DLL側の関数やクラスを呼び出すことができるのです
コメントアウトを元に戻して
再度実行ファイルを作ります
実行ファイルを起動するにはDLLが必ず必要となります
(同じフォルダもしくはシステムの環境パスが通ってる箇所の...
&ref(DLL10.JPG);
さて、
実行(.exe)ファイルを変えずに機能を変えるにはどうしたら...
ここがプラグインを作るポイントなのですが
名前修飾の定義が変わらなければ
(エクスポートするクラスや関数の定義)
LIBファイルは変わりません
つまり、実行ファイルからのDLL側の関数もしくはクラスの呼び...
そこでDLL側の実装ファイルを変更してみましょう
// TestClass.cpp
#include "TestClass.h"
#include <tchar.h>
#include <windows.h>
TestClass::TestClass(void)
{
MessageBox(NULL,_T("DLLクラス呼び出し、差し替えver"),NU...
}
TestClass::~TestClass(void)
{
}
void Test(){
InOnlyDLLCall();
}
void InOnlyDLLCall(){
MessageBox(NULL,_T("DLL関数呼び出し、差し替えver"),NULL...
}
ビルドしてできたDLLファイルを先ほどの実行ファイルフォルダ...
(DLLファイルの名前は変えていけないのでそのまま上書きしま...
実行ファイルの起動結果は次のようになります
&ref(DLL12.JPG);
いかがだったでしょうか?
うまく設計すれば、DLLを差し替えるだけでアプリケーションを
バージョンアップさせることができます
これはまさにプラグインです
話はそれますが、COMはこの方法をきちんと体系化したものにす...
いちいちアプリケーション本体をビルドをしなくてよくなります
プログラムが大きくなるとアプリケーションのビルドは時間も...
DLLを使うことでビルド時間の短縮にもなります
そのほかにも共通の部品として扱えるようになるので
ファイルサイズの削減にもなります
---------------------------------------------------------...
今回のソースファイルは下からダウンロードできます
&ref(DLL.zip);
#vote((^ω^)やったお[1],何これwww意味不すぎwww[0...
終了行:
DLL(Dynamic Link Library)は実行時にリンクされるライブラリ...
実行時にリンクされるので、うまい作り方ができればプラグイ...
(実行ファイルを変えることなく、DLLのみ差し替えれば機能変...
今回はVisual C++を使って、DLLを作ってみます
Visual C++を起動し、新規プロジェクトを作ります
&ref(DLL1.JPG);
とりあえずコンソールを指定しておきます
(後の設定で変更する)
ここでプロジェクト名は自由に決めていいのですが
SIZE(30){ COLOR(#ffaa00){プロジェクト名は覚えておいてくだ...
&ref(DLL2.JPG);
「完了」を押さずに「次へ」を押してください
&ref(DLL3.JPG);
ここでDLLを選択します
空のプロジェクトにも忘れずにチェックを入れてください
チェックを入れたら完了を押してください
以下のファイルを新規作成します
・DLLMain.cpp
・DLLHeader.h
・TestClass.cpp
・TestClass.h
&ref(DLL4.JPG);
---------------------------------------------------------...
DLLMain.cppです
// DLLMain.cpp
#include <windows.h>
#include <tchar.h>
// DLLのエントリーポイント
// DLL読み込み時やDLL解放時に呼び出され
// DLLの初期化や後処理を行う
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // DLL モジュールのハンドル
DWORD fdwReason, // 関数を呼び出す理由
LPVOID lpvReserved // 予約済み
)
{
switch(fdwReason)
{
// このDLLの呼び出し元のアプリケーション(プロセス)が
// 動的な呼び出し、またLoadLibrary関数で
// このDLLを読み込んだときに行う処理(初期化に使う)
case DLL_PROCESS_ATTACH:
{
// 初期化処理
MessageBox(NULL,_T("DLL ロード"),NULL,MB_OK);
}
break;
// このDLLの呼び出し元のアプリケーション(プロセス)が
// 終了したあるいはそこからFreeLibrary関数が呼ばれたと...
// このDLLはアンロードされる
// アンロードされる前に行う処理(後処理に使う)
case DLL_PROCESS_DETACH:
{
// 後処理
MessageBox(NULL,_T("DLL 解放"),NULL,MB_OK);
}
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
DLLのエントリーポイントです
プロセスやスレッドの初期化時と終了時、
また、LoadLibrary 関数と FreeLibrary 関数の呼び出し時に、
システム(OS)がDLLMain関数を呼び出します。
上記の方法は明示的な呼び出しですが、
動的な呼び出しのほうがより一般的な使い方です
動的な呼び出しとはDLLと関連付けられたアプリケーションが起...
(より正確にはアプリケーションで最初に書かれたDLL側の関数...
DLL_PROCESS_ATTACHは呼ばれ、呼び出し元のアプリケーション...
DLLもDLL_PROCESS_DETACHが呼ばれ自動的に破棄されます
使い方としてはDLLの初期化処理や後処理などを行うといいでし...
(実はDLLMain関数は書かなくてもビルドできます)
---------------------------------------------------------...
DLLHeader.hです
このヘッダーはこのDLLを呼び出すアプリケーションでも必要に...
// DLLHeader.h
#pragma once
#ifdef TESTDLL_EXPORTS // DLL側プロジェクトで定義されて...
#define _EXPORT __declspec(dllexport)
#else
#define _EXPORT __declspec(dllimport)
#endif /* TESTDLL_EXPORTS */
他のアプリケーションからDLL側の関数やクラスを呼び出すため...
DLL側でそれらの関数やクラスを一回エクスポートし
呼び出し側ではそれらの関数やクラスをインポートする必要が...
(逆に言えば呼び出し側で直接使わせたくない関数やクラスに...
エクスポートするには次のようなエクスポートキーワードを
エクスポートする関数やクラスの宣言に付けます
__declspec(dllexport)
インポートするには次のようなインポートキーワードを
インポートする関数やクラスの宣言に付けます
__declspec(dllimport)
普通に作ると同じ関数やクラス宣言なのに
エクスポート用とインポート用の
2つのヘッダーを作る必要がでてきてしまうのですが
エクスポート用とインポート用を共通のヘッダーで済ます方法...
ここでポイントは次の一行です
#ifdef TESTDLL_EXPORTS // DLL側プロジェクトで定義されて...
これはどこで定義(#define)されているのでしょうか?
答えはプロジェクトのプロパティ→プリプロセッサの定義です
ここに書かれているものは同一プロジェクト内で#defineされて...
&ref(DLL5.JPG);
DLLプロジェクトを作ると自動的に
SIZE(30){ COLOR(#ffaa00){「プロジェクト名(大文字)_EXPORTS...
という定義がされます
他のプロジェクトでは定義がされていないので
DLL側とDLL呼び出しアプリケーション側で切り替えを行います
#ifdef TESTDLL_EXPORTS // DLL側プロジェクトで定義されて...
#define _EXPORT __declspec(dllexport)
#else
#define _EXPORT __declspec(dllimport)
#endif /* TESTDLL_EXPORTS */
つまり、
共通の#defineした_EXPORTを使うことで
DLL側、呼び出し側でエクスポート、インポートの切り替えが行...
DLL側、呼び出し側共通のヘッダーとして扱うことができます
したがって(エクスポートorインポート)する関数やクラスに...
使い方はTestClass.hで説明します
---------------------------------------------------------...
TestClass.hです
// TestClass.h
#pragma once
#include "DLLHeader.h"
// class エクスポート(インポート)キーワード クラス名
class _EXPORT TestClass
{
public:
TestClass(void);
virtual ~TestClass(void);
};
// 関数の場合は先頭にエクスポート(インポート)キーワー...
_EXPORT void Test();
// エクスポートしない関数やクラスにはキーワードを付けない
void InOnlyDLLCall();
DLLHeader.hヘッダーのインクルードを忘れないでください
クラスをエクスポート(インポート)するには
class エクスポート(インポート)キーワード クラス名を指定...
関数をエクスポート(インポート)するには
先頭にエクスポート(インポート)キーワードを付けます
エクスポートしない関数やクラスは何もつけません
この場合、呼び出し側ではこの関数やクラスを使うことはでき...
(DLL側内では使える)
---------------------------------------------------------...
TestClass.cppです
// TestClass.cpp
#include "TestClass.h"
#include <tchar.h>
#include <windows.h>
TestClass::TestClass(void)
{
MessageBox(NULL,_T("DLLクラス呼び出し"),NULL,MB_OK);
}
TestClass::~TestClass(void)
{
}
void Test(){
InOnlyDLLCall();
}
void InOnlyDLLCall(){
MessageBox(NULL,_T("DLL関数呼び出し"),NULL,MB_OK);
}
特に気を付けることは何もありません
ではビルドしてみましょう
ビルドに成功すると
DLLプロジェクトのDebug(もしくはRelease)フォルダに
DLLファイルとLIBファイルが作成されます
&ref(DLL6.JPG);
---------------------------------------------------------...
では、早速今作ったDLLを使ってみましょう
新規のプロジェクトを作ります
DLLのヘッダーファイルと作成したLIBファイル、DLLファイルが...
今作ったプロジェクトのフォルダにコピーしてください
&ref(DLL8.JPG);
main.cppを作り、DLLの関数やクラスを使ってみます
&ref(DLL7.JPG);
#include "TestClass.h"
#pragma comment(lib,"TestDLL.lib")
int main(){
TestClass testclass;
Test();
// エクスポートしてないのでリンカエラーとなる
//InOnlyDLLCall();
return 0;
}
ビルド→実行して
次のメッセージボックスが出てきたら成功です
&ref(DLL11.JPG);
ここで気を付けてほしいのはInOnlyDLLCall関数はエクスポート...
呼び出そうとするとリンカーエラーとなります
試しにコメントをはずすしてビルドすると次のエラーが出てき...
&ref(DLL9.JPG);
error LNKはリンカーのエラーを示しています
void _cdecl InOnlyDLLCall()の横に気味の悪い文字列が出てき...
?InOnlyDLLCall@@YAXXZ
これは関数やクラスの名前修飾と呼ばれ
本当の関数の名前です
@以下は関数の戻り値、引数を示しており
同じ名前の関数で引数、戻り値が違う(オーバーロードされて...
正しい関数を呼び出しできるのはこれのおかげです
ちなみに、名前修飾の付け方はコンパイラによって異なります
実は作成したLIBファイルにはDLLからエクスポートされた関数...
そのために、その情報を元にDLLの実装を実行時にリンクさせ
DLL側の関数やクラスを呼び出すことができるのです
コメントアウトを元に戻して
再度実行ファイルを作ります
実行ファイルを起動するにはDLLが必ず必要となります
(同じフォルダもしくはシステムの環境パスが通ってる箇所の...
&ref(DLL10.JPG);
さて、
実行(.exe)ファイルを変えずに機能を変えるにはどうしたら...
ここがプラグインを作るポイントなのですが
名前修飾の定義が変わらなければ
(エクスポートするクラスや関数の定義)
LIBファイルは変わりません
つまり、実行ファイルからのDLL側の関数もしくはクラスの呼び...
そこでDLL側の実装ファイルを変更してみましょう
// TestClass.cpp
#include "TestClass.h"
#include <tchar.h>
#include <windows.h>
TestClass::TestClass(void)
{
MessageBox(NULL,_T("DLLクラス呼び出し、差し替えver"),NU...
}
TestClass::~TestClass(void)
{
}
void Test(){
InOnlyDLLCall();
}
void InOnlyDLLCall(){
MessageBox(NULL,_T("DLL関数呼び出し、差し替えver"),NULL...
}
ビルドしてできたDLLファイルを先ほどの実行ファイルフォルダ...
(DLLファイルの名前は変えていけないのでそのまま上書きしま...
実行ファイルの起動結果は次のようになります
&ref(DLL12.JPG);
いかがだったでしょうか?
うまく設計すれば、DLLを差し替えるだけでアプリケーションを
バージョンアップさせることができます
これはまさにプラグインです
話はそれますが、COMはこの方法をきちんと体系化したものにす...
いちいちアプリケーション本体をビルドをしなくてよくなります
プログラムが大きくなるとアプリケーションのビルドは時間も...
DLLを使うことでビルド時間の短縮にもなります
そのほかにも共通の部品として扱えるようになるので
ファイルサイズの削減にもなります
---------------------------------------------------------...
今回のソースファイルは下からダウンロードできます
&ref(DLL.zip);
#vote((^ω^)やったお[1],何これwww意味不すぎwww[0...
ページ名: