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. 参考文献・参考サイト
・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




コメント