DOBON.NET DOBON.NETプログラミング掲示板過去ログ

スレッドでWindows.Forms.Controlを扱うとき

環境/言語:[Windows2000Pro、VB.NET2002]
分類:[.NET]

環境:Windows2000 , 言語:VB.NET2002

いつも拝見しています。nobです。

上位PCとTcpClientクラスを使用してメッセージ受信を行って、
そのメッセージを使用して処理を行うプログラムを作成しています。

frmMain:メイン画面
frmReceive:受信専用画面(受信したメッセージをListboxで表示する)

の2つのフォームがあり、frmReceiveにてスレッドにてメッセージ受信を行っています。
次のようなスレッドです。(コード長いため省略しています)

Public Sub ThreadProc()

Do

'接続
While resultConnect = False

'TcpCient作成
If Not mTcpClient Is Nothing Then
mTcpClient.Close()
mTcpClient = Nothing
End If
mTcpClient = New clsClient()

resultConnect = FncConnect()

Thread.Sleep(500)
End While


'読取
While resultReceive
reciveMessage = ""
resultReceive = mTcpClient.Receive(reciveMessage, length)

mReceiveBuff &= reciveMessage

'受信メッセージチェック
If mReceiveBuff.StartsWith(gMessage.HeaderMark) And mReceiveBuff.EndsWith(gMessage.FooterMark) Then

While mReceiveBuff.Length > 0
'受信メッセージの整形処理
oneMessage = FncMessageRemove(mReceiveBuff)

If Not oneMessage = "" Then
SyncLock Me
Dim args As Object() = {oneMessage}
Dim ar As IAsyncResult = Me.BeginInvoke(dlgtInsert, args)

ar.AsyncWaitHandle.WaitOne()

If ar.IsCompleted Then
Dim ret As Object = Me.EndInvoke(ar)
End If
End SyncLock
End If
End While

End If

Thread.Sleep(100)
End While

Loop

End Sub

スレッドの内容としては、
接続処理を行ってから、「受信処理−>ListBoxにInsert」のLoopです。

御教授頂きたいのは、この後のListBoxにあるメッセージの処理の仕方なんです。
やりたいことは、
1.ListBoxのItem(0)を取得して、Removeする。
2.その値から、frmMain上の各種オブジェクトを制御する。
なのですが、どのように取得すればいいのか分からず質問させていただきました。

下手な質問で申し訳ないのですが、どなたか、宜しくお願いします。
■No1495に返信(nobさんの記事)
> 御教授頂きたいのは、この後のListBoxにあるメッセージの処理の仕方なんです。
> やりたいことは、
> 1.ListBoxのItem(0)を取得して、Removeする。
> 2.その値から、frmMain上の各種オブジェクトを制御する。
> なのですが、どのように取得すればいいのか分からず質問させていただきました。

ご質問の意味を理解し切れていないため、もしかしたら全く見当違いの回答かもしれません。

コールバックデリゲートまたはイベントを使ってfrmMainのメソッドを呼び出してはいかがでしょうか。詳しくはヘルプをご覧ください。

・マルチスレッドプロシージャのパラメータと戻り値
http://www.microsoft.com/japan/msdn/library/ja/vbcn7/html/vaconparametersreturnvaluesforfreethreadedprocedures.asp
やはり、質問悪いですよね・・・
自分自身でやりたかったこととやりたいことを実現するための手法が
いまいちつかめていなかったと思います。

スレッドでは、tcpClientでメッセージ処理を行い、frmReceiveのListBoxに書き込むという処理を行っています。
このメッセージを受信したことをfrmMainの方に通知したいのですが、
どのような方法があるのか分からなくて、質問していました。

いろいろ自分なりに調べたつもりなのですが、スレッド内からControlを参照するサンプルコードは、
様々なものがあって、スレッド内から直接、フォームのControlをさわってるものもあれば、Invokeを使用して
さわってるものもあるので、混乱しています。
(どちらも動かしてみると、期待通りの動きはするのですが。。。)

管理人さんのレスを参考に、考えてみます。

取り急ぎお礼を。

ありがとうございます。
■No1561に返信(nobさんの記事)
> いろいろ自分なりに調べたつもりなのですが、スレッド内からControlを参照するサンプルコードは、
> 様々なものがあって、スレッド内から直接、フォームのControlをさわってるものもあれば、Invokeを使用して
> さわってるものもあるので、混乱しています。

分かりにくいというのは、全く同感です。ヘルプでは別スレッドからコントロールを扱うには必ずInvokeを使えというようなことが書かれていますが、マイクロソフトのサンプルコードを見ると、Invokeを使った例は皆無です。

私個人の意見としては、Invokeを使うか使わないかで迷った時は、とりあえず使った方がいいと思います。
本来なら、これで「出来ました。ありがとうございました。」と
お礼で締めくくりたいのですが、管理人さんからのサンプルコードを参照して
試したところ、次のような問題が生じたので、度々で申し訳ないのですが、
質問させてください。

今回、スレッドを使用して処理していた内容は、
1.メッセージを受信する。
2.メッセージをチェックする。
3.リストボックスに表示する。
でした。

スレッドを使用した理由が、
1.いつメッセージがくるか分からないので、常時起動させておきたい。
2.メインスレッドで処理すると、他ユーザ入力の応答が悪くなる。
からでした。

サンプルを試したところ、
イベント処理()が終了するまで、スレッドが待機状態?になりました。

期待した動作は、呼び出したイベントを待機することなく、お互い独自で処理していくように
したいのですが、これは可能なのでしょうか?

もしかしたら、このような処理を行う場合は、スレッドを使用するのではなく
プロセスを分けてしまって、ウィンドウメッセージでプロセス通信するのが一般的なのでしょうか?
■No1582に返信(nobさんの記事)
> サンプルを試したところ、
> イベント処理()が終了するまで、スレッドが待機状態?になりました。

イベントハンドラは呼び出したスレッドで処理されるので、そうなりますね。

> 期待した動作は、呼び出したイベントを待機することなく、お互い独自で処理していくように
> したいのですが、これは可能なのでしょうか?

意味がいまいち分かりませんが、また別スレッドを作成して処理させるとか、BeginInvokeを使うとかいうことではないですよね?
返事遅れてすいません。

イベントハンドラは、呼び出し元処理とは、非同期に動作するものと思っていました。

> 意味がいまいち分かりませんが、また別スレッドを作成して処理させるとか、BeginInvokeを使うとかいうことではないですよね?

確かに、管理人さんの言う通りに、再度、別スレッド作成してそっちの方で、メッセージに対応するコントロール処理を
行えば、期待する動きになると思うのですが、対象コントロール数が多くて、複雑なコードになってしますので、
呼び出し元のスレッドと非同期で動作する(しかし、処理先に渡すメッセージデータはきちんと同期が取れている)
何か都合のいいやり方があるのかなと聞いてしまいました。

もうすこし、別の方法も含めて考えてみようと思います。

管理人さんには、いろいろと返事を頂き大変参考になりました。
ありがとうございました。
■No1631に返信(nobさんの記事)

Invokeメソッドは、そのコントロールを作成したスレッドで実行せよということですので、BeginInvokeを使うのが良いのかもしれませんね。またまた見当違いかもしれませんが。

DOBON.NET | プログラミング道 | プログラミング掲示板