Delphi Starter Edition でのデバッグ

# はじめに

これは [Delphi Advent Calendar 2017](https://qiita.com/advent-calendar/2017/delphi)...とは全く関係のない記事です (w

Delphi Starter Edition では Professional Edition 以上の SKU で使えるデバッグ機能が一部削られています。ではどうやってデバッグするのか？というお話です。

# デバッグ

まず、デバッグ検証用のコードを用意します。プロジェクトを新規作成し、フォームにメモ (TMemo) とボタン (TButton) をそれぞれ一つずつ貼り付けます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/dca32d9f-5530-2411-6bab-08695f873bfb.png)

Button1 のイベントハンドラにコードを記述します。内容は Fizz Buzz です。

```pascal:
procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
  s: String;
begin
  Memo1.Lines.Clear;
  for i:=1 to 100 do
    begin
      if ((i mod 3) + (i mod 5)) = 0 then
        s := 'Fizz Buzz'
      else if (i mod 3) = 0 then
        s := 'Fizz'
      else if (i mod 5) = 0 then
        s := 'Buzz'
      else
        s := i.ToString;
      Memo1.Lines.Append(s);
    end;
end;
```

## 通常のデバッグ

デバッグ実行するためには**デバッグビルド**である必要があります。まずはここをプロジェクトマネージャで確認してください。**リリースビルド**だとデバッグ情報が実行ファイルに埋め込まれないため、実行ファイルのサイズは小さくなります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/b3662f0f-8767-7b48-4042-f5a6a9236354.png)

次に**ブレークポイント**を置きます。ブレークポイントはコードエディタで〔F5〕キーで押すかマウスで行番号の左をクリックすると設置できます。

 - [ブレークポイントを設定し変更する (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E3%83%96%E3%83%AC%E3%83%BC%E3%82%AF%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%97%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/a36a5028-e24a-2d6e-b112-e941a3856711.png)

一旦コンパイルすると青いポイントが出てくるのでブレークポイントを設置可能な場所が一目で判るようになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/a6e21b99-16b7-e58a-2946-3187093dac72.png)

そして〔F9〕で実行するとデバッグ実行が開始されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/96f805ca-b9a2-cb43-5062-70bb04e532b4.png)

Button1 を押すと...

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/50070a18-da5b-4155-257a-4c5b312c2ef2.png)

ブレークポイントで止まります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/f0c44498-5f6a-3b76-ccfb-36b0397ac490.png)

ここで Professional Edition 以上の SKU だと変数にマウスカーソルを合わせると値が表示される**ツールチップ式評価**があるのですが、この機能は残念ながら Starter Edition にはありません。

 - [デバッグ中にツールチップを使用する (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E4%B8%AD%E3%81%AB%E3%83%84%E3%83%BC%E3%83%AB%E3%83%81%E3%83%83%E3%83%97%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/fa589d8f-6439-7323-de47-b514124a369a.png)

ではどうするのかといいますと、変数にカーソルを合わせ〔Ctrl〕+〔F5〕で変数を**監視式**に追加します。監視式への追加は右クリックから [デバッグ | カーソル位置の単語を監視] でも行えます。 監視式のウィンドウが出ていない場合には [表示 | デバッグ | 監視式] で表示します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/8cc45544-a7e8-f026-930c-f32f9876ea65.png)

この一覧を右クリックし [監視式の追加] を行っても監視式を追加できます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/62fecbbf-851c-f8f9-69e7-3b4a82aa56b4.png)

監視式に値を追加した状態で〔F8〕を押すと**ステップ実行**できます。〔F9〕を押すと次のブレークポイントまで実行されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/fc2f8b8a-97c0-5933-4aed-623f37a45b7c.png)

それから、変数 i の値が 50 の時の状態を見るのに〔F9〕を何度も押すのは面倒なのですが、そういう場合にはブレークポイントを右クリックして、**[ブレークポイントの設定]**を選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/9c474447-3501-c069-49cb-db05807b70dd.png)

