Visual Basic 初級講座 |
Visual Basic 中学校 > 初級講座 >
VBの強力なデバッグ機能・ツールを紹介します。ツールを使いこなして快適なデバッグを行いましょう。今回はツールの紹介をし、次回にはこれらのツールを活用した実際のデバッグを紹介する予定です。
概要 ・[Ctrl] + [Pause(Break)]またはブレークポイントを使用して、プログラムの実行を一時停止することができる。 ・ステップ実行を使うとプログラムを1行ずつ実行できるのでプログラムの流れや分岐の様子が手に取るようにわかる。 ・ローカルウィンドウやウォッチウィンドウを使うと、一時停止中のプログラムの変数の値を簡単に確認することができる。 ・イミディエイトウィンドウを使うと、臨機応変かつ即座に命令を下すことができる。VBの構文を確認したり、開発中のメソッドを試しに実行してみたりもできる。 ・VB2005では実行停止中にコードを修正して、実行を再開するエディットコンティニュー機能が使用できる。 |
作ったプログラムが何の問題もなく、最初に実行した時から完璧に動作するなどということはまずありえないことです。よっぽど小さな練習用のプログラムならまだしも、ある程度の実用性を持ったプログラムでこのようなことがあったとしたら、そのプログラマはよほど優秀なプログラマです。
それで、うまく動かないときはどうするかというと動かない原因を探し出して動くように修正するわけです。
この動かない原因のことを「バグ」と言い、動くように修正することを「デバッグ」と呼びます。
メモ - 「バグ」と「デバッグ」 バグは「虫」と言う意味で、「デバッグ」は「虫捕り」と言う意味です。昔コンピューターに蛾が入り込んで動かなくなったことがあり、それ以来コンピュータが動かなくなる原因は「バグ」(虫)と呼ばれることになったそうです。 ところで、この蛾を退治したのは知る人ぞ知るグレース・ホッパー女史(1906 - 1992)で、しかもこの蛾はちょっとしたジョークで研究日誌に貼り付けられたということです。この研究日誌が今も存在するのならプログラムの歴史上とてもユニークな存在になりますね。 |
言葉で書いてしまうとこれだけのことですが、一体どうやって原因を探し出したらよいでしょうか?昔のプログラマはソースコードをもう一度良く眺めてプログラムの流れを追っていき、必要ならフローチャートなどの図を描いて流れのどこにおかしいところがあるのか鉛筆と紙を使って探し出していました。昔はプログラムはパンチカードに書いていたので画面を見ながら作業ということはまずありませんでした。
21世紀の現在、あなたはどうやっていますか?まさか、鉛筆と紙ということはないでしょう。もちろん時には鉛筆と紙も非常に役に立ちますがデバッグの最初から鉛筆と紙の出番という 人はそうはいないはずです。
ひょっとして、「紙」が「画面」に変っただけで昔のプログラマと同じことをやっていませんか?画面上のプログラムを眺めてプログラムの流れを考えながら「どこが悪いんだろう?」なんて一生懸命考えていますか?
VBを使っている我々にはこのような作業はあまり必要ありません。
今回と次回はこのデバッグの方法について説明します。今回はデバッグで使用する機能やツールを紹介し、次回は実際にバグのあるプログラムを動かしながらデバッグを体験してみます。
VBにおけるデバッグの基本は一時停止です。プログラムは途中まで正常に動いていて途中からおかしくなるのですから、どこからおかしくなるのかプログラムの実行を一時停止して調べるのが有効です。真面目にこつこつやれば99%の場合で「どこからおかしくなるのか」突き止めることが可能です。
実行中のプログラムを一時停止する手段はいくつかあります。代表的なものをざっと見てみましょう。
一時停止の主な方法 |
[Ctrl] + [Pause] (または[Ctrl] + [Break]) を押す。 |
ツールバーの一時停止ボタンをクリックする。 |
ブレークポイントを設定する。 |
Stopステートメントを使用する。 |
■表1
次にサンプルプログラムを使って実際にプログラムを停止させて見ます。
フォームにボタンとリストボックスを1つずつ配置してボタンのクリックイベントに次の通りプログラムしてください。
Private
Sub
Button1_Click(ByVal
sender As
System.Object, ByVal
e As
System.EventArgs) Handles
Button1.Click Dim
i As
Integer For i = 0 To 9 If i < 5
Then ListBox1.Items.Add(St) Next End Sub |
■リスト1
コメントを見てもらえればわかるのですが、ちょっと普通じゃないコードを入れています。というのも短いプログラムは一瞬で実行が終了してしまうので止める暇がないのです。そこで、わざと実行が遅くなるようなコードを入れて余裕を持たせています。後で説明する ブレークポイントを使えばどんなに短いプログラムでも確実に一時停止することができるのですが、今回はその他の方法も試したいのであえてこのようなコードを用意しました。
なお、Application.DoEventsは今回はなくても良いのですが、表示が更新されないと気持ち悪いので入れました。どういうことかというと普段はプログラムが一瞬で実行されるので気になりませんが、実は表示に関する多くの命令はプログラムが命令した瞬間に表示が更新されるわけではなく、実行がEnd Subにたどり着くなど一息つけるところに来てから実際の表示更新が行われるのです。
Application.DoEventsは自分で指定してこの「一息つく」という動作を実行しています。別の言葉を使えば「命令されたことをすぐやれ」という意味です。このあたりの動作をVBに即してもっとコンピューター的に説明すると長くなってしまうので今回はこのような比喩で許してください。動作上はApplication.DoEventsを試しにコメントにして見るとこれがあるときとないときとでどのように異なるか簡単に比較できます。
前置きが長くなりましたが、早速実行を一時停止してみましょう。
プログラムを実行させたら[Ctrl] + [Pause](または[Ctrl] + [Break])を押してください。多分、次のような画面になったのではないでしょうか?実行状況によってどの行で止まるかは異なるので違う状態になる場合もあります。
■画像1
この画像の場合は、DoEventsメソッド実行中にプログラムが一時停止されたことが示されています。
何も指示しなければプログラムはこのままの状態でずっと止まっています。本来ならここでデバッグを開始するのですが、今回は実行を再開してみましょう。
■画像2
プログラムを開始したときのボタンをクリックすると実行を再開できます。プログラム停止ボタンをクリックしてこのまま実行を完全に停止してしまうこともできます。
次はツールバーの一時停止ボタンを押して同じことを試してみてください。まったく同じようにプログラムの実行が停止できることを確認できることでしょう。
博士のワンポイントレッスン
|
今度はブレークポイントを使ってみましょう。ブレークポイントは実行を停止させたい個所を自分で指定する機能です。If i < 5 Thenの行で停止させて見ます。この行の左の部分をクリックしてください。言葉で「左の部分」と言ってもわかりにくいと思います。もし、わからない場合はこの行にカーソルを移動して[F9]を押してください。クリックでも[F9]でもどちらでも ブレークポイントを設定することが出来ます。ブレークポイントは茶色い丸として表示されます。
■画像3:ブレークポイント
この状態で実行すると、すぐに実行が停止されて次のような画面に切り替わります。
■画像4:ブレークポイントで停止したところ
停止した後はさきほどと同じように再開するなり終了するなりできます。試しに実行を再開してみてください。またすぐに同じところで止まります。これはこの部分がFor 〜 Nextに囲まれていて何度も実行されるからです。ブレークポイントは実行がその地点に来ると何度でも反応して実行を停止します。
ブレークポイントを細かく制御して停止する条件を指定することも出来ますがこの機能はVB2005 Express Editionでは使用できません。停止する条件を指定するにはブレークポイント右クリックして各コマンドから条件を設定します。
また、設定されているブレークポイントの一覧を表示することもできます。それには[デバッグ]メニューの[ウィンドウ] - [ブレークポイント]をクリックします。この機能も残念ながらVB2005 Express Editionでは使用できません。
Stopステートメント(読み方:Stop = ストップ)はブレークポイントとまったく同じ効果があります。通常はブレークポイントを使用すれば良いのでStopステートメントの出番はありませんが、VB2005 Express Editionで条件付で実行を停止したい場合には使用する価値があります。
たとえば、次の例では変数Countの値が5の時に実行を一時停止します。
Dim Count As
Integer For Count = 0 To 10 If
Count = 5 Then Next |
■リスト2:Stopによる一時停止
VB2005 Express Edition以外でもこのコードは有効ですが、使う機会はほとんどありません。
この他にもエラーが発生するとその行で実行が一時停止されます。ただし、VB.NET2002およびVB.NET2003ではこの状態から実行を再開することはできません。
これで自由にプログラムの実行を一時停止できるようになりました。さて、実行停止中にどんなことができるのでしょうか?
まずはステップ実行を紹介しましょう。ステップ実行とはプログラムを1行ずつ実行していく方法です。説明するより実際にやった方が早いので次の手順で実行してください。
まず、さきほどのリスト1のプログラムを実行してどのような手段でもよいので一時停止させます。後は簡単でキーボードの[F10]を押すたびに1行ずつプログラムが実行されていきます。キーボード設定によっては[F10]ではなく、[Shift] + [F8]の場合もあります。[F10]で反応がおかしい場合は[Shift] + [F8]を試してみてください。
この方法でどんどん実行していくとプログラムの実際の流れが手に取るようにわかります。For 〜 Nextでループしている様子もわかるし、If文で条件を分岐して実行していく様子もわかります。なんとすばらしいことではありませんか!
この機能は2、30年前のプログラマにとっては夢の機能です。まさに21世紀という感じです。
さらにステップ実行中にコードの左側に表示されている黄色い矢印をマウスを使って上下に移動させることもできます。移動させると移動先の行から実行を再開することができます。もう一度処理を実行させて試したい場合や、処理を飛ばして先のほうを実行したい場合などに便利な機能です。ただし、飛ばした部分のプログラムは一切実行されないなど通常のプログラムとは異なる特別な順番で実行することになりますのでその点には注意を払ってください。
ステップ実行にはステップイン、ステップアウト、ステップオーバーという3つの実行方法があります。今、上で説明したのはステップオーバーという方法です。
ステップインはステップオーバーと同じように1行ずつ実行していくのですが、メソッドやプロパティの行を実行する場合に、そのメソッドやプロパティ自体のプログラムにも入り込んで実行する方法です。ステップインで実行するには[F11]を押します。キーボードの設定によっては[F8]の場合もあります。
たとえば、次のプログラムをステップオーバーとステップインの両方で実行して違いを確かめてみてください。
Private
Sub Button1_Click(ByVal
sender As System.Object,
ByVal e As System.EventArgs)
Handles Button1.Click Dim St As String St = GetFirstFile() End Sub |
Private
Function GetFirstFile()
As String
Dim
Path As
String Path =
Environment.GetFolderPath(Environment.SpecialFolder.Recent) Return FileName End Function |
Private Function
GetFileName(ByVal FullPath
As String)
As
String Return IO.Path.GetFileName(FullPath) End Function |
■リスト3
じっくりステップ実行できるようにわざと長いコードにしてみました。同じコードをもっと短く書くことも可能です。
ここでButton1_ClickのところのSt = GetFirstFile()の行にブレークポイントを設定してステップインとステップオーバーの両方を実行して違いを確認してください。ステップオーバーで実行すると、Button1_Click内だけを順番に実行して3行で終了してしまうのに対し、ステップインで実行するとGetFirstFileメソッドの内部にまで入り込んで1行ずつ実行していくのがわかります。
ステップアウトは現在実行中のメソッドを脱出してその次から実行します。この間のステップ実行を飛ばされた部分のコードはステップ実行こそ飛ばされますがちゃんと実行はされます。ステップアウトは[Shift] + [F11]で行えます。キーボードの設定によっては[Shift] + [Ctrl] + [F8]の場合もあります。
ステップアウトを試すには上記のプログラムでGetFirstFileメソッド内のPath = …の行にブレークポイント設定してください。ステップイン、ステップオーバーで実行すると、次のFileName = …の行に実行が移りますが、ステップオーバーを利用するとGetFirstFileメソッドの残りの部分の実行を全て行ってからButton1_Clickに戻ったところから実行します。
ステップイン、ステップオーバー、ステップアウトは状況により使い分けます。また途中まではステップオーバーで実行して、途中からはステップインで実行するなど1行ごとに使い分けることも可能です。ツールバーのボタンをボタンをクリックすることで好きな方法でステップ実行を行うことができます。このツールバーはデバッグツールバーです。表示されていない場合は[表示]メニューの[ツールバー] - [デバッグ]で表示させることができます。
■画像5:デバッグツールバー
しかし、VB2005 Express Editionではデバッグツールバーを表示してもステップ実行のアイコンがありません。自分で追加することはできますので必要ならば追加してください。
特徴 | キーボード | 別の設定では... | |
ステップイン | メソッド内にも入り込む。 | [F11] | [F8] |
ステップオーバー | メソッド内に入り込まない。 | [F10] | [Shift] + [F8] |
ステップアウト | 呼び出し元プロシージャに帰る。 | [Shift] + [F11] | [Shift] + [Ctrl] + [F8] |
■表2:ステップ実行の種類
ステップ実行だけでもバグの原因を探すのに役に立ちそうです。しかしVBのデバッグ機能はまだまだこんなものではありません。
今度はローカルウィンドウというものを使ってみます。リスト1のプログラムを用意してください。そして、さきほど説明したように実行を停止し、この状態で[デバッグ]メニューの[ウィンドウ] - [ローカル]をクリックしてください。次の画像にあるウィンドウが表示されます。
■画像6:ローカルウィンドウ
このウィンドウはプログラムに含まれている変数の状態を示しています。私が実行を停止したときには i = 0、 St = "□0"という状態だったことがこのローカルウィンドウから読み取れます。面白いのはここからです。このウィンドウを表示したままステップ実行をしていくと変数の内容がリアルタイムに変化していきます。
これで変数の内容をチェックしながらバグを探すことが出来るわけです。
なおウォッチウィンドウを使用すると指定した変数だけを見ることもできます。ウォッチウィンドウは同じように[デバッグ]メニューの[ウィンドウ] - [ウォッチ]で表示できます。
■画像7:ウォッチウィンドウ
ウォッチウィンドウにははじめは何も表示されていないので自分で見たい変数を指定します。名前欄に直接iとかStとか入力することもできますが、プログラム画面から変数をドロップして追加することもできてこちらの方が楽です。上の画像ではiとStをドロップして追加し、ListBox1.Items(0)を直接入力して追加しました。
なお、このローカルウィンドウとウォッチウィンドウはただ変数の値を見るだけではなく、値を変更することもできます。
ただちょっと変数の値を見たいと言う場合にはこれらのウィンドウを使用しなくてもマウスカーソルを変数の上に持っていくだけで構いません。
■画像8
変数を右クリックして「クイックウォッチ」を選択しても値を見ることができます。クイックウォッチで見ると配列やコレクションのその時点の内容をまとめて見ることができますので便利です。
次は強力なイミディエイトウィンドウを紹介します。イミディエイトウィンドウを表示するには実行の一時停止中に[デバッグ]メニューの[ウィンドウ] - [イミディエイト]をクリックします。
イミディエイトウィンドウはデバッグする人の命令を即座に実行してくれる機能を提供します。命令はVBで書くことができるので実行停止中に臨機応変にいろいろな動作を命令することができます。
一緒にやってみましょう。先ほどのリスト1のプログラムでIf i < 5 Thenの行にブレークポイントを設定して実行してください。すぐにブレークポイントで実行が停止します。この状態でイミディエイトウィンドウを表示してください。
■画像9
この状態でイミディエイトウィンドウに次のように入力してください。入力が終わったら[ENTER]キーを押します。
? i
これは「 i の値を表示しなさい」という命令です。
そうすると、0 と表示されます。これだけならイミディエイトウィンドウを使わなくてもウォッチで可能ですね。
今度は一昨日の日付を表示させて見ましょう。次の通り入力してください。
? Now.AddDays(-2)
これもちゃんと表示されます。一昨日の日付が突然知りたくなることなどまずないのですが、要するにVBで使用できる命令がそのままイミディエイトウィンドウでも使用できることを説明したかったのです。
■画像10
イミディエイトウィンドウは表示以外にもさまざまな命令を実行できます。このままの状態で今度は次の通り入力してください。
i = 100
これで[ENTER]を押すと現在実行中のプログラムで変数iの値が100になります。ウォッチでiの値が変ったことを確認してみてください。
最後に次の通りに打ち込んでください。
ListBox1.Items.Add("イミディエイトは便利")
そして、プログラムの実行を再開するとどうなるかお分かりですよね。試してみてください。
このようにイミディエイトウィンドウを使用すると実行停止中のいろいろな情報を取得することもできますし、通常のプログラムにはないことを命令して動作を確認することができます。私はこのイミディエイトウィンドウが大好きでデバッグといえばイミディエイトウィンドウを使用します。
なお、イミディエイトウィンドウの内容を全て削除するには「>cls」と入力してください。
VB2005ではさらにイミディエイトウィンドウの機能が強化されています。なんと開発中にもイミディエイトウィンドウが使えるのです。
たとえば、プログラム中に「Format(12, "000")ってどんな値になるのかな?」と思ったときに試しにイミディエイトウィンドウに打ってみるとすぐに結果がわかります。
? Format(12, "000")
こういうことって結構多いですよね。VB.NET2002とVB.NET2003では実際に実行して確認しないとわからないのですがらVB2005のメリットは大きいです。
この機能を使うとなんと自分で作ったメソッドをプログラム中にイミディエイトウィンドウで試しに実行してみることもできます。
とりあえず、次のメソッドをForm1に貼り付けてください。
Private Function Add(ByVal
X As
Integer,
ByVal Y
As
Integer)
As
Integer Return X + Y End Function |
■リスト4
貼り付けたらすぐにイミディエイトウィンドウを表示して次の通り打ち込んでください。
? (New Form1).Add(2, 3)
そうするとちゃんと 5 と表示されます。このようにVB2005では結果を確認しながらプログラムを行うことができるのです。でも実行するのにちょっと時間がかかるのが難点です。VB6やAccess2003のイミディエイトウィンドウは即座に実行できるのですごいです。次期VBではさらに使いやすくなるようにマイクロソフトにお願いしたいですね。
とはいえ、実のところVB2005の開発者と直接会って聞いたことがあるのですが、イミディエイトウィンドウのこの機能を作るのはかなり大変だったらしいです。ですから当分の間はこれ以上は便利になりそうにないです。まぁこれだけでもいちいちプログラムを実行して結果を確認し、まずかったら修正してもう一度実行し…という手順を踏むのに比べたらはるかに便利ですのでせっかくの機能を活用していくことにしましょう。
発展学習 - (New Form1)とは? 発展学習では意欲的な方のために現段階では特に理解する必要はない項目を解説します。 本文で説明したAddメソッドをイミディエイトウィンドウから呼び出すには、Form1.Addではなく、(New Form1).Addと書かなければなりません。Form1.Addと書いた場合はAddメソッドはForm1の共有メンバであることが前提となります。ところが実際にはAddメソッドは非共有メンバですからインスタンス経由でしか呼び出しができません。そこでNewキーワードを使ってForm1のインスタンスを作成し、そのインスタンスのAddメソッドを呼び出すように書く必要があるわけです。 なお、Sharedを使用してPrivate Shared Function Add …のようにするとメソッドをクラス(Form1)の共有メンバにすることができます。この場合にはイミディエイトウィンドウでForm1.Addの形式でメソッドを呼び出すことができます。 ・共有メンバ・非共有メンバについては初級講座第9回 5.メンバの種類を参照してください。 ・Sharedなどの宣言方法については後日解説予定です。 |
エディットコンティニューとは実行の一時停止中にプログラムを修正する機能のことです。この機能はVB2005でないと使用できません。
たとえば、次のコードを実行してみたときのことを考えましょう。
Dim
St As
String MsgBox(St.Length) |
■リスト5
このコードは2行目のMsgBoxの行でエラーになります。StがNothingなのにLengthプロパティを使用しようとしているからです。
実行するとエラーが発生してMsgBoxの行で実行が一時停止します。このエラーはどこかにSt = ""のようなStへの値のセットを記述すれば簡単に修正できます。実行を一時停止した状態のままでMsgBoxの上の行にこの記述を追加して次のようにすることができます。
Dim
St As
String St = "" MsgBox(St.Length) |
■リスト6
そして、実行停止を示す黄色い行の左にある矢印を今追加した St = ""の行にドラッグして実行を再開すると今度はエラーにならずに処理を最後まで実行することができます。
このように実行停止中にプログラムを修正してすぐに実行を再開できるのがエディットコンティニュー機能です。
この機能がなければエラーになるたびにプログラムを終了して、コードを修正して再実行しなければなりません。どう考えてもエディットコンティニューを使った方がすばやく効率的にプログラムできると言うものです。
このエディットコンティニュー機能はVB6にもあったのですがVB.NET2002、VB.NET2003ではなくなってしまった機能でした。しかし、この便利な機能を復活して欲しいと言う要望がとても多く、ついにVB2005で復活したと言うことです。このエディットコンティニュー機能の復活にはマイクロソフトの開発チームも相当大変だったと言う話を聞いています。
この機能を活用するとVB2005では大胆で素早いデバッグが可能になります。まず、プログラムを実行します。ビルドエラーで実行できない場合はエラー一覧に表示されている個所を順に修正します。実行してエラーになる個所はエディットコンティニューを使って順番に修正します。すべてのエラーがなくなればデバッグ完了です。
もっとも「エラーがない」ということは「正常に動作する」ということと同じではないのでこの方法がいつも通用するわけではありませんが、方法としてはなんだか直感的でわかりやすくて簡単な方法だと思いませんか?
最後に呼び出し履歴を紹介します。VBではメソッドやプロパティを呼び出してプログラムを構成していきます。そしてメソッドの中からまた別のメソッドを呼び出すころもできます。呼び出し履歴は実行の一時停止中に現在のプロシージャが呼び出された経路を示す機能です。
この機能を試すためにステップ実行のところで紹介したリスト3のプログラムを使ってみます。このプログラムでGetFileNameメソッド内のReturn IO.Path.GetFileName(FullPath)の行にブレークポイントを設定して実行してください。
この行で実行が停止します。このときに[デバッグ]メニューの[ウィンドウ] - [呼び出し履歴]をクリックして呼び出し履歴ウィンドウを表示すると次のようになっています。
■画像11
これを見ると、現在実行中なのはGetFileNameプロシージャで、これを呼び出したのはGetFirstFileプロシージャで、それを呼び出したのはButton1_Clickプロシージャであることがわかります。さらに細かくどの行からプロシージャが呼び出されたのか視覚的に表示することもできます。
呼び出し履歴の2行目(GetFirstFileの行)をダブルクリックすると、GetFirstFileプロシージャ内でGetFileNameメソッドを呼び出した行が表示されます。