はじめに

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

Visual Studio 2022のインストールはこちら




DLLの作成

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

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

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

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

今回は入力値を100回加算して返す「Increment」関数を実装しました。この関数を呼び出し用アプリケーションから呼び出します。
なお、DLLで実装した関数をほかのアプリケーションで呼び出すには
  1. extern "C" __declspec(dllexport)
をつける必要があります。
C++にはオーバーロードがあるためコンパイル時にDLLで実装した関数名が変わってしまう可能性があるので「extern "C"」をつけることでそれを防ぎます。
  1. #include <iostream>
  2.  
  3. // 入力した値を100回加算して返すだけの関数
  4. extern "C" __declspec(dllexport) int Increment(int add) {
  5. int result = 0;
  6. for (int i = 0; i < 100; i++) {
  7. result += add;
  8. }
  9. return result;
  10. }
4


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

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

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

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

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

このアプリケーションでは数値が入力されたらDLLを読み込み、入力された数値をincrement関数に入力して出力しています。
※ LoadLibraryAやGetProcAddressを使用するためwindows.hをインクルードします。
  1. #include <iostream>
  2. #include <windows.h>
  3.  
  4. using namespace std;
  5.  
  6. // 関数型の定義(型名は任意)
  7. typedef int(__stdcall* INCREMENT)(int);
  8.  
  9. int main()
  10. {
  11. int input;
  12. cout << "入力待ち" << endl;
  13. cin >> input;
  14.  
  15. // DLLのパス
  16. const char* dllPath = "DLLTest.dll";
  17.  
  18. // DLLを読み込む
  19. // Unicodeの場合はLoadLibraryA
  20. // マルチバイトの場合はLoadLibrary
  21. HMODULE hDll = LoadLibraryA(dllPath);
  22. if (!hDll) {
  23. cerr << "DLLのロードに失敗しました。" << endl;
  24. return -1;
  25. }
  26.  
  27. // DLLの関数を読み込む
  28. INCREMENT increment = (INCREMENT)GetProcAddress(hDll, "Increment");
  29. if (!increment) {
  30. cerr << "関数の読み込みに失敗しました。" << endl;
  31. FreeLibrary(hDll);
  32. return -1;
  33. }
  34.  
  35. // 関数呼び出し
  36. int result = increment(input);
  37.  
  38. cout << "入力:" << input << endl;
  39. cout << "結果:" << result << endl;
  40.  
  41. return 0;
  42. }
ここでDLLの読み込みを行いますが、文字セットがUnicodeの場合はLoadLibraryが使用できないのでLoadLibraryAを使用します (Visual Studio 2022はデフォルトの設定がUnicodeになっていると思います)。
  1. // DLLを読み込む
  2. // Unicodeの場合はLoadLibraryA
  3. // マルチバイトの場合はLoadLibrary
  4. HMODULE hDll = LoadLibraryA(dllPath);
  5. if (!hDll) {
  6. cerr << "DLLのロードに失敗しました。" << endl;
  7. return -1;
  8. }
ここでDLLで実装した関数を読み込みます。
GetProcAddressの第2引数にDLLで定義した関数名を入力します。
  1. // DLLの関数を読み込む
  2. INCREMENT increment = (INCREMENT)GetProcAddress(hDll, "Increment");
  3. if (!increment) {
  4. cerr << "関数の読み込みに失敗しました。" << endl;
  5. FreeLibrary(hDll);
  6. return -1;
  7. }
実装が完了したらビルドしておきます。
9

動作確認

第1章で作成したDLLTest.dllを第2章で作成したReadDLLTestプロジェクトの
x64/Debug
の中にコピーします。

・DLLTestプロジェクト
10


・ReadDLLTestプロジェクト
11


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


結果はこちら。
14


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

DLLのデバッグ手順

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

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

16

17

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

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

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

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

今回は以上です。

参考文献・参考サイト

・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