月間ウインドウズデベロッパーマガジン10月号のざっくりわかるインターネットプログラミングでのSyslogサーバのサンプルをVS2005 Beta2上でC#で作り替えてみました。
この連載のまま作ると受信データをテキストボックスのTextプロパティに追加しようとした段階でスレッド違反の例外が発生する。これはVS2005上でVBで雑誌のサンプルコードをそのまま実装しても同様。
基本的に別スレッドからWinFormのコントロールにアクセする場合には、Invokeを使って、デリゲートを経由して操作しなくてはいけません。
そして、その修正を行ったのが以下のコード。
namespace SyslogCS
{
public partial class Form1 : Form
{
private UdpClient udpClient;
private IPEndPoint riep;
private Thread receive;
///
/// ワーカースレッドからフォーム上のテキストボックスにテキストを追加するためのデリゲート
///
/// 追加テキスト
private delegate void MessageAppendDelegate(string str);
///
/// コンストラクタ
///
public Form1() {
InitializeComponent();
}
///
/// Syslog受信のためのワーカースレッド
///
private void ReceiveThread() {
udpClient = new UdpClient(514);
riep = new IPEndPoint(IPAddress.Any, 0);
byte[] bary;
MessageAppendDelegate dlg = new MessageAppendDelegate(AppendMessage);
while (true) {
//受信
bary = udpClient.Receive(ref riep);
//表示
string appText = Encoding.Unicode.GetString(bary) + "\r\n";
//Ivokeでデリゲートを呼び出す
this.Invoke(dlg, new object[] {appText});
}
}
///
/// フォーム上のSyslog表示用テキストボックスにテキストを追加する。
///
/// 追加テキスト
private void AppendMessage(string str) {
this.textBox1.AppendText(str);
}
///
/// フォーム起動時のイベントプロシージャ
/// Syslog受信用のワーカースレッドを作成する。
///
///
///
private void Form1_Load(object sender, EventArgs e) {
receive = new Thread(this.ReceiveThread);
receive.Name = "recieve";
receive.Start();
}
///
/// フォーム終了時のイベントプロシージャ
/// ワーカースレッドを停止し、開いていたポートを閉じる。
///
///
///
private void Form1_FormClosed(object sender, FormClosedEventArgs e) {
receive.Abort();
udpClient.Close();
receive.Join();
}
}
}
VB 7.1だと記事のサンプル通りで普通に動くのですが、なぜ?
コンパイラがごにょごにょ上手くやってくれるのかもしれん。でも、VB 8.0だと動かないから、安易にワーカースレッドを使った処理をしていた場合には注意が要りそうですね。
追記: (2005.09.04 11:56)
コメント
もともと7.1ではノーチェックだから
もちろんWindows APIのみなんかの場合にもノーチェック
8ではあえてチェックが入っている為です。
中さんありがとうございます。
まともにVBの評価をしてこなかったのもバレバレなんですが、ちょっと気まずい仕様のような。VB8で正常になって何よりという感じです。
C#でも状況はいっしょだったよね、たしか。
onoさんの書かれた通りC# 2003も同じでしたね・・・。
試してみました。
やっぱり.NET 2.0の使用の方が正解でしょうね。
スレッドセーフではない物に、別スレッドから操作できてしまうのはおかしい。
なにこの糞なコード。 { を書く位置も理解できないの?