そして [ブレーク条件に] に "i=50" と書けば i の値が 50 になった時にブレークポイントでブレークします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/9710948b-b350-cb8c-86f6-2957589de4a8.png)

実はまぁまぁデバッグできるのですが、上位エディションを使っていると**「面倒くさいなぁ」**と思う事もよくあります。

**See Also:**

 - [デバッガを使用する (DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E6%93%8D%E4%BD%9C%E6%89%8B%E9%A0%86%EF%BC%9A%E3%83%87%E3%83%90%E3%83%83%E3%82%AC%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B)


## インライン評価プラグイン DLight

ツールチップ式評価の簡単な代替は[らいなたんさん](http://d.hatena.ne.jp/tales/)の **DLight プラグイン**を使う事でしょう。

![687474703a2f2f692e696d6775722e636f6d2f576859547041482e676966.gif](https://qiita-image-store.s3.amazonaws.com/0/21785/39a22823-0f4f-705a-a02e-d78591c9a7ac.gif)

 - [インライン評価プラグイン DLight (GitHub)](https://github.com/lynatan/DLight)

機能については見ての通りで、何の説明も要りませんよね！


## DebugView

すべての言語においてよくやるデバッグ手法に **Print デバッグ**というのがあります。これは標準出力などに文字列を出力してデバッグするというものです。原始的ですが効果的にデバッグできる事もあります。

さて、コンソールアプリケーションならともかく、GUI アプリケーションで Print デバッグを行うにはどうすればいいでしょう？

 - TMemo をフォームの端っこに貼って TMemo.Lines.Append() で文字列を追加する。リリース時には TMemo の Visible を False にする。
 - ShowMessage() で文字列をポップアップ表示する。リリース時にはコメントアウトする。

そういうのもいいのですが、Windows 用のデバッグとして OutputDebugString() という関数を使う方法があります。これはデバッガに対して文字列を送り付ける API です。

 - [OutputDebugString() (Microsoft Developer Network)](https://msdn.microsoft.com/ja-jp/library/cc428973.aspx)

Delphi で使うにはこうします...まぁ、PChar() でキャストするだけですが。

```pascal:
procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
  s: String;
begin
  Memo1.Lines.Clear;
  for i:=1 to 100 do
    begin
      if ((i mod 3) + (i mod 5)) = 0 then
        s := 'Fizz Buzz'
      else if (i mod 3) = 0 then
        s := 'Fizz'
      else if (i mod 5) = 0 then
        s := 'Buzz'
      else
        s := i.ToString;
      Memo1.Lines.Append(s);

      OutPutDebugString(PChar(s)); // <-- 追加

    end;
end;
```

そして実行すると Delphi IDE の**イベントログ**にデバッグ出力が表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/7ba48b7b-d973-6d29-a42c-6f23cfa0a1c6.png)

...のですが、**Starter Edition にはイベントログがありません**。

OutputDebugString() は Windows の API なのでイベントログウィンドウの有無に限らず実行できますが、それを受けるデバッガが Starter Edition にはないのです。

では Starter Edition では OutputDebugString() による Print デバッグができないのでしょうか？いえ、これができるのです。これを実現するには Microsoft のフリーソフトである **DebugView for Windows** を使います。

 - [DebugView for Windows (Microsoft SysInternals)](https://technet.microsoft.com/ja-jp/sysinternals/debugview.aspx)

DebugView のアーカイブをダウンロードしたら適当な場所に解凍しておきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/f4830902-e63c-0525-1001-def1991b2059.png)

次に dbgview.exe を右クリックし [互換性] タブで [管理者としてこのプログラムを実行する] にチェックを入れます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/5a3b0785-19be-9f29-139c-8da86fc81d6e.png)

DubugView の初回起動時には使用許諾が表示されますので [Agree] ボタンを押します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/4936a9a2-ed80-a134-3396-dc1fcb8c959e.png)

起動時はこのようになっていると思います。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/a22041bf-65fa-352a-cf13-d759cebcd5a4.png)

お使いの Windows が 32bit の場合には "Capture Win32" にチェックを、64bit の場合には "Capture Win32" と "Capture Global Win32" の両方にチェックを入れます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/cb3acf31-4cd9-417c-7e2d-4de2a1c35806.png)

