Visual Basic 初級講座 [改訂版]
VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

第29回 アプリケーションのライフサイクルの基本

2021/2/21

この記事が対象とする製品・バージョン

VB2019 Visual Basic 2019 対象です。
VB2017 Visual Basic 2017 対象です。
VB2015 Visual Basic 2015 対象です。
VB2013 Visual Basic 2013 対象です。
VB2012 Visual Basic 2012 対象です。
VB2010 Visual Basic 2010 対象です。
VB2008 Visual Basic 2008 対象です。
VB2005 Visual Basic 2005 対象です。
VB.NET 2003 Visual Basic.NET 2003 対象外ですがほとんどの説明があてはまるので参考になります。
VB.NET 2002 Visual Basic.NET (2002) 対象外ですがほとんどの説明があてはまるので参考になります。
VB6対応 Visual Basic 6.0 × 対象外です。

 

目次

 

1.アプリケーションのライフサイクル

1-1.アプリケーションの種類

アプリケーションが開始してから終了するまでの流れをライフサイクルと呼びます。

.NETで作成できるアプリケーションにはいろいろな種類があり、どのような一生(ライフサイクル)を送るかには違いがあります。

最も基本的なライフサイクルはコンソールアプリケーションを見るとわかります。

Windowsフォームアプリケーションや、Webアプリケーションなど他のアプリケーションでは、一見コンソールアプリケーションとは全然違うライフサイクルを送るように見えますが、実は、コンソールアプリケーションと根本的には同じであり、いろいろなところで工夫や仕掛けが追加されているだけです。

そこで、今回はアプリケーションのライフサイクルを考えるうえで基本となるコンソールアプリケーションを取り上げ、次回は、そのほかの種類のアプリケーションの例としてWindowsフォームアプリケーションでそれがどのように工夫され、どのような仕掛けが施されるのかを見ていくことにします。

 

1-2.コンソールアプリケーション

この初級講座では今までコンソールアプリケーションがほとんど登場していなかったので少し説明しておきます。

コンソールアプリケーションは最も基本的なアプリケーションの形態です。そのため勉強の題材でも良く扱われます。

コンソールアプリケーションには固有の画面はなく、文字を入力してコマンドで操作します。

Windows上ではコンソールアプリケーションのexeをダブルクリックすると、黒い色が印象的なコマンドプロンプトが起動します。

アプリケーションの実行が終了すると、コマンドプロンプトは自動的に閉じるので、短いプログラムだと一瞬画面にちらっと黒い色が見える程度で文字を読み取ることも難しいです。

PowerShellやコマンドプロンプトなど OS のシェルと呼ばれる機能を起動してコマンドからプログラム(Windowsの場合exe)を呼び出すと、実行が終了しても自動的にPowerShellやコマンドプロンプトなどが閉じることはありません。

PowerShellやコマンドプロンプトの扱いに不慣れな方もいると思うので、これらについて必要な操作はすべて明確に説明の中に記載します。

 

2.アプリケーションの開始

アプリケーションが実行を開始した時に最初に呼び出される場所をエントリーポイントと呼びます。慣例上、ほとんどのプログラミング言語でエントリーポイントは、main(メイン) という名前のプロシージャ です。

 

コンソールアプリケーションを作成して確認してみましょう。コンソールアプリケーションを作成するには、プロジェクトの新規作成画面で「コンソール アプリ (.NET Core)」または「コンソール アプリ(.NET Framework)」を選択します。

.NET Coreの方が新しいので、特に理由がなければこちらを選択しましょう。

Visual Studio のバージョンによっては表記は少し違います。

なお、.NET Coreの方のコンソールアプリは、Windows以外にLinuxやMacOSでも動作します。(.NET Coreはあたしいバージョンは単に「.NET」と呼ぶので、将来Visual Studioの表現も変わるかもしれません)

メモ メモ  - Linux や MacOS での動作

プログラムが動作する環境をプラットフォームと呼び、Windows, Linux, MacOSなど複数の環境で動作することをクロスプラットフォームと呼びます。.NETのクロスプラットフォームはとてもよくできていて、WindowsのVisual Studioで作成したアプリケーションをそのままLinuxなどにコピーするだけで動作させることができます。Linux側でプログラムの再コンパイルは不要です。

