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

第30回 Windowsフォームアプリケーションのライフサイクル

2021/3/7

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

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.Windowsフォームアプリケーションの開始

1-1.Mainの隠蔽

前回はコンソールアプリケーションの開始と終了について扱いました。エントリーポイントであるMainから実行を開始し、Mainが終了することがアプリケーションの終了を意味にするのでした。

この原則はコンソールアプリケーション以外のすべてのアプリケーションでも共通なのですが、.NETのフレームワークはそれぞれのアプリケーションの特性に合わせて、より扱いやすいように工夫されています。

 

今回はその例としてWindowsフォームアプリケーションの場合を扱います。原則に従いながらも使いやすくアレンジされているということに注目してください。WPFやASP.NETなど他の種類のアプリケーションではまたライフサイクルのアレンジのされ方は異なりますが、一例としてWindowsフォームを通して、Mainから始まるライフサイクルをどのようがどういう手段でどういう姿にアレンジされているか知っておくと、他の種類のアプリケーションも理解しやすくなると思います。

 

実際にWindowsフォームアプリケーションを作成して確認してみましょう。

Windowsフォームアプリケーションは .NET Frameworkと.NET 5以降とで作成するときの選択肢が変わります。

この2つのWindowsフォームアプリケーションはVisual Studioでの操作方法は95%くらい同じですが、違いはあります。

.NET 5以降の方が新しいので今後も最新の機能が追加されていきますが、2021年2月時点では新しすぎて、情報が少なく、この5%の差で混乱することがあるかもしれません。.NET Frameworkの方は20年近い歴史があり、情報も多いので、多少古くてもとりあえず勉強でやってみるレベルならこちらを選択する方が良いかもしれません。今回はどちらでもお好みの方を選択してください。

なお、.NET Core 3.1 の Windowsフォームはこの記事では扱いません。

 

さて、Windowsフォームアプリケーションの場合、一般的にはMainなどは存在せず、プログラムの開始とともにフォームが表示され、フォームを閉じるとアプリケーションが終了するように見えます。

しかし、フォームのLoadイベントにブレークポイントを設定し、呼び出し履歴を確認すると、アプリケーションが Run と呼ばれるメソッドがから開始されていることがわかります。

※呼び出し履歴を右クリックして「外部コードの表示」を選択すると確認できます。

名前はMainではありませんが、引数を見ると string[] commandLine となっています。呼び出し履歴ウィンドウはC#の形式で定義を表示するので、これをVB風に表現すると commandLine As String() ということです。つまり、ここでコマンドラインを受け取るようになっているのですから、Mainと同じ役割と思ってよいです。

なぜ、Main ではないのでしょうか?

エントリーポイントの名前は慣例的にMainがよく使われますが、Main以外の名前を使用することもできます。でも、それだけが理由ではありません。

VBのWindowsフォームアプリケーションには「アプリケーションフレームワーク」という独自の仕組みがあり、VBのコンセプトである わかりやすく親しみやすい言語 を実現しようとしています。

アプリケーションフレームワークは、プログラマーに成り代わって、Windowsフォームアプリケーションのライフサイクルにありがちなよくある処理の面倒を見てくれているのです。そのためMainではなく独自の仕組みでエントリーポイントからライフサイクルが制御されているというわけです。

メモ メモ  - 名前が Main でない理由は私の推測です。

 

1-2.アプリケーションフレームワーク

アプリケーションフレームワークが 具体的に何をやってくれているかは、プロジェクトのプロパティで確認できます。

Windowsフォームアプリケーションのプロジェクトのプロパティのアプリケーションページの下の方には、「Windows アプリケーション フレームワーク プロパティ」と呼ばれる領域があります。

ここが、VBがプログラマーに成り代わって面倒を見てくれるアプリケーションフレームワークの機能です。

代表的な機能はあとで説明することにして、ここでは赤枠の中で一番上にある 「アプリケーションフレームワークを有効にする」というチェックに注目します。

このチェックをはずすとアプリケーションフレームワークを無効にできます。

試しにアプリケーションフレームワークを無効にした状態で、Loadイベントにブレークポイントを設定して呼び出し履歴を確認してみましょう。

.NET Frameworkの場合は、このまま実行するとすぐに確認できますが、.NET 5の場合は、もうふた手間必要です。