そしてターゲットとなるプログラムを実行するとデバッグ出力が表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/9c2bee21-d9d7-c862-d51e-296756d44a06.png)

この OutputDebugString() はデバッグビルド / リリースビルドとは関係ありませんので、デバッグ用に仕込んだままにしても問題ありません。例えばユーザーに DebugView をダウンロードしてもらい、デバッグ出力を伝えてもらう...なんて事も可能です。

「いや、リリースビルドにまでデバッグ文字列を埋め込みたくない！」と仰る方は**条件定義**を用いて**条件コンパイル**を行いましょう。

```pascal:
procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
  s: String;
begin
  Memo1.Lines.Clear;
  for i:=1 to 100 do
    begin
      if ((i mod 3) + (i mod 5)) = 0 then
        s := 'Fizz Buzz'
      else if (i mod 3) = 0 then
        s := 'Fizz'
      else if (i mod 5) = 0 then
        s := 'Buzz'
      else
        s := i.ToString;
      Memo1.Lines.Append(s);

      {$IFDEF DEBUG}
      OutPutDebugString(PChar(s));
      {$ENDIF}
    end;
end;
```

このように {\$IFDEF DEBUG} ～ {\$ENDIF} で括れば、デバッグビルドの時だけデバッグ出力するようにする事ができます。リリースビルドの時には余計なコードは含まれません。これでコードをコメントアウトしたりアンコメントする手間も省けますね！

**See Also:**

 - [条件付きコンパイル（Delphi）(DocWiki)](http://docwiki.embarcadero.com/RADStudio/ja/%E6%9D%A1%E4%BB%B6%E4%BB%98%E3%81%8D%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB%EF%BC%88Delphi%EF%BC%89)

# おわりに

Delphi のデバッグ手法に関しては以下の動画も参考になると思います。

 - [「実践！Delphiデバッグテクニック」 1/2 (Youtube)](https://www.youtube.com/watch?v=h-x9-0cKPTs)
 - [「実践！Delphiデバッグテクニック」 2/2 (Youtube)](https://www.youtube.com/watch?v=tn6q94g4pfs)

Starter Edition では使えない機能もあるのですが、それを差し引いても勉強になると思います。

あ、書き忘れていましたが、DebugView は [ツール | ツールの構成...] でツールに登録しておくと呼び出すのが便利になります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/42cf20a3-f9b8-a51d-382b-57fce2583a52.png)

しかしながら Delphi IDE が管理者権限で実行されていない場合、このようなエラーが出ます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/6f89b631-ace0-05e7-70ec-fdd8e6017d6a.png)

DebugView を管理者権限で実行しないようにすればこのエラーは出なくなるのですが、そうすると今度は一部の機能が使えなくなってしまいます。

この問題を回避するにはちょっとしたツールを作ります。以下のコードを KickExec.dpr という名前で保存し [ファイル | プロジェクトを開く...] で読み込ませます。

```pascal:KickExec.dpr
program KickExec;

uses
  Windows, ShellAPI;

{$R *.res}

begin
  ShellExecute(0, 'runas', PChar(ParamStr(1)), nil, nil, SW_SHOWNORMAL);
end.
```

これをコンパイルしてできた KickExec.exe を Dbgview.exe と同じ場所に置き、 [ツール | ツールの構成...] を以下のように設定します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/29d0f3a9-021e-f24c-4183-a615f9524b1d.png)

こうして実行すると DebugView 実行時に権限の昇格を聞いてくるようになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/bee49f79-97e7-5938-93ad-ad287aede106.png)

ちょっと便利になりました。

**See Also:**

 - [Delphi / C++Builder Starter Edition (無償版)](https://www.embarcadero.com/jp/free-tools)