VC++ LIB、DLLの作成と参照方法

概要

ライブラリ

再利用可能なプログラム
ライブラリの種類
スタティックリンクライブラリ
~.lib
静的。
コンパイル時にリンクされ、実行ファイル(exe)になる。
exeだけで実行できる反面、libの内容変更時は再リンク、再リリースが必要。
ダイナミックリンクライブラリ
~.dll
動的。
実行ファイル(exe)から実行時に呼び出される。
exeだけでなく、dllのリリースも必要な反面、dllの内容変更時はdllの再リリースだけで済む。

コンパイル、リンク手順

ソースコード1
←(コンパイラ)
ネイティブコード(機械語)1
ソースコード2
←(コンパイラ)
ネイティブコード(機械語)1
ネイティブコード1+ネイティブコード2
←(リンカ)
実行ファイル(exe)

DLLにおけるリンク方法

暗黙的リンク
.libファイル内にdllの関数情報を保持。
libファイル内に関数情報を出力するには、__declspec(dllexport) を関数に付ける。
リンカはこれを使用してリンクを行いDLLを生成する。
明示的リンク
.defファイル内にdllの関数情報を保持。
リンカはこれを使用してリンクを行う。
libファイルは必要無い。

通常関数

スタティックリンクライブラリ(lib)

【呼び出し先】
※~.h
namespace NS_MyFunc
{
 int fnPlus(int prm1, int prm2);
}

※~.cpp
#include "MyFunc.h"
namespace NS_MyFunc
{
 int fnPlus(int prm1, int prm2)
 {
  return prm1 + prm2;
 }
}
vc_dll1
これにより~.libファイルが作成される。

【呼び出し元】
※~.cpp
#include "MyFunc.h"
// ライブラリ参照先をソースで指定する場合
#pragma comment( lib, "~.lib" );
using namespace NS_MyFunc;
int _tmain(int argc, _TCHAR* argv[])
{
 int ret = fnPlus(5, 5);
 →ret:10
 return 0;
}

vc_dll2
vc_dll3
(1)ヘッダーファイルの参照(インクルードディレクトリ、インクルードファイルの指定)
(2)ライブラリの参照(ライブラリディレクトリの指定)
(3)ライブラリの参照(ライブラリファイルの指定)
が必要。
これにより~.libファイルを参照/利用できる。

暗黙的リンク(lib+dll)

【呼び出し先】
※~.h
__declspec(dllexport) int __stdcall fnPlus(int prm1, int prm2)
{
 return prm1 + prm2;
};
※__declspec(dllexport)により関数情報がlibファイルに書き込まれる。
vc_dll5

【呼び出し元】
※~.h
#pragma comment( lib, "MyFunc.lib" )
※libファイルの参照(dllも同階層に保存しておく事)
__declspec(dllexport) int __stdcall fnPlus(int prm1, int prm2);
またはヘッダーファイルのinclude

int _tmain(int argc, _TCHAR* argv[])
{
 int ret = fnPlus(5, 10);
 →ret:15
 
 return 0;
}

明示的リンク(dll)

【呼び出し先】
※~.cpp
int __stdcall fnPlus(int prm1, int prm2)
{
 return prm1 + prm2;
};
※MyFunc.def
LIBRARY MyFunc
EXPORTS
 fnPlus
vc_dll7
vc_dll5

【呼び出し元】
※~.cpp
// 関数ポインタ
typedef int(__stdcall *p)(int prm1, int prm2);
// DLLのロード
HMODULE hModule = LoadLibrary(_T("MyFunc.dll"));
if (hModule == NULL)
{
 printf("%s", "DLLのロードに失敗しました。");
 return 0;
};

// 関数のアドレス取得
p func = (p)GetProcAddress(hModule, "fnPlus");
if (func == NULL)
{
 printf("%s", "関数のアドレス取得に失敗しました。");
 FreeLibrary(hModule);
 return 0;
}

// 実行
int ret = func(5, 10);
FreeLibrary(hModule);

クラス

スタティックリンクライブラリ(lib)

【呼び出し先】
※~.cpp
namespace NS_MyClass
{
 MyClass::MyClass(int prm1, int prm2)
 {
  this->myInt1 = prm1;
  this->myInt2 = prm2;
 }
 
 int MyClass::fnPlus()
 {
  return this->myInt1 + this->myInt2;
 }
}
vc_dll1