一つ目の手間は、少し上にあるスタートアップフォームに Form1 を選択してください。

二つ目の手間は、ソリューションエクスプローラーで「すべてファイルを表示」して、My Project の配下にある、Application.Designer.HighDpi.vb を開きます。

表示されるプログラムの一番上に、#Const APPLICATION_FRAMEWORK = True という行があるので、この True を False にして保存します。

これで実行できるようになります。

(繰り返しますが、このふた手間が必要なのは.NET Core/.NET 5以降の場合です。.NET Frameworkの場合はこのようなプログラムはそもそも存在しません。)

 

実行して、さきほどと同じように確認すると今度はこのように Main が出現します。つまり、通常モードということですね。

しかし、ここにVBならではのからくりがあります。このMainプロシージャがどこにあるのか一生懸命探してもどこにも見つかりません。実はこのMainはプログラム時点では存在していなくて、コンパイルするときにVBが自動的に生成しているものなのです。あくまでもMainをプログラマーから隠して、プログラマーにはフォームのプログラムに集中してもらいたいというVBの思想がここに表れています。C#の場合は、そもそもアプリケーションフレームワークというものは存在せず、WindowsフォームアプリケーションでもMainが堂々と存在しています。

 

メモ メモ  - .NET 5以降のアプリケーションフレームワーク

2021年2月現在、.NET 5のWindowsフォームアプリケーションはまだ細かい機能が整っていません。本文中で説明したように追加の手順が必要であったり、画面の設定値が少し納得いかない場合などもあります。.NET 5のWindowsフォームアプリケーションについて 2020年11月3日付のマイクロソフトのBLOGでは、次のように述べられています。

Updates to Visual Studio dialogs: Updates have been made to a few dialogs to support Visual Basic features in the new project format. This work is ongoing.

Visual Studio の画面の改善: いくつかの画面でVisual Basicの新しいプロジェクト種別の機能をサポートするように改善しています。この作業は現在進行中です。

Visual Basic WinForms Apps in .NET 5 and Visual Studio 16.8 | .NET Blog (microsoft.com)

 

発展 発展学習  -  自動生成された Main の中身

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

ILSpyというツールは、コンパイルされたexeやdllから元のソースコードを復元する機能があります。完全に元のソースコードを復元することは理論上不可能なのですが、だいたいこんなものだったはずというところがわかります。

ILSpyを使ってコンパイルされたexeを見ると、自動的に生成されたMainがどのようなものかわかります。中身は Application.Run を呼び出しているだけです。Application.Runは、メインとなるフォームを表示して、そのフォームを監視し続けます。そして、フォームが閉じると役割を終えて実行を終了します。Application.Runが実行を終了すると、Mainの末端に到達するのでアプリケーションは終了します。この動作はまさにコンソールアプリケーションと同じですね。

 

1-3.Mainの露出

アプリケーションフレームワークが無効な場合、Main を自分でプログラムすることも可能です。

通常はアプリケーションフレームワークを使った方がいろいろ楽で便利なので、自分で Main をプログラムすることはお勧めではありません。やり方だけ紹介しておきます。

アプリケーションフレームワークが有効な場合と無効な場合とで、よく見ると、スタートアップの表記が変わります。

アプリケーションフレームワークが有効な場合、「スタートアップフォーム」という設定名になっており、どのフォームから開始するかというフォーム中心のアプローチであることがわかります。

アプリケーションフレームワークが無効な場合、「スタートアップオブジェクト」という設定名に変化し、フォーム以外も選択できます。

このとき、選択肢に Sub Main が出現します。

Sub Mainを選択した場合、自分でSub Mainをプログラムする必要があります。

たとえば、Program.vbというファイルをプロジェクトに追加して次のように記述します。

VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Public Class Program

    Public Shared Sub Main()

        Debug.WriteLine("Sub Mainが呼び出されました。")
        Application.Run(New Form1)
        Debug.WriteLine("Sub Mainを終了します。つまり、アプリケーションは終了します。")

    End Sub
End Class

Debug.WriteLineで出力される場所