アプリケーションの種類にってクロスプラットフォームの対応状況は異なります。初級講座でよく出てくるWindowsフォームアプリケーションはその名の通り、Windowsでしか動作しません。コンソールアプリ、ASP.NET はクロスプラットフォームで動作します。

 

コンソールアプリを作成すると、Mainという名前のプロシージャが含まれる次のようなプログラムが生成されます。バージョンによっては少し異なるかもしれません。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

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 と記述します。これがあると、何かキーが押されるまでプログラムは終了しなくなるので、表示されている内容をゆっくり確認できます。

[デバッグ]メニューの[デバッグなしで開始]で開始する方法もあります。この方法だと、実行終了時にまた別のメッセージが表示されて、キーを押すまで画面が閉じないようになります。ただし、名前の通り、ほとんどのデバッグ機能が使用できなくなります。

 

3.コマンドライン引数

3-1.エントリーポイントの引数

エントリーポイントである Main には引数があります。この引数は、args As String() というように文字列型の配列として定義されており、いくつでも受け取ることができます。

引数が不要な場合は Main の定義を単に Sub Main() と書いても動作します。

この引数は、「コマンドライン引数」と呼ばれます。

コマンドライン引数はプログラムの実行時に渡された引数をそのまま表しています。

たとえば、次のようなプログラムでコマンドライン引数を出力するようにしてみましょう。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

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 のような名前でファイルがあります。

どうしてもわからないなら、次のようにプログラムすることで、自分が存在するフォルダーのパスを表示することもできます。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Imports System

Module Program Sub Main(args As String()) Dim appPath As String = AppDomain.CurrentDomain.BaseDirectory 'Debug.WriteLine(appPath) '←コンソールアプリ以外の場合 Console.WriteLine(appPath)
End Sub
End Module

 

フォルダーを開いたら、何もないところを 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のスタートメニューに並んでいるショートカットでもコマンドライン引数がついているものがたまにあります。

 

3-2.コマンドラインの取得方法

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つの文字列として返します。

先ほどのプログラムをこのように改造してみると違いがわかります。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

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.dll

発展学習では意欲的な方のために現段階では特に理解する必要はない項目を解説します。

上記で出力で、実行されているアプリケーションが ConsoleApp1.exe ではなく、ConsoleApp1.dll となっているのは奇妙に思われるかもしれません。

実は、みなさんのプログラムが格納されているのは ConsoleApp1.dll の方で、ConsoleApp1.exe はこの dll を同じ引数でキックしているだけなのです。

Linuxでの実行と比較するとよくわかります。このプログラムをLinux上で実行する場合は次のようにdotnetコマンドを使って、ConsoleApp1.dll を直接指定します。

rucio@Azure:~/pg/ConsoleApp1$ dotnet ConsoleApp1.dll -t -f:123 test
コマンドライン引数の数:4
コマンドライン:/home/rucio/pg/ConsoleApp1/ConsoleApp1.dll -t -f:123 test
/home/rucio/pg/ConsoleApp1/ConsoleApp1.dll
-t
-f:123
test

dllの方はWindowsでもLinuxでもMacOSでも共通で使用でき、各OSごとの起動方法の違いはexeやdotnetコマンドで吸収しているというわけです。

プロジェクトの公開の設定で自己完結を選択すれば、dotnetコマンドなしで直接実行できるように構成することもできます。この場合は、Windows用のコンパイルと、Linux用のコンパイルなど、プラットフォームごとに実行ファイルを用意する必要があります。

参考

アプリケーションの発行 - .NET | Microsoft Docs

Visual Studio を使用して .NET Core アプリを展開する - .NET | Microsoft Docs

 

 

3-3.ドラッグ&ドロップでの起動

アプリケーションがドラッグ&ドロップによって開始した時は、ドロップされたファイルはコマンドライン引数としてアプリケーションに渡されます。

「ドラッグ&ドロップで起動」の意味は言葉では説明しにくいので、動画にしてみました。下記のような起動方法を言います。

このアプリケーション DropTest はWindowsフォームアプリケーションとして作成しており、次のようなプログラムです。

VB2005対応 VB2008対応 VB2010対応 VB2012対応 VB2013対応 VB2015対応 VB2017対応 VB2019対応

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

