【C++】Visual StudioでDLLをデバッグする方法

DLL(Dynamic Link Library)はプログラム実行時に動的に読み込むライブラリです。
特定の機能を他のアプリケーションでも使用したい場合に便利ですが、DLL単体では実行することができず呼び出し元のアプリケーションが必要になるため そのままではデバッグができません。
そこで今回はVisual StudioでDLLをデバッグする方法についてまとめました。
なお、本記事では

  • Windows11 64bit
  • Visual Studio 2022
  • C++

を想定しています。

1. DLLの作成

まずはDLLを作成します。
Visual Studio 2022を起動して「新しいプロジェクトを作成」を選択し、「DLL」と検索します。

表示された一覧の中から「ダイナミック リンク ライブラリ(DLL)」を選択します。

そしてDLL用のプロジェクトを作成します。今回のプロジェクト名は「DLLTest」としました。

プロジェクトを作成したらDllMainのみが実装された状態になっていると思いますので、呼び出す関数を実装します。

今回は入力値を100回加算して返す「Increment」関数を実装しました。この関数を呼び出し用アプリケーションから呼び出します。
なお、DLLで実装した関数をほかのアプリケーションで呼び出すには

extern "C" __declspec(dllexport) 

をつける必要があります。
C++にはオーバーロードがあるためコンパイル時にDLLで実装した関数名が変わってしまう可能性があるので「extern “C”」をつけることでそれを防ぎます。

#include  <iostream>

// 入力した値を100回加算して返すだけの関数
extern "C" __declspec(dllexport) int Increment(int add) {
    int result = 0;
    for (int i = 0; i < 100; i++) {
        result += add;
    }
    return result;
}

実装が完了したら正常にビルドできるか確認しておきます。

2. DLL呼び出し用アプリケーションの作成

次にDLLを呼び出すアプリケーションを作成します。
Visual Studio 2022を起動して「新しいプロジェクトを作成」を選択し、C++の「コンソール アプリ」を選択します。

そして呼び出し用アプリケーションのプロジェクトを作成しました。今回のプロジェクト名は「ReadDLLTest」としました。

プロジェクトが作成されたら最初はHell Worldのみが出力される状態になっていると思いますので
ここにDLLを呼び出す処理を実装します。

このアプリケーションでは数値が入力されたらDLLを読み込み、入力された数値をincrement関数に入力して出力しています。
※ LoadLibraryAやGetProcAddressを使用するためwindows.hをインクルードします。

#include <iostream>
#include <windows.h>

using namespace std;

// 関数型の定義(型名は任意)
typedef int(__stdcall* INCREMENT)(int);

int main()
{
    int input;
    cout << "入力待ち" << endl;
    cin >> input;

    // DLLのパス
    const char* dllPath = "DLLTest.dll";

    // DLLを読み込む
    // Unicodeの場合はLoadLibraryA
    // マルチバイトの場合はLoadLibrary
    HMODULE hDll = LoadLibraryA(dllPath);
    if (!hDll) {
        cerr << "DLLのロードに失敗しました。" << endl;
        return -1;
    }

    // DLLの関数を読み込む
    INCREMENT increment = (INCREMENT)GetProcAddress(hDll, "Increment");
    if (!increment) {
        cerr << "関数の読み込みに失敗しました。" << endl;
        FreeLibrary(hDll);
        return -1;
    }

    // 関数呼び出し
    int result = increment(input);

    cout << "入力:" << input << endl;
    cout << "結果:" << result << endl;

    return 0;
}

ここでDLLの読み込みを行いますが、文字セットがUnicodeの場合はLoadLibraryが使用できないのでLoadLibraryAを使用します (Visual Studio 2022はデフォルトの設定がUnicodeになっていると思います)。

// DLLを読み込む
// Unicodeの場合はLoadLibraryA
// マルチバイトの場合はLoadLibrary
HMODULE hDll = LoadLibraryA(dllPath);
if (!hDll) {
    cerr << "DLLのロードに失敗しました。" << endl;
    return -1;
}

ここでDLLで実装した関数を読み込みます。
GetProcAddressの第2引数にDLLで定義した関数名を入力します。

// DLLの関数を読み込む
INCREMENT increment = (INCREMENT)GetProcAddress(hDll, "Increment");
if (!increment) {
    cerr << "関数の読み込みに失敗しました。" << endl;
    FreeLibrary(hDll);
    return -1;
}

実装が完了したらビルドしておきます。

3. 動作確認

第1章で作成したDLLTest.dllを第2章で作成したReadDLLTestプロジェクトの

x64/Debug

の中にコピーします。

・DLLTestプロジェクト

・ReadDLLTestプロジェクト

次にReadDLLTestをデバッグします。

結果はこちら。

ReadDLLTest.exe、DLLTest.dllともに正常に動作していることが確認できました。

4. DLLのデバッグ手順

ここからが本題です。
DLL呼び出し用アプリケーション(ReadDLLTest.exe)はVisual StudioからそのままデバッグできますがDLL(DLLTest.dll)はできません。
しかし、ReadDLLTest.exeへアタッチすることでDLLのデバッグが可能になります。

①まずはDLLTest.dllの出力先をReadDLLTest.exeの実行ファイルと同じフォルダ変更し、DLLTestをビルドします。

②次にReadDLLTest.exeをデバッグなしで実行(もしくはReadDLLTest.exeをダブルクリックで実行)します。

③DLLTestのデバッグから「プロセスにアタッチ」を開きます。

④ プロセス一覧から「ReadDLLTest.exe」を選択し、「アタッチ」を選択します。

これでデバッグ出来るようになりました。
DLLTestのIncrement関数に入れたブレークポイントも機能していることが確認できました。

今回は以上です。

5. 参考文献・参考サイト

・DLL とは
https://learn.microsoft.com/ja-jp/troubleshoot/windows-client/setup-upgrade-and-drivers/dynamic-link-library

・Visual Studio で DLL プロジェクトからデバッグする (C#、C++、Visual Basic、F#)
https://learn.microsoft.com/ja-jp/visualstudio/debugger/how-to-debug-from-a-dll-project?view=vs-2022

・extern (C++)
https://learn.microsoft.com/ja-jp/cpp/cpp/extern-cpp?view=msvc-170#extern-c-and-extern-c-function-declarations

・__declspec(dllexport) を使った DLL からのエクスポート
https://learn.microsoft.com/ja-jp/cpp/build/exporting-from-a-dll-using-declspec-dllexport?view=msvc-170

・LoadLibraryA 関数 (libloaderapi.h)
https://learn.microsoft.com/ja-jp/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya

・GetProcAddress 関数 (libloaderapi.h)
https://learn.microsoft.com/ja-jp/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress

コメント

タイトルとURLをコピーしました