このプログラムでは、無条件で Application.Run を使ってForm1を起動していますが、条件をつけて、ある場合にはForm2から開始したり、そもそも画面を表示しないというように制御したり、フォームが閉じられた後に、何か複雑な処理を実行したり思いのままプログラムできます。

Sub Mainではなく、Function Main にしたり、引数でコマンドラインを受け取れるように Main(args As String()) と宣言することもできますがあまり意味はありません。前回説明したように、終了コードを返したい場合は、Environment.ExitCode が使用できますし、コマンドライン引数を取得したいのであれば Environment.GetCommandLineArgs や Environment.GetCommandLine が使用できるからです。

 

2.Windowsフォームアプリケーションの終了

2-1.シャットダウンモード

アプリケーションフレームワークが有効な場合、Windowsフォームアプリケーションの終了はアプリケーションフレームワークのシャットダウンモードによって制御されます。

シャットダウンモードは上述のプロジェクトのプロパティ画面で選択できます。

次の2つの選択肢があります。

スタートアップフォームが閉じるとき

この設定がデフォルトです。デフォルトでは 最初に開く Form1 を閉じるとアプリケーション全体が終了します。

たとえ、他にForm2, Form3などが開いていても 最初に開いたForm1 が閉じるとすべて終了します。

スタートアップフォームを同じプロパティ画面でForm1以外に変更することもできます。

最後のフォームが閉じるとき

これを選択すると、複数のフォームが同時に開くようなアプリケーションの場合、すべてのフォームを閉じるまでアプリケーションは終了しません。

 

なお、前回説明した End や Environment.Exit はアプリケーションを強制終了するため、アプリケーションフレームワークの設定に従いません。これらの使用は避けるべきです。

メモ メモ  - End と Environment.Exit は使用しない

Windowsフォームアプリケーションを終了させる良い方法は、後で説明するように、Me.Close です。

私がレビュー担当者で End を見かけたら NG を出します。Environment.Exit には終了コードを返す機能もあるので、Mainの末端などごく限定的な状況で使用されているなら、ダメとは言いませんが、たいがいNGです。その時点でプログラムが終了してしまうので、後日プログラムの変更で終了時の挙動を変更したいときに、どこかで Environment.Exit があるとやりにくいですし、TryのFinallyなども実行されなくなるので、Environment.Exitがどこかにあるというだけで気にしないといけないことが増えるからです。

 

2-2.FormClosingイベント

フォームが閉じようとするときには、FormClosingイベント(読み方:FormClosing=フォームクロージング)が発生します。FormClosingイベントでは、まだ、フォームが閉じられておらず、フォームを閉じる処理をキャンセルすることができます。たとえば、未保存のデータがある場合に、保存しないで終了するかという確認メッセージを表示し、キャンセルという選択肢を示すことができます。

VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing

    Dim result As MsgBoxResult

    result = MsgBox("終了してよろしいですか?", vbQuestion Or vbOKCancel)

    If result = MsgBoxResult.Cancel Then
        e.Cancel = True
    End If

End Sub

フォームが閉じるのをキャンセルするにはこの例のように FormClosingイベントの 引数 e の Cancel プロパティに True を設定します。

引数 e の CloseReasonプロパティ(読み方:CloseReason=クローズリーズン)を使うとフォームが閉じる原因もわかるので、原因によって処理を変えることもできます。

代表的な理由は次のようなものです。

CloseReasonの値 フォームが閉じられようとしている理由
UserClosing ユーザーが右上の × をクリックしたり、Alt + F4 を押すなどのユーザー操作。
WindowsShutDown Windowsのシャットダウン。
TaskManagerClosing タスクマネージャーで(アプリケーションではなく)フォームを選択してタスクの終了を選択した。
ApplicationExitCall Application.Exit(後述)メソッドが呼び出された。

理由の完全な一覧はMicrosoft Docsを参照してください。

CloseReason 列挙型 (System.Windows.Forms)

 

次のプログラムでフォームが閉じる理由を確認できます。

VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
    Select Case e.CloseReason
        Case CloseReason.UserClosing
            MsgBox("UserClosing")
        Case CloseReason.TaskManagerClosing
            MsgBox("TaskManagerClosing")
        Case CloseReason.ApplicationExitCall
            MsgBox("ApplicationExitCall")
        Case Else
            MsgBox(e.CloseReason.ToString)
    End Select
