ネットワーク断絶は「例外」ではない。「仕様」である。
業務アプリケーションを構築する際、多くのエンジニアが犯す最大の過ちは、ネットワーク接続を「常に安定している前提」で設計することだ。VPNの瞬断、無線LANのローミング、DHCPのリース更新。これらは現代のオフィス環境において不可避な「日常」である。
本稿では、`System.Net.NetworkInformation.NetworkChange` クラスを軸に、単なる死活監視を超えた、レジリエント(回復力のある)なWindows Formsアプリケーションの設計思想を説く。
—
1. NetworkChangeの真実とイベントの罠
`NetworkChange` クラスは、ネットワーク状態の変化を検知する標準的な手段だが、注意が必要だ。このクラスのイベントは「OSの通知サブシステム」に依存している。つまり、コードの書き方次第では、メモリリークの温床となり、アプリの寿命を縮める。
極限の設計パターン:シングルトンによる状態管理
イベントハンドラを不適切にフォームへ直書きしてはいけない。フォームの破棄時にイベントの解除(`-=`)を忘れると、ガベージコレクタは一生そのフォームを回収できない。以下の「監視コンポーネント」による抽象化が最適解だ。
.net
Imports System.Net.NetworkInformation
Public NotInheritable Class NetworkMonitor
‘ 外部からのインスタンス生成を制限
Private Sub New()
AddHandler NetworkChange.NetworkAvailabilityChanged, AddressOf OnNetworkChanged
End Sub
Private Shared _instance As New NetworkMonitor()
Public Shared ReadOnly Property Instance As NetworkMonitor
Get
Return _instance
End Get
End Property
Public Event StatusChanged(isOnline As Boolean)
Private Sub OnNetworkChanged(sender As Object, e As NetworkAvailabilityEventArgs)
‘ NetworkAvailabilityChangedは別スレッドで飛ぶため、UI更新にはInvokeが必要
RaiseEvent StatusChanged(e.IsAvailable)
End Sub
‘ アプリ終了時に明示的にリソースを解放する契約
Public Sub Dispose()
RemoveHandler NetworkChange.NetworkAvailabilityChanged, AddressOf OnNetworkChanged
End Sub
End Class
—
2. SQLiteによる「トランザクション退避」戦略
ネットワークが切断された瞬間、ユーザーの入力を捨てるのは素人の仕事だ。プロは、「ローカルのSQLite(System.Data.SQLite)をトランザクションのバッファとして利用する」。
1. データ送信時: まずローカルDBの「未送信キュー」テーブルにインサートする。
2. オンライン時: 非同期のバックグラウンドワーカーがキューを監視し、サーバーへ同期(プッシュ)する。
3. サーバー応答確認後: ローカルDBのフラグを更新、あるいは物理削除する。
この設計により、ユーザーはネットワークの死活を意識することなく、「送信ボタン」を押した瞬間にタスクを完了できる。
—
3. レガシー環境を生き抜くためのWindows API活用
時に、`NetworkChange` だけでは検知できない「論理的な切断(IPは振られているが、ゲートウェイが死んでいる等)」に直面する。この場合、`wininet.dll` の `InternetGetConnectedState` を用いたネイティブチェックを併用するのが、伝説的なエンジニアの嗜みだ。
.net
Imports System.Runtime.InteropServices
Public Class NativeNetworkChecker
Private Shared Function InternetGetConnectedState(ByRef lpdwFlags As Integer, ByVal dwReserved As Integer) As Boolean
End Function
Public Shared Function IsActuallyConnected() As Boolean
Dim flags As Integer
‘ 0x01: INTERNET_CONNECTION_MODEM, 0x02: INTERNET_CONNECTION_LAN
‘ APIを叩いて実質的なコネクティビティを判定する
Return InternetGetConnectedState(flags, 0)
End Function
End Class
—
4. チーフアーキテクトからの提言:UIのスレッドセーフとUX
ネットワークが切断された際、UIをフリーズさせてはいけない。以下の運用ルールを徹底せよ。
- ステータスインジケータの常駐: メインフォームの右下に、通信状態を示す透過ステータスを配置する。
- 例外の隠蔽: `Ping` や `WebRequest` で発生する `WebException` は、UI層に投げず、通信層でキャッチして「再試行ポリシー(Exponential Backoff)」を自動実行せよ。
- 終了時の安全確保: フォームの `FormClosing` イベントで、未送信キューが残っている場合に「データが残っていますが終了しますか?」と警告し、強制終了を防ぐガードを実装せよ。
結びに代えて
ネットワークは不安定なものだ。だからこそ、その不安定さをコードの「一部」として取り込み、隠蔽し、ユーザーに平穏な体験を提供する。それこそが、我々エンジニアが成すべき「システム設計」の本質である。
VB.NETは古びた言語ではない。適切にマネージドのライフサイクルを制御すれば、依然としてWindows環境における最も強力な武器の一つとなる。さあ、この堅牢なアーキテクチャを実装し、現場のネットワークトラブルを過去のものにしよう。