【呼び出し元】
※~.cpp
#include "MyClass.h"
#pragma comment( lib, "MyClass.lib" )
using namespace NS_MyClass;
int _tmain(int argc, _TCHAR* argv[])
{
 MyClass *m = new MyClass(5, 10);
 int ret = m->fnPlus();
 
 return 0;
}

暗黙的リンク(lib+dll)

【呼び出し先】
※~.h
namespace NS_MyClass
{
 class MyClass
 {
 private:
  int myInt1;
  int myInt2;
 public:
  __declspec(dllexport) MyClass(int prm1, int prm2);
  __declspec(dllexport)int __stdcall fnPlus();
  ※__declspec(dllexport)定義された関数はライブラリに公開される。
 };
}
※~.cpp
#include "MyClass.h"
namespace NS_MyClass
{
 MyClass::MyClass(int prm1, int prm2)
 {
  this->myInt1 = prm1;
  this->myInt2 = prm2;
 }
 __declspec(dllexport)int __stdcall MyClass::fnPlus()
 {
  return this->myInt1 + this->myInt2;
 }
}
構成の種類は「スタティックライブラリ」
通常関数」参照

【呼び出し元】
#include "MyClass.h"
using namespace NS_MyClass;
int _tmain(int argc, _TCHAR* argv[])
{
 MyClass *myClass = new MyClass(5, 5);
 int ret = myClass->fnPlus();
 →ret:10
 return 0;
}
・ヘッダーファイル参照方法(インクルードディレクトリ)
・ライブラリ参照方法(ライブラリディレクトリ)
・リンカー入力指定(ライブラリファイル名)
は通常関数と同じ。
通常関数」参照

明示的リンク(dll)

【呼び出し先】
※~.h
class MyClass
{
public:
 クラス外から呼び出せるフレンド関数を定義
 friend MyClass* CreateInstance();
 friend void ReleseInstance(MyClass* p);
public:
 メンバー関数は仮想関数でないとダメ(らしい)
 virtual int __thiscall fnPlus(int prm1, int prm2);
};
// MyClassのインスタンス(のアドレス)を返す
MyClass* CreateInstance() {
 return new MyClass;
}
// インスタンスを破棄
void ReleseInstance(MyClass* p) {
 delete p;
}
int __thiscall MyClass::fnPlus(int prm1, int prm2)
{
 return prm1 + prm2;
}

※MyClass.def
LIBRARY MyClass
EXPORTS
 fnPlus
vc_dll6
※これによりdefファイルの内容がdllに埋め込まれる。
vc_dll5

【呼び出し元】
int _tmain(int argc, _TCHAR* argv[])
{

 // DLLのロード
 HMODULE hModule = LoadLibrary(_T("MyClass.dll"));
 if (hModule == NULL)
 {
  FreeLibrary(hModule);
  return 0;
 }
 
 // DLL内のCreateInstanceメソッドを取得
 typedef MyClass* (__thiscall *CreateInstance_)();
 CreateInstance_ c = (CreateInstance_)GetProcAddress(hModule, "CreateInstance");
 if (c == NULL)
 {
  FreeLibrary(hModule);
  return 0;
 }
 
 // DLL内のReleseInstanceメソッドを取得
 typedef void(__thiscall *ReleseInstance_)(MyClass* p);
 ReleseInstance_ r = (ReleseInstance_)GetProcAddress(hModule, "ReleseInstance");
 if (r == NULL)
 {
  FreeLibrary(hModule);
  return 0;
 }
 
 // CreateInstanceメソッドを実行し、MyClassクラスのインスタンスを作成
 MyClass *m = c();
 
 // メンバー関数を実行
 int ret = m->fnPlus(5, 10);
 
 // ReleseInstanceメソッドを実行し、MyClassクラスのインスタンスを破棄
 r(m);
 
 FreeLibrary(hModule);
 
 return 0;
}

呼び出し規約

関数における引数の保存先等のルール
異なる言語の関数を呼ぶ場合、規約を合わせる必要がある。
__stdcall
WIN32 API規約
スタックの解放を呼ばれた側が行う。
__cdecl
C言語規約
スタックの解放を呼ぶ側が行う
__thiscall
C++メンバ関数規約
クラスのメンバ関数に使用
clrcall
.NET Framework専用規約