End Sub

 

フォームが閉じられることが確定して、閉じられると FormClosedイベント (読み方:FormClosed=フォームクローズド)が発生します。

この段階ではもはやフォームを閉じないという選択肢はないので、未保存のデータの保存など最後に残っているやるべき処理があるとしたら、それを実行します。

 

 

2-3.Windowsフォームアプリケーションの終了のさせ方

ここまで説明したように、Windowsフォームアプリケーションにはライフサイクルに関していろいろな仕掛けがあり、便利に使用できるようになっています。

しかし、End や Environment.Exit のようなキーワードでアプリケーションを強制終了させるとこれらの仕掛けは台無しになってしまうので良くありません。

Windowsフォームアプリケーションを正しく終了させるには単にフォームを閉じるのが最も良い方法です。そのためにはフォームの Close メソッドを使用します。

 

複数のフォームが開かれていて、シャットダウンモードが 「最後のフォームが閉じるとき」の場合は、Application.Exit を使用することもできます。これはEnvironment.Exitに見かけが似ていますが、強制終了するわけではなく、すべてのフォームに対して正規の終了手順を実施してくれます。たとえば、FormClosingイベントも発生します。

 

3.アプリケーションイベント

3-1.アプリケーションイベント

アプリケーションフレームワークはアプリケーションのライフサイクルに従ってイベントを発生させます。

これらをアプリケーションイベントと呼びます。

アプリケーションイベントには次の6種類があります。ApplyHighDpiModeイベントは .NET 5以降でのみ使用可能です。

イベント 発生するタイミング
Startup アプリケーションが開始したとき
Shutdown アプリケーションの終了したとき
StartupNextInstance アプリケーションのインスタンスがもう1つ起動されたとき
UnhandledException 未処理の例外が発生した時
NetworkAvailabilityChanged ネットワークが切断/接続されたとき
ApplyHighDpiMode アプリケーションで最初のフォームが表示されるときに1回だけ発生します。このイベントでアプリケーションの高DPIモードを設定できます。
Visual Basic WinForms Apps in .NET 5 and Visual Studio 16.8 | .NET Blog (microsoft.com)

このうち、UnhandledExceptionについては、既に  第22回 例外処理の決定 で説明しています。

アプリケーションの開始はスタートアップフォームに指定されているフォーム(デフォルトではForm1)の、Loadイベントや、Shownイベントでも代わりになります。シンプルなアプリケーションならそれで十分でしょう。Shutdownイベントの方もフォームのFormClosingイベントや、FormClosedイベントで代えることこともできます。

複数のフォームを制御する複雑なアプリケーションの場合は、開始や終了の処理をフォームに記述せず、アプリケーションイベントを使用したほうが構成がシンプルになり、フォーム単体での使い勝手も向上します。

また、このイベント内は WindowsFormApplicationBaseクラス の機能を利用することができます。

私の感覚ではあまり魅力的な機能はないのですが、場合によっては便利に使えるかもしれません。

機能の一覧はMicrosoft Docsをご覧ください。

WindowsFormsApplicationBase クラス (Microsoft.VisualBasic.ApplicationServices) | Microsoft Docs

 

3-2.Startupイベントの使用例

ここでは、簡単にStartupイベントを利用して、メインフォーム(デフォルトではForm1)を表示する前にライセンスの同意確認を表示する例を紹介します。ライセンスへの同意がなかった場合は、フォームを終了せずアプリケーションを終了します。

アプリケーションイベントを利用するには、アプリケーションフレームワークのアプリケーションイベントの表示ボタンをクリックします。

アプリケーションイベントの表示

このボタンをクリックすると、ApplicationEvents.vb というファイルがプロジェクトに追加され、エディターで表示されます。初期状態では次のようになっています。

VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Namespace My
    ' 次のイベントは MyApplication に対して利用できます:
    ' Startup:アプリケーションが開始されたとき、スタートアップ フォームが作成される前に発生します。
    ' Shutdown:アプリケーション フォームがすべて閉じられた後に発生します。このイベントは、アプリケーションが異常終了したときには発生しません。
    ' UnhandledException:ハンドルされない例外がアプリケーションで発生したときに発生します。
    ' StartupNextInstance:単一インスタンス アプリケーションが起動され、それが既にアクティブであるときに発生します。
    ' NetworkAvailabilityChanged:ネットワーク接続が接続されたとき、または切断されたときに発生します。
    Partial Friend Class MyApplication
    End Class
