自作dllの作り方

make-my-dll.png

ゴール

  • プロジェクト名.dll
  • プロジェクト名.lib
  • .hファイル

つまりは自作ライブラリです
DLL を呼び出す実行可能ファイルをビルドするときに、リンカーは、.lib ファイルで、エクスポートされたシンボルを使用して、この情報をローダーに格納します。
ローダーが DLL を読み込むと、DLL は実行可能ファイルのメモリ領域にマップされます。
DLL 内の特殊な関数である DllMain は、DLL が必要とするすべての初期化を実行するために呼び出されます。[2]

自作dllの作り方

VisualStudioを立ち上げる。

新規作成->プロジェクト->Win32プロジェクト

selectproj.png
ラジオボタンのDLLを選ぶ。
シンボルのエクスポートにチェックを入れる。
共通ヘッダー ファイルを追加: は何もチェックしない。
「完了」を押す。
appwizerd.png
最初こんなかんじのコードが現れると思う。
// dlltry.cpp : DLL アプリケーション用にエクスポートされる関数を定義します。
//
#include "stdafx.h"
#include "dlltry.h"
// これは、エクスポートされた変数の例です。
DLLTRY_API int ndlltry=0;
// これは、エクスポートされた関数の例です。
DLLTRY_API int fndlltry(void)
{
    return 42;
}
// これは、エクスポートされたクラスのコンストラクターです。
// クラス定義に関しては dlltry.h を参照してください。
Cdlltry::Cdlltry()
{
    return;
}

モジュール定義ファイルを指定する(Optional)

モジュール定義ファイルという.defのファイルを作り、
リンカ->入力->モジュール定義ファイル
で作った.defファイルを指定する。
dllexport として宣言すると、少なくともエクスポートした関数の指定には、モジュール定義 (.def) ファイルが不要になります。
dllexport 属性は __export キーワードに置き換わるものです。

インポートする時、エクスポートする時で処理を分けるマクロ

// MathFuncsDll.h
#ifdef MATHFUNCSDLL_EXPORTS
#define MATHFUNCSDLL_API __declspec(dllexport) 
#define MATHFUNCSDLL_API __declspec(dllimport)
#endif

上のコードの場合、このMATHFUNCSDLL_APIというマクロをどの関数にもつけるのだ。以下のように
MathFuncsDll.hの続き
//MathFuncsDll.hの続き
namespace MathFuncs
{
    // This class is exported from the MathFuncsDll.dll
    class MyMathFuncs
    {
    public:
        // Returns a + b
        static MATHFUNCSDLL_API double Add(double a, double b);
 
        // Returns a - b
        static MATHFUNCSDLL_API double Subtract(double a, double b);
 
        // Returns a * b
        static MATHFUNCSDLL_API double Multiply(double a, double b);
 
        // Returns a / b
        // Throws const std::invalid_argument& if b is 0
        static MATHFUNCSDLL_API double Divide(double a, double b);
    };
}

関数の中身 ここが自作ライブラリなところ

この中身は、dllを使う側には詳しく見れないようになってるので、ここに秘伝のソースコードを書くところです

// MathFuncsDll.cpp : Defines the exported functions for the DLL application.
//
 
#include "stdafx.h"
#include "MathFuncsDll.h"
#include <stdexcept>
 
using namespace std;
 
namespace MathFuncs
{
    double MyMathFuncs::Add(double a, double b)
    {
        return a + b;
    }
 
    double MyMathFuncs::Subtract(double a, double b)
    {
        return a - b;
    }
 
    double MyMathFuncs::Multiply(double a, double b)
    {
        return a * b;
    }
 
    double MyMathFuncs::Divide(double a, double b)
    {
        if (b == 0)
        {
            throw invalid_argument("b cannot be zero!");
        }
 
        return a / b;
    }
}

declspecとは?
そして、このファイル.hはメインでインクルードするヘッダからインクルードされてる形にする

外から使用したいもににはすべて

dllプロジェクトをビルド

ビルドすると、プロジェクト名.dllが作られます。

自作dllを使ってみよう

インクルードファイル

まずは作った.hファイルをインクルードする。
追加のインクルードパスに.hファイルへのパスを追加する

.libファイル

リンカー->全般->追加のライブラリディレクトリ

に自分の作った.libファイルのありかを書く

リンカー->追加の依存ファイル

に自分の作った.libファイルを書く

ソースコード

// MyExecRefsDll.cpp
// compile with: /EHsc /link MathFuncsDll.lib
 
#include <iostream>
 
#include "MathFuncsDll.h"
int main(int argc, char **argv)
{
 
    double a = 7.4;
    int b = 99;
 
    cout << "a + b = " <<
        MathFuncs::MyMathFuncs::Add(a, b) << endl;
    cout << "a - b = " <<
        MathFuncs::MyMathFuncs::Subtract(a, b) << endl;
    cout << "a * b = " <<
        MathFuncs::MyMathFuncs::Multiply(a, b) << endl;
    cout << "a / b = " <<
        MathFuncs::MyMathFuncs::Divide(a, b) << endl;
 
    try
    {
        cout << "a / 0 = " <<
            MathFuncs::MyMathFuncs::Divide(a, 0) << endl; 
    }
    catch (const invalid_argument &e) 
    {
        cout << "Caught exception: " << e.what() << endl; 
    }
 
    return 0;
}

読み込みは成功したか?

出力ウィンドウに

シンボルが読み込まれました

と出れば成功。

Cannot find or open PDB file

と出たら失敗である。
PDB fileとは何か?
デバッグとプロジェクト状態情報を保持します。この情報により、プログラムのデバッグ構成のインクリメンタル リンクが可能になります。/ZI または /Zi (C/C++ 用) を使用して構築すると、PDB ファイルが作成されます。
だそうだ。[1]
普通にやってればpdbファイルは作成される。dllをコピーする時に、.pdbファイルも一緒に引越しするのを忘れたがためにこのことが起こった。

ありうるエラー

LNK1103 ファイル'OOO.lib'を開くことができません

.libの探し先ディレクトリが間違ってるか、ooo.libのスペルミス

未解決の外部シンボル

  • インクルードファイルは見つかっているが、.libがないorみつからないせい
  • 上の例でのMATHFUNCSDLL_API など外から使いますよの印となるマクロを関数宣言に書いていないせい

コンピュータにoooo.dllがないため、プログラムを開始できません。この問題を解決するには、プログラムを再インストールしてみてください。

.dllが見つかってないせい。
.dllを探しに行くパスは

  • アプリケーション(*.exe)と同じフォルダ
  • カレントディレクトリ
  • システムディレクトリ(C:\Windows\System32 など)
  • 16Bitシステムディレクトリ(C:\Windows\System など)
  • Windowsディレクトリ(C:\Windows など)
  • PATH環境変数に列挙されているディレクトリ

なので上記のどれかに.dllを置きましょう

サポートサイト Wikidot.com