Windows FormsでRS-232Cを掌握する:UIを殺さない「非同期シリアル通信」の極意
こんにちは。現場で「画面が固まった!」と叫ばれる悲劇を幾度となく見てきた、自動化エンジニアの先輩です。
Visual Basic (VB.NET)で計測器やバーコードリーダーを扱う際、多くの初心者が「メインスレッドで通信を待機してしまう」という罠に陥ります。通信相手からの応答をただ待つコードを書くと、Windowsは「このアプリは応答なし」と判断し、画面を真っ白にしてフリーズさせます。
今日は、そんな事態を回避し、現場レベルで通用する「堅牢な非同期シリアル通信」の設計図を伝授します。
—
1. なぜ「イベント駆動」が最強なのか
VB.NETの`System.IO.Ports.SerialPort`コンポーネントには、`DataReceived`という強力なイベントが備わっています。
初心者はつい「送信して、すぐに受信して…」という同期的な処理(`ReadLine()`など)を書きがちですが、これでは相手機器の気まぐれなタイミングや通信遅延に振り回されます。「データが来た時だけ反応する」というイベント駆動こそが、UIを軽快に保つ唯一の道です。
—
2. 堅牢なシリアル通信の実装例
以下のコードは、UIスレッドを汚染せず、かつデータを取りこぼさないためのテンプレートです。
Imports System.IO.Ports
Public Class SerialManager
‘ 通信ポートのインスタンス
Private WithEvents _serialPort As SerialPort
Public Sub InitializeSerial(portName As String)
_serialPort = New SerialPort(portName, 9600, Parity.None, 8, StopBits.One)
‘ タイムアウト設定は重要:これがないと一生戻ってこない可能性がある
_serialPort.ReadTimeout = 2000
_serialPort.WriteTimeout = 2000
_serialPort.Open()
End Sub
‘ 【重要】データ受信イベントは別スレッドで走る
Private Sub _serialPort_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles _serialPort.DataReceived
Try
‘ データを読み出す
Dim receivedData As String = _serialPort.ReadExisting()
‘ UIを操作する場合は、必ずInvokeを使う(スレッドセーフの基本!)
Me.Invoke(Sub()
UpdateUI(receivedData)
End Sub)
Catch ex As Exception
‘ 通信エラー時のログ出力処理など
Debug.WriteLine(“通信エラー: ” & ex.Message)
End Try
End Sub
Private Sub UpdateUI(data As String)
‘ ここでテキストボックス等に反映
TextBox1.AppendText(data & vbCrLf)
End Sub
End Class
—
3. ここがプロのこだわり:3つの鉄則
① UIスレッドと通信スレッドを「分離」せよ
コード内に登場した `Me.Invoke` は、別スレッド(通信用)で受信したデータを、UIスレッド(画面表示用)へ安全に受け渡すための「架け橋」です。これを使わずに直接テキストボックスを操作すると、高確率で「クロススレッド操作の無効化」というエラーでアプリが強制終了します。
② 「パケットの断片化」を想定せよ
ネットワークやシリアル通信は、1回の命令で全てのデータが届くとは限りません。「バーコードの半分だけ届いた」という状況は日常茶飯事です。
- 対策: 受信データをバッファ(String型の変数など)に蓄積し、終端文字(`CR/LF`など)が確認できたタイミングで初めて「1件のデータ」として処理する設計にしましょう。
③ タイムアウトは「防御壁」
計測器が故障した際、プログラムが永久に返事を待ち続けると、現場のオペレーターはパニックになります。必ず `ReadTimeout` を設定し、`Try-Catch` ブロックで囲むことで、通信失敗時にも「エラー検知」をして画面に知らせる仕組みを組み込んでください。
—
4. 陥りやすいエラーと回避策
- エラー:「ポートが既に開かれています」
- 原因: フォームを閉じた際にポートを解放していない。
- 対策: `FormClosing` イベントで必ず `_serialPort.Close()` を呼び出してください。リソースの解放はエンジニアの礼儀です。
- エラー:「データが文字化けする」
- 原因: ボーレート(通信速度)やパリティ設定の不一致。
- 対策: 相手機器のマニュアルを信じず、まずは接続設定を一つずつ再確認しましょう。
—
最後に:先輩からのアドバイス
VB.NETでの通信開発は、目に見えない信号との対話です。最初は難しく感じるかもしれませんが、「メイン画面をフリーズさせない」「例外処理でアプリを落とさない」という2点を守るだけで、あなたのプログラムの信頼性は一気にプロのレベルへ引き上がります。
このコードをベースに、まずは自分の手元にある計測器やバーコードリーダーと通信してみてください。動いた時の感動は、何にも代えがたいものです。
分からないことがあれば、いつでもまた聞きに来てくださいね。応援しています!