コンソールアプリケーションでも同じようにしてコマンドライン引数で起動時にドロップされたファイルを受け取れます。

 

3-4.コマンドライン引数のデバッグ

Visual Studioでデバッグ実行する場合、プロジェクトのプロパティでコマンドライン引数を設定できます。

ソリューションエクスプローラーで、プロジェクトを右クリックし、「プロパティ」を選択すると、デバッグの欄にアプリケーション引数またはコマンドライン引数という入力項目があります。ここに、Visual Studio でデバッグ実行するときのコマンドラインを指定できます。

ここに記入した内容はVisual Studioで実行する場合にだけ利用されます。[デバッグ] - [デバッグなしで開始]から実行してもここで指定したアプリケーション引数がコマンドライン引数になります。

完成したプログラムをVisaul Studioを使わないで実行した場合には、この設定は利用されません。

 

4.アプリケーションの終了

4-1.エントリーポイントの末端到達

エントリーポイントのプロシージャの実行が終了すると、アプリケーションは終了します。

つまり、コンソールアプリケーションの場合は、Main の実行が終了するとアプリケーション全体が終了するというわけです。

一見特に難しいことはないように思われるかもしれませんが、非同期実行をしている際に問題になります。

非同期というのは、初級講座では扱わない予定ですが、要するに2つ、3つの処理を同時に実行する機能です。これを使うと時間のかかる処理を行いながら別の処理を並行して行うというプログラムが可能になります。

たとえば、次のプログラムは1秒ずつ数を数えて、出力するという処理を非同期で実行します。

見慣れない Async や Await というキーワードが非同期処理の目印です。

VB2012 VB2013 VB2015 VB2017 VB2019

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まで数え終わっていなくても終了してしまうことがわかります。

つまり、アプリケーションの終了とは、処理がエントリーポイントの末端にたどり着くことであり、他に何か処理が実行中でもおかまいなしに終了してしまうということです。

 

4-2.強制終了

End (読み方:End=エンド) または Environment.Exit を使用すると、その場でアプリケーションを強制終了できます。

強制終了はデメリットの方が大きいのでこれらのキーワードは使用するべきではありません。というのも、強制終了というのは通常のアプリケーションのライフサイクルを断ち切って強引にアプリケーションを終了させるものです。アプリケーションのフローでそのあと本来なら実行されるべきものもすべて実行されずに終了してしまうのです。

たとえば、Try内で End を使用すると、通常必ず実行されるべきFinally も実行されずに End の行で実行が終了します。

 

4-3.終了コード

アプリケーションは終了時に終了コードと呼ばれる戻り値を呼び出し元に返すことができます。

特に意識せずアプリケーションを起動した場合、終了コードは何にも使われないため、普段はそのようなものが存在することも気にしていませんが、呼び出し元が積極的に終了コードを取得する場合は、その値を使って何かすることができます。

メモ メモ  - 一般的な終了コード

一般的には、終了コードはエラーを表現するのに使用されます。アプリケーションが終了コード 0 で終わった場合は、正常終了です。それ以外の場合は、何か数字の応じてエラーが発生しているという意味であることが多いです。

終了コードははわかりにくいのでお勧めではありません。他に手段がない場合のみ使用するように心がけるのが良いと思います。

どういうことか順を追って説明しましょう。

まず、終了コードを指定するには Environment.ExitCode プロパティを使います。このプロパティに数値を設定した状態でアプリケーションが終了すると、設定した値が終了コードになります。

Mainプロシージャ内で Return を使用すると、終了コードを設定して、かつ直ちに実行を終了させることもできます。

たとえば、次のコードは終了コード 627 を返します。

VB2002 VB2003 VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Imports System

Module Program Sub Main(args As String()) Environment.ExitCode = 627
End Sub
End Module
メモ メモ  - Return の場合

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やコマンドプロンプトなど呼び出し側の仕様です。

 

5.練習問題

問1.アプリケーションが実行を開始するエントリーポイントとして適切なものはどれでしょうか?
Main
Load
プログラムの先頭
Run
問2.コマンドライン引数を受け取る方法として適切ではないものはどれでしょうか?
Mainの引数
Environment.GetCommandLineArgs
Environment.CommandLine
Command
問3.アプリケーションの終了のさせ方として適切なものはどれでしょうか?
Mainの末端に到達
End
Environment.Exit