End Namespace

ここにイベントを追加するには、自分でキーボードからイベントハンドラーをすべて入力してもよいのですが、ナビゲーションバーを使うと便利です。

ナビゲーションバーが表示されていない場合は、Visual Studio の[ツール]メニューから、[オプション] - [テキストエディター] - [Basic] と進み、「ナビゲーションバー」にチェックを付けて、OK をクリックすると表示されます。

テキスト入力のカーソルを Partial Friend Class MyApplication の位置に移動させ、

ナビゲーションバーの真ん中のドロップダウンリストの中から、「(My Applicationイベント)」 を選択し、右のドロップダウンリストを展開すると使用可能なイベントの一覧が表示されます。

ここで使いたいイベントを選択すると、そのイベントのハンドラーが自動的に生成されるので便利です。

Startupを選択するイベントハンドラーが生成されるでの、次のようにプログラムしましょう。

VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Private Sub MyApplication_Startup(sender As Object, e As StartupEventArgs) Handles Me.Startup

    Dim answer As MsgBoxResult
    answer = MsgBox("ライセンスに同意しますか?", vbQuestion Or vbYesNo)

    If answer = MsgBoxResult.No Then
        e.Cancel = True
    End If

End Sub

このイベントの引数の Cancel を True に設定することでアプリケーションを起動させないようにできます。

この例は簡易的に MsgBox を使っていますが、ライセンス同意専用画面を別のFormで作っておき、利用することもできます。

 

4.アプリケーションフレームワークの便利機能

4-1.認証モード

アプリケーションフレームワークにはライフサイクルと直接関係ない機能もあります。

たとえば、認証機能はアプリケーションを実行しているユーザーが誰かを特定します。

認証の詳細は本題ではありませんが、簡単に紹介します。

アプリケーションでユーザー名を取得するには次のようにします。

VB2005 VB2008 VB2010 VB2012 VB2013 VB2015 VB2017 VB2019

Dim userName As String = My.User.Name
MsgBox(userName)

この結果が、認証モードが「Windows」の場合と「アプリケーション定義」の場合とで異なります。

認証証モードを「Windows」にするとWindowsにログインしているユーザーをアプリケーションを実行しているユーザーとみなします。「アプリケーション定義」にすると、ユーザーが誰であるか判断するのはプログラマーの役割になります。アプリケーション定義にする場合は、アプリケーション独自のログイン画面などを表示してユーザーにIDとパスワードを入力させるような使い方になるでしょう。

プロジェクトを右クリックして、[追加] - [新しい項目] で Windows Forms を選択すると、ログインフォームという選択肢があり、IDとパスワードを入力する簡単な画面のひな形を挿入できます。

 

4-2.スプラッシュスクリーン

簡単に利用できるアプリケーションフレームワークの機能に、スプラッシュスクリーンがあります。スプラッシュスクリーンとはアプリケーションが起動するときに表示される会社名や製品名が表示される小さな画面のことです。起動に時間がかかるアプリケーションの場合、スプラッシュスクリーンを表示して時間を稼ぐのが常套手段です。

自分でスプラッシュスクリーン用のフォームを作成し、スプラッシュスクリーンに設定すると、アプリケーション起動時に自動的に表示してくれます。

自分でゼロからスプラッシュスクリーンを作成するのが面倒な場合は、プロジェクトを右クリックして[追加] - [新しい項目] から、「スプラッシュ スクリーン」を追加すると、スプラッシュスクリーン風のフォームが追加されるので、とりあえずこれを利用してもよいでしょう。

 

4-3.単一インスタンスのアプリケーション

単一インスタンスのアプリケーションを作成するにチェックを入れると、同じアプリケーションを複数起動できなくなります。メモ帳やExcelのようにいくつでもアプリケーションを起動できるようなものが多いですが、たまに、2つ目を起動しようとすると、1つ目に起動していた画面がアクティブになるだけというようなアプリケーションもあります。

このように、ただ1つしか起動できないアプリケーションというものをここにチェックを入れるだけで実現できます。