Visual Basic 初級講座 [改訂版] |
Visual Basic 中学校 > 初級講座[改訂版] >
2021/2/21
この記事が対象とする製品・バージョン
Visual Basic 2019 | ◎ | 対象です。 | |
Visual Basic 2017 | ◎ | 対象です。 | |
Visual Basic 2015 | ◎ | 対象です。 | |
Visual Basic 2013 | ◎ | 対象です。 | |
Visual Basic 2012 | ◎ | 対象です。 | |
Visual Basic 2010 | ◎ | 対象です。 | |
Visual Basic 2008 | ◎ | 対象です。 | |
Visual Basic 2005 | ◎ | 対象です。 | |
Visual Basic.NET 2003 | △ | 対象外ですがほとんどの説明があてはまるので参考になります。 | |
Visual Basic.NET (2002) | △ | 対象外ですがほとんどの説明があてはまるので参考になります。 | |
Visual Basic 6.0 | × | 対象外です。 |
目次
アプリケーションが開始してから終了するまでの流れをライフサイクルと呼びます。
.NETで作成できるアプリケーションにはいろいろな種類があり、どのような一生(ライフサイクル)を送るかには違いがあります。
最も基本的なライフサイクルはコンソールアプリケーションを見るとわかります。
Windowsフォームアプリケーションや、Webアプリケーションなど他のアプリケーションでは、一見コンソールアプリケーションとは全然違うライフサイクルを送るように見えますが、実は、コンソールアプリケーションと根本的には同じであり、いろいろなところで工夫や仕掛けが追加されているだけです。
そこで、今回はアプリケーションのライフサイクルを考えるうえで基本となるコンソールアプリケーションを取り上げ、次回は、そのほかの種類のアプリケーションの例としてWindowsフォームアプリケーションでそれがどのように工夫され、どのような仕掛けが施されるのかを見ていくことにします。
この初級講座では今までコンソールアプリケーションがほとんど登場していなかったので少し説明しておきます。
コンソールアプリケーションは最も基本的なアプリケーションの形態です。そのため勉強の題材でも良く扱われます。
コンソールアプリケーションには固有の画面はなく、文字を入力してコマンドで操作します。
Windows上ではコンソールアプリケーションのexeをダブルクリックすると、黒い色が印象的なコマンドプロンプトが起動します。
アプリケーションの実行が終了すると、コマンドプロンプトは自動的に閉じるので、短いプログラムだと一瞬画面にちらっと黒い色が見える程度で文字を読み取ることも難しいです。
PowerShellやコマンドプロンプトなど OS のシェルと呼ばれる機能を起動してコマンドからプログラム(Windowsの場合exe)を呼び出すと、実行が終了しても自動的にPowerShellやコマンドプロンプトなどが閉じることはありません。
PowerShellやコマンドプロンプトの扱いに不慣れな方もいると思うので、これらについて必要な操作はすべて明確に説明の中に記載します。
アプリケーションが実行を開始した時に最初に呼び出される場所をエントリーポイントと呼びます。慣例上、ほとんどのプログラミング言語でエントリーポイントは、main(メイン) という名前のプロシージャ です。
コンソールアプリケーションを作成して確認してみましょう。コンソールアプリケーションを作成するには、プロジェクトの新規作成画面で「コンソール アプリ (.NET Core)」または「コンソール アプリ(.NET Framework)」を選択します。
.NET Coreの方が新しいので、特に理由がなければこちらを選択しましょう。
Visual Studio のバージョンによっては表記は少し違います。
なお、.NET Coreの方のコンソールアプリは、Windows以外にLinuxやMacOSでも動作します。(.NET Coreはあたしいバージョンは単に「.NET」と呼ぶので、将来Visual Studioの表現も変わるかもしれません)
プログラムが動作する環境をプラットフォームと呼び、Windows, Linux, MacOSなど複数の環境で動作することをクロスプラットフォームと呼びます。.NETのクロスプラットフォームはとてもよくできていて、WindowsのVisual Studioで作成したアプリケーションをそのままLinuxなどにコピーするだけで動作させることができます。Linux側でプログラムの再コンパイルは不要です。 アプリケーションの種類にってクロスプラットフォームの対応状況は異なります。初級講座でよく出てくるWindowsフォームアプリケーションはその名の通り、Windowsでしか動作しません。コンソールアプリ、ASP.NET はクロスプラットフォームで動作します。 |
コンソールアプリを作成すると、Mainという名前のプロシージャが含まれる次のようなプログラムが生成されます。バージョンによっては少し異なるかもしれません。
Imports System
Module Program
Sub Main(args As String())
Console.WriteLine("Hello World!")
End Sub
End Module
この Main というプロシージャ がエントリーポイントです。アプリケーションはここから開始します。Main はプログラムの開始地点というだけではなく、Mainが終了することがプログラムの終了も意味します。
このプログラムが実行されると、Mainの中には Console.WriteLine でメッセージを表示するプログラムが1行あるだけなので、メッセージを表示して、すぐにMainの実行が終了し、プログラムも終了します。
これだと一瞬で終了し、メッセージが正しく表示されたかどうか確認するのも困難なので、.NET Core の場合、Visual Studioはプログラムが終了してから、コンソール画面を閉じるまでの間にメッセージを表示して待機します。
これはあくまでもVisual Studioのデバッグ機能なので、Visual Studioなしで単体で実行している場合は、このメッセージは表示されず、アプリケーションは一瞬で終了します。
.NET Frameworkの場合は、Visual Studioで実行していても、このメッセージは表示されないので、アプリケーションは一瞬で終了してしまいます。
このことを確認するには、コンパイルしてできた実行ファイル(上記の例の場合 ConsoleApp1.exe)を直接実行します。
コンソールアプリケーションを一瞬で終了させたくないとき、私は Main の末尾に Console.ReadKey と記述します。これがあると、何かキーが押されるまでプログラムは終了しなくなるので、表示されている内容をゆっくり確認できます。 [デバッグ]メニューの[デバッグなしで開始]で開始する方法もあります。この方法だと、実行終了時にまた別のメッセージが表示されて、キーを押すまで画面が閉じないようになります。ただし、名前の通り、ほとんどのデバッグ機能が使用できなくなります。 |
エントリーポイントである Main には引数があります。この引数は、args As String() というように文字列型の配列として定義されており、いくつでも受け取ることができます。
引数が不要な場合は Main の定義を単に Sub Main() と書いても動作します。
この引数は、「コマンドライン引数」と呼ばれます。
コマンドライン引数はプログラムの実行時に渡された引数をそのまま表しています。
たとえば、次のようなプログラムでコマンドライン引数を出力するようにしてみましょう。
Imports System
Module Program
Sub Main(args As String())
Dim count As Integer = args.Count
Console.WriteLine("コマンドライン引数の数:" & count.ToString)
For Each arg As String In args
Console.WriteLine(arg)
Next
End Sub
End Module
普通に実行すると、「コマンドライン引数の数:0」と表示されるだけです。
そこでコンパイルして作成されたexeを直接実行してみましょう。
まずWindowsのエクスプローラーでexeのあるフォルダーを開いてください。
exeのあるフォルダーの場所がわからない場合、ソリューションエクスプローラーでプロジェクトを右クリックして、「エクスプローラーでフォルダーを開く」を選択します。次に、開いたフォルダーの中の、bin\debugフォルダーを開きます。場合によってはここに ConsoleApp1.exe のような名前で作成されたexeがあります。そうでない場合は、netcoreapp3.1 のようなフォルダーがさらにあり、その中にConsoleApp1.exe のような名前でファイルがあります。 どうしてもわからないなら、次のようにプログラムすることで、自分が存在するフォルダーのパスを表示することもできます。
|
フォルダーを開いたら、何もないところを Shift を押しながら右クリックします。そして、「PowerShell ウィンドウをここで開く」または「コマンドウィンドウをここで開く」を選択します。
起動したウィンドウで次のとおり、プログラム名を入力して実行します。私の場合は ConsoleApp1.exe です。
.\consoleapp1.exe
そうすると、コマンドライン引数:0 と表示され、プログラムは終了します。
起動時にコマンドライン引数を指定するにはスペースで区切ってexeの後ろに付けます。
次のように引数を指定して実行してみましょう。
.\consoleapp1.exe -t -f:123 test
今度は次のように表示されます。
コマンドライン引数の数:3
-t
-f:123
test
スペースで区切られた部分が引数になるので、-t と -f:123 と test の3つが指定されたということになります。
これが基本的なコマンドライン引数の指定方法です。
コマンドライン引数のことをアプリケーションのパラメーターと表現することもあります。.NET Coreのプロジェクトの場合、Visual Studioのプロジェクトのプロパティ画面では「アプリケーション引数」と記載されています。.NET Frameworkの場合は「コマンドライン引数」と記載されています。 |
exeのショートカットを作って、リンク先にコマンドライン引数を入力しておけば、このショートカットから起動するときには必ず指定したコマンドライン引数がついた状態になります。
よく観察するとWindowsのスタートメニューに並んでいるショートカットでもコマンドライン引数がついているものがたまにあります。 |
Mainの引数以外にコマンドライン引数を取得する方法がいくつかあります。
コマンドラインの取得方法 | 取得できるもの | メモ |
---|---|---|
Mainの引数 | コマンドライン引数 | |
Environment.GetCommandLineArgs | アプリケーションのフルパスとコマンドライン引数 | ★これを使いましょう! |
Environment.CommandLine | アプリケーションのフルパスとコマンドライン全体 | |
Command | コマンドライン全体 | 古い。.NET Frameworkのみ |
My.Application.GetCommandLineArgs | コマンドライン引数 | 古い。.NET Frameworkのみ |
.NET Frameworkでしか使えない古いものも表に含めています。中でもCommandは前世紀のVB6時代から使えたとても古いものです。
これから新しくプログラムする場合は、Environment.GetCommandLineArgs を使ってコマンドライン引数を取得するのがお勧めです。
コマンドライン引数をばらばらに取得したいのではなく、全体を1つの文字列として取得したい場合はEnvironment.CommandLine も使用できます。
これらはアプリケーションのどこでも好きな場所で呼び出せるので便利です。
1つ注意点としては、この2つはどちらもコマンドラインだけではなく、アプリケーションのフルパスも取得できるという点です。
Environment.GetCommandLineArgs の 最初の要素は実行されているアプリケーションのフルパスで、2つ目以降に実行時に指定されたコマンドライン引数が格納されます。
Environment.CommandLine は、アプリケーションのフルパス + コマンドライン全体を1つの文字列として返します。
先ほどのプログラムをこのように改造してみると違いがわかります。
Imports System
Module Program
Sub Main(args As String())
Dim count As Integer = Environment.GetCommandLineArgs.Count
Console.WriteLine("コマンドライン引数の数:" & count.ToString)
Console.WriteLine("コマンドライン:" & Environment.CommandLine)
For Each arg As String In Environment.GetCommandLineArgs
Console.WriteLine(arg)
Next
End Sub
End Module
これを先ほどと同じように次のコマンドで起動してみます。
.\consoleapp1.exe -t -f:123 test
結果は次のようになります。
コマンドライン引数の数:4
コマンドライン:C:\Users\rucio\source\repos\ConsoleApp1\ConsoleApp1\bin\Debug\netcoreapp3.1\ConsoleApp1.dll -t -f:123 test
C:\Users\rucio\source\repos\ConsoleApp1\ConsoleApp1\bin\Debug\netcoreapp3.1\ConsoleApp1.dll
-t
-f:123
test
発展学習では意欲的な方のために現段階では特に理解する必要はない項目を解説します。 上記で出力で、実行されているアプリケーションが ConsoleApp1.exe ではなく、ConsoleApp1.dll となっているのは奇妙に思われるかもしれません。 実は、みなさんのプログラムが格納されているのは ConsoleApp1.dll の方で、ConsoleApp1.exe はこの dll を同じ引数でキックしているだけなのです。 Linuxでの実行と比較するとよくわかります。このプログラムをLinux上で実行する場合は次のようにdotnetコマンドを使って、ConsoleApp1.dll を直接指定します。
dllの方はWindowsでもLinuxでもMacOSでも共通で使用でき、各OSごとの起動方法の違いはexeやdotnetコマンドで吸収しているというわけです。 プロジェクトの公開の設定で自己完結を選択すれば、dotnetコマンドなしで直接実行できるように構成することもできます。この場合は、Windows用のコンパイルと、Linux用のコンパイルなど、プラットフォームごとに実行ファイルを用意する必要があります。 参考 アプリケーションの発行 - .NET | Microsoft Docs Visual Studio を使用して .NET Core アプリを展開する - .NET | Microsoft Docs |
アプリケーションがドラッグ&ドロップによって開始した時は、ドロップされたファイルはコマンドライン引数としてアプリケーションに渡されます。
「ドラッグ&ドロップで起動」の意味は言葉では説明しにくいので、動画にしてみました。下記のような起動方法を言います。
このアプリケーション DropTest はWindowsフォームアプリケーションとして作成しており、次のようなプログラムです。
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.Location = New Point(100, 100)
End Sub
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
Label2.Text = Environment.GetCommandLineArgs(1)
End Sub
End Class
コンソールアプリケーションでも同じようにしてコマンドライン引数で起動時にドロップされたファイルを受け取れます。
Visual Studioでデバッグ実行する場合、プロジェクトのプロパティでコマンドライン引数を設定できます。
ソリューションエクスプローラーで、プロジェクトを右クリックし、「プロパティ」を選択すると、デバッグの欄にアプリケーション引数またはコマンドライン引数という入力項目があります。ここに、Visual Studio でデバッグ実行するときのコマンドラインを指定できます。
ここに記入した内容はVisual Studioで実行する場合にだけ利用されます。[デバッグ] - [デバッグなしで開始]から実行してもここで指定したアプリケーション引数がコマンドライン引数になります。
完成したプログラムをVisaul Studioを使わないで実行した場合には、この設定は利用されません。
エントリーポイントのプロシージャの実行が終了すると、アプリケーションは終了します。
つまり、コンソールアプリケーションの場合は、Main の実行が終了するとアプリケーション全体が終了するというわけです。
一見特に難しいことはないように思われるかもしれませんが、非同期実行をしている際に問題になります。
非同期というのは、初級講座では扱わない予定ですが、要するに2つ、3つの処理を同時に実行する機能です。これを使うと時間のかかる処理を行いながら別の処理を並行して行うというプログラムが可能になります。
たとえば、次のプログラムは1秒ずつ数を数えて、出力するという処理を非同期で実行します。
見慣れない Async や Await というキーワードが非同期処理の目印です。
Imports System
Module Program
Sub Main(args As String())
CountUp()
Console.WriteLine("終了するには何かキーを押してください。")
Console.ReadKey()
End Sub
Async Sub CountUp()
Await Task.Run(Sub()
For i As Integer = 1 To 10
Console.WriteLine(i.ToString)
Task.Delay(1000).Wait()
Next
End Sub)
End Sub
End Module
プログラムの実行が開始するとすぐに CountUp を呼び出します。この中身の詳細は割愛しますが、非同期に1秒ごとに1から10まで数えるので実行が完了するのに10秒かかります。しかし、非同期なので、10秒をまたずにMainの次の処理 Console.WriteLineが実行されます。このままだとすぐプログラムが終わってしまうので、Console.ReadKey でユーザーがキーを押すまでこの位置でプログラムを停止します。
ユーザーが何かキーを押すまで、1秒ごとに数字が出力されますが、何かキーを押した瞬間にプログラムは終了します。10まで数え終わっていなくても終了してしまうことがわかります。
つまり、アプリケーションの終了とは、処理がエントリーポイントの末端にたどり着くことであり、他に何か処理が実行中でもおかまいなしに終了してしまうということです。
End (読み方:End=エンド) または Environment.Exit を使用すると、その場でアプリケーションを強制終了できます。
強制終了はデメリットの方が大きいのでこれらのキーワードは使用するべきではありません。というのも、強制終了というのは通常のアプリケーションのライフサイクルを断ち切って強引にアプリケーションを終了させるものです。アプリケーションのフローでそのあと本来なら実行されるべきものもすべて実行されずに終了してしまうのです。
たとえば、Try内で End を使用すると、通常必ず実行されるべきFinally も実行されずに End の行で実行が終了します。
アプリケーションは終了時に終了コードと呼ばれる戻り値を呼び出し元に返すことができます。
特に意識せずアプリケーションを起動した場合、終了コードは何にも使われないため、普段はそのようなものが存在することも気にしていませんが、呼び出し元が積極的に終了コードを取得する場合は、その値を使って何かすることができます。
一般的には、終了コードはエラーを表現するのに使用されます。アプリケーションが終了コード 0 で終わった場合は、正常終了です。それ以外の場合は、何か数字の応じてエラーが発生しているという意味であることが多いです。 |
終了コードははわかりにくいのでお勧めではありません。他に手段がない場合のみ使用するように心がけるのが良いと思います。
どういうことか順を追って説明しましょう。
まず、終了コードを指定するには Environment.ExitCode プロパティを使います。このプロパティに数値を設定した状態でアプリケーションが終了すると、設定した値が終了コードになります。
Mainプロシージャ内で Return を使用すると、終了コードを設定して、かつ直ちに実行を終了させることもできます。
たとえば、次のコードは終了コード 627 を返します。
Imports System
Module Program
Sub Main(args As String())
Environment.ExitCode = 627
End Sub
End Module
Environment.ExitCode はアプリケーション内のどこからでも使用できるのに対し、Return を使って終了コードを指定する方法はエントリーポイントでしか通用しません。Return で終了コードを返すには、Sub Main(args As String()) の部分を自分で Function Main(args As String()) As Integer と修正したうえで、Main内で Return 627 のように記述します。 なお、アプリケーションを強制終了させる前述の Environment.Exit も引数に終了コードを指定できます。 |
実行しても何も起こらず終了しますが、コンソールウィンドウや出力ウィンドウのデバッグには 627 で終了したというメッセージは表示されます。
PowerShellでこのプログラムを呼び出している場合は、呼び出し後に $lastexitcode と入力することで終了コードを取得できます。
コマンドプロンプトの場合は、呼び出し終了後にecho %errorlevel% で終了コードを表示できます。
他にも呼び出した手段によって、終了コードの取得方法はさまざまです。これはもはやVBや.NETの仕様ではなく、PowerShellやコマンドプロンプトなど呼び出し側の仕様です。