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

マルチスレッドで別スレッドからメインスレッドのテキストボックスに文字を表示するには

環境/言語:[VB 2010 Express]
分類:[.NET]

いつもお世話になっております。
環境は以下の通りです。

Windows XP SP3
VB.NET 2010 Express


現在マルチスレッドのプログラムを初めて組んでおります。
そこで、modSub.subHogeの「fmMain.Invoke(New dlgSetText・・・」の個所で
”ウィンドウ ハンドルが作成される前、コントロールで Invoke または BeginInvoke を呼び出せません。” というエラーが帰ってきます。

初めてマルチスレッドを勉強していますので、このようなコーディングでいいのかさえ分からないので
そこからご指摘して頂けたらありがたいです。


ソースが長いので簡単に処理の流れを記入しておきます。

○使用するフォーム、モジュール
 ・fmMain
 ・modSub

1.fmMainのBtn1をクリックし、別モジュールの関数(modSub.subHoge)を別スレッドで開始
2.別モジュールの関数(modSub.subHoge)からデリゲードを利用?し、fmMainのテキストボックスに文字を記載する

といった処理になっています。

以上、よろしくお願い致します。

'----------------------------------------------------------------------------------------------------
' ▽▽▽▽ fmMain ここから ▽▽▽▽
'----------------------------------------------------------------------------------------------------
Imports System.Threading
Imports TEST.modSub

Public Class fmMain

  '======================================================
  ' テスト用別スレッド
  '======================================================
  Private ThreadSub As System.Threading.Thread

  '======================================================
  ' @別スレッドスタートボタン
  '======================================================
  Private Sub btn1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn1.Click
    'このテキストボックスに文字を表示したい
    TextBox1.text = ""

    ThreadSub = New System.Threading.Thread(AddressOf modSub.subHoge)
    ThreadSub.Start()
  End Sub

  '======================================================
  ' 別スレッドも終わらす
  '======================================================
  Private Sub fmMain_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
    ThreadSub.Abort()
  End Sub
End Class
'----------------------------------------------------------------------------------------------------
' △△△△ fmMain ここまで △△△△
'----------------------------------------------------------------------------------------------------



'----------------------------------------------------------------------------------------------------
' ▼▼▼▼ modSub ここから ▼▼▼▼
'----------------------------------------------------------------------------------------------------
Imports System.Threading

Module modSub

  '======================================================
  ' デリゲード
  '======================================================
  Delegate Sub dlgSetText(ByVal txt As Object, ByVal text As String)

  '======================================================
  ' Bメインフォームのテキストボックスに書込み
  '======================================================
  Private Sub threadTextValue(ByVal sender As Object, ByVal strValue As String)
    Dim txt As TextBox = DirectCast(sender, TextBox)
    txt.Text = strValue
  End Sub

  '======================================================
  ' Aメインから別スレッドとして呼ばれる関数
  '======================================================
  Private Sub subHoge()
    fmMain.Invoke( _
     New dlgSetText(AddressOf threadTextValue), New Object() {fmMain.TextBox1, "別スレッドから値きました!"} _
           )
  End sub
End Module

'----------------------------------------------------------------------------------------------------
' ▲▲▲▲ modSub ここまで ▲▲▲▲
'----------------------------------------------------------------------------------------------------
■No30301に返信(なまちゃさんの記事)

My.Formsは、別のスレッドでは別のインスタンスを返します。よって、新しいスレッドのMy.Forms.fmMainは、メインスレッドのMy.Forms.fmMainとは違うものになってしまいます。これが原因ではないでしょうか?

もしこれが原因であれば、My.Formsは使わずに、別の方法(公開された静的プロパティを使うなど)によってfmMainのインスタンスを取得するようにしてみてください。
管理人様ご返事ありがとうございます。

> My.Formsは、別のスレッドでは別のインスタンスを返します。
> よって、新しいスレッドのMy.Forms.fmMainは、メインスレッドのMy.Forms.fmMainとは違うものになってしまいます。
> これが原因ではないでしょうか?

まずは、ここに驚いています。
私のイメージでは、別スレッドは「処理のみ」を動かすといったイメージでしたので
フォームなどは共通(メインスレッドと別スレッド)に使えるという認識でしたが、
別スレッドをSTARTさせた時に、1つの画面がまったく別物の画面としてできてしまい
スレッド間では画面など干渉しないという事なんですね。

> もしこれが原因であれば、My.Formsは使わずに、別の方法(公開された静的プロパティを使うなど)によってfmMainのインスタンスを取得するようにしてみてください。

勉強不足で申し訳ございませんが、「公開された静的プロパティを使うなど」とは何をさすのでしょうか?
公開してるのは、どこなんだろか・・・?といった感じで調べようにもよくわかない状態です。
もう少しヒントを頂けたら幸いです。


また、Delegateが別スレッド間での値のセッターゲッターみたいな役割をしてるんだと思っていましたが、その認識もまちがっているのでしょうか?
■No30338に返信(なまちゃさんの記事)

例えば
> Module modSub
Module modSub
Public fMain as frmMain


> New dlgSetText(AddressOf threadTextValue), New Object() {fmMain.TextBox1, "別スレッドから値きました!"} _
fmMain => fMain


> ThreadSub = New System.Threading.Thread(AddressOf modSub.subHoge)
fMain = Me
ThreadSub = New System.Threading.Thread(AddressOf modSub.subHoge)

のようにスレッド実行前にインスタンスをスレッドから参照できる変数に設定しておいてスレッド内ではfmMainではなく変数を見るようにするとか。

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