Windows
Delphi
Pascal
objectpascal

Delphi Starter Edition でのデバッグ

はじめに

これは Delphi Advent Calendar 2017...とは全く関係のない記事です (w

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

デバッグ

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

image.png

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

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

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

image.png

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

image.png

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

image.png

Button1 を押すと...

image.png

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

image.png

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

image.png

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

image.png

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

image.png

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

image.png

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

image.png

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

image.png

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

See Also:

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

ツールチップ式評価の簡単な代替はらいなたんさんDLight プラグインを使う事でしょう。

687474703a2f2f692e696d6775722e636f6d2f576859547041482e676966.gif

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

DebugView

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

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

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

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

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

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

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

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

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

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

image.png

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

image.png

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

image.png

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

image.png

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

image.png

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

image.png

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

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

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 のデバッグ手法に関しては以下の動画も参考になると思います。

-「実践!Delphiデバッグテクニック」 1/2 (Youtube)
-「実践!Delphiデバッグテクニック」 2/2 (Youtube)

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

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

image.png

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

image.png

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

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

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

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

image.png

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

See Also: