C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[10]: スレッドセーフにて、ComboBoxの値取得方法


(過去ログ 126 を表示中)

[トピック内 21 記事 (1 - 20 表示)]  << 0 | 1 >>

■75093 / inTopicNo.1)  スレッドセーフにて、ComboBoxの値取得方法
  
□投稿者/ ハルハル (1回)-(2015/02/26(Thu) 16:07:02)

分類:[VB.NET/VB2005 以降] 

2015/02/26(Thu) 17:54:26 編集(投稿者)
2015/02/26(Thu) 17:54:21 編集(投稿者)

Windows7 VB2008 でWindowsアプリケーション開発を行っています。

ハンディーをCOM接続して、SerialPortクラスを使用しています。

SerialPort.DataReceived イベント内で、
画面のコンボボックスの値を取得する必要があるのですが、
「有効ではないスレッド間の操作:コントロールが作成されたスレッド以外の
 スレッドからコントロール'ComboBox1'がアクセスされました。」

となり取得できません。

どなたかお願いします。


引用返信 編集キー/
■75094 / inTopicNo.2)  Re[1]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ 774RR (223回)-(2015/02/26(Thu) 16:51:23)
Form Application の仕様として UI スレッド以外からの UI 部品アクセスは禁止になっている。
で、そのエラー表示は別スレッド (Worker thread) から UI 部品をアクセスしたってことだ。
Form 部品の使い方仕様に反しているので当然のエラー、ってことになる。

ふつーに Timer timer1 を配置し timer1_Tick をハンドルするだけなら UI スレッド内で呼ばれるので
そういうエラーにはならないはずなんだけど・・・
BackgroundWorker でも使った?
とにかく異スレッドから UI 部品を直接操作しちゃ駄目。
で、どんなコードが必要になるかは今のつくり次第なのでソース提示なしには提案不可能。
# タイマーごときで Worker Thread 作ってるってのがなんだか微妙に間違ってる気がする。

具体的コードは、俺 VB 知らない人だから他の皆様にお任せってことで。
引用返信 編集キー/
■75096 / inTopicNo.3)  Re[2]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ ハルハル (2回)-(2015/02/26(Thu) 17:56:17)
No75094 (774RR さん) に返信
> Form Application の仕様として UI スレッド以外からの UI 部品アクセスは禁止になっている。
> で、そのエラー表示は別スレッド (Worker thread) から UI 部品をアクセスしたってことだ。
> Form 部品の使い方仕様に反しているので当然のエラー、ってことになる。
>
> ふつーに Timer timer1 を配置し timer1_Tick をハンドルするだけなら UI スレッド内で呼ばれるので
> そういうエラーにはならないはずなんだけど・・・
> BackgroundWorker でも使った?
> とにかく異スレッドから UI 部品を直接操作しちゃ駄目。
> で、どんなコードが必要になるかは今のつくり次第なのでソース提示なしには提案不可能。
> # タイマーごときで Worker Thread 作ってるってのがなんだか微妙に間違ってる気がする。
>
> 具体的コードは、俺 VB 知らない人だから他の皆様にお任せってことで。
まことに申し訳ございません。質問内容を間違えてましたm(__)m
友人にタイマーと一緒でしょ?という風に言われて、タイマーと書いてしまいました…
質問内容を修正しましたので、よろしくお願い致します。

引用返信 編集キー/
■75098 / inTopicNo.4)  Re[3]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ 774RR (224回)-(2015/02/26(Thu) 18:22:31)
ええー元発言をそっくり編集して入れ替えちゃったの? 俺の発言が意味不明になってる。
でもまあ俺が言いたいことは既に言い尽くしてあるし追加説明は必要ないだろうってことで。

引用返信 編集キー/
■75099 / inTopicNo.5)  Re[4]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ shu (699回)-(2015/02/27(Fri) 07:52:28)
Me.Invokeを使うか
ComboBoxのSelectedIndexChanged,SelectedValueChangedで値をprivate変数に取得しておくとか。
引用返信 編集キー/
■75100 / inTopicNo.6)  Re[5]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ ハルハル (3回)-(2015/02/27(Fri) 09:20:36)
No75099 (shu さん) に返信
> Me.Invokeを使うか
> ComboBoxのSelectedIndexChanged,SelectedValueChangedで値をprivate変数に取得しておくとか。
回答ありがとうございます。
Me.Invoke を使う場合 値をComboBoxに反映する方法はわかったのですが、取得する方法がわかりません。
SelectedValueChangedで private変数に保存の方が良いんですかね?

引用返信 編集キー/
■75101 / inTopicNo.7)  Re[4]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ ハルハル (4回)-(2015/02/27(Fri) 09:39:36)
No75098 (774RR さん) に返信
> ええー元発言をそっくり編集して入れ替えちゃったの? 俺の発言が意味不明になってる。
> でもまあ俺が言いたいことは既に言い尽くしてあるし追加説明は必要ないだろうってことで。
引用返信するのは違うかと思い…編集したら入れ替わってしまいました。
重ねて申し訳ないですm(__)m
コードとしては、単純にコンボ1か2が何か設定されてなければ
という判定をする箇所でエラーとなってしまいます。

If Len(ComboBox1.Text) = 0 Or Len(ComboBox2.Text) = 0 Then


>別スレッド (Worker thread) から UI 部品をアクセスしたってことだ
何か取得する方法はないのでしょうか?
引用返信 編集キー/
■75102 / inTopicNo.8)  Re[6]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ shu (700回)-(2015/02/27(Fri) 09:57:40)
No75100 (ハルハル さん) に返信
> ■No75099 (shu さん) に返信
>>Me.Invokeを使うか
>>ComboBoxのSelectedIndexChanged,SelectedValueChangedで値をprivate変数に取得しておくとか。
> 回答ありがとうございます。
> Me.Invoke を使う場合 値をComboBoxに反映する方法はわかったのですが、取得する方法がわかりません。
反映も取得も変わらない気がしますが、どのように記述されましたでしょうか?


> SelectedValueChangedで private変数に保存の方が良いんですかね?
>
スレッド間の移動が減るのでこの方が安定感はあるかと思います。
引用返信 編集キー/
■75103 / inTopicNo.9)  Re[5]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ 774RR (225回)-(2015/02/27(Fri) 11:08:35)
Invoke するってことはワーカスレッドと UI スレッドの間での行き来が発生するってこと。
あまり頻繁に使うと性能低下するかもしれない。

俺 C# でないと書けないから以下 C# のサンプル (わざと TextBox にしてみたり)
Invoke の返却値は System.Object だからキャストが必要で

private void backgroundworker1_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i=0; i<10; ++i)
    {
        System.Threading.Thread.Sleep(500);
        System.String got_text = Invoke( (Func<TextBox, System.String>)((TextBox textbox) => { return textbox.Text; }), textBox1) as System.String;
        System.Diagnostics.Debug.WriteLine(got_text);
    }
}

ピュア疑問:こんなときは .NET Framework 4.5 の async/wait のほうがよいのだろうか?

引用返信 編集キー/
■75106 / inTopicNo.10)  Re[7]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ ハルハル (5回)-(2015/02/27(Fri) 13:11:27)
2015/02/27(Fri) 13:16:52 編集(投稿者)

No75102 (shu さん) に返信
> ■No75100 (ハルハル さん) に返信
>>■No75099 (shu さん) に返信
> >>Me.Invokeを使うか
> >>ComboBoxのSelectedIndexChanged,SelectedValueChangedで値をprivate変数に取得しておくとか。
>>回答ありがとうございます。
>>Me.Invoke を使う場合 値をComboBoxに反映する方法はわかったのですが、取得する方法がわかりません。
> 反映も取得も変わらない気がしますが、どのように記述されましたでしょうか?
Delegate Sub SetTextCallback(ByVal [text] As String)

Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.EventArgs) Handles SerialPort1.DataReceived
Call SetText_ComboBox1("コンボリストにあるどれか")
End Sub

Private Sub SetText_ComboBox1(ByVal [text] As String)
If Me.ComboBox1.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText_ComboBox1)
Me.Invoke(d, New Object() {[text]})
Else
Me.ComboBox1.Text = [text]
End If
End Sub
こんな感じで記述したら、設定はうまくいきました。


引用返信 編集キー/
■75109 / inTopicNo.11)  Re[6]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ 774RR (226回)-(2015/02/27(Fri) 13:47:59)
VS2008 ってことは .NET Framework は 3.5 対応だっけ?
https://msdn.microsoft.com/ja-jp/library/bb822049.aspx
# 俺としては VS2010 ってか .NET 4.0 Client Profile 以後対象を強く推奨する
# はやく VS2013 買ってくれ > ウチの部署 (もう期末だからって理由で買ってくれないの)

InvokeRequired を自分で意識して使うのはずいぶんと古い記述方法だ。
http://www.atmarkit.co.jp/fdotnet/dotnettips/312ctrlinvoke/ctrlinvoke.html
この辺では性能的に InvokeRequired を使えとあるが、もう忘れてしまっていい。

ラムダ式は 3.5 でも使えたはずなので、もう少し新しい解説ページを探してみよう。
俺が参考にしたのはこのあたり
http://ufcpp.net/study/csharp/sp_delegate.html
あとやっぱり MSDN ページは必読。

4.5 ぢゃないから async/await は使えないのか・・・
引用返信 編集キー/
■75110 / inTopicNo.12)  Re[7]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ ハルハル (6回)-(2015/02/27(Fri) 14:26:23)
No75109 (774RR さん) に返信
> VS2008 ってことは .NET Framework は 3.5 対応だっけ?
> https://msdn.microsoft.com/ja-jp/library/bb822049.aspx
> # 俺としては VS2010 ってか .NET 4.0 Client Profile 以後対象を強く推奨する
> # はやく VS2013 買ってくれ > ウチの部署 (もう期末だからって理由で買ってくれないの)
>
> InvokeRequired を自分で意識して使うのはずいぶんと古い記述方法だ。
> http://www.atmarkit.co.jp/fdotnet/dotnettips/312ctrlinvoke/ctrlinvoke.html
> この辺では性能的に InvokeRequired を使えとあるが、もう忘れてしまっていい。
>
> ラムダ式は 3.5 でも使えたはずなので、もう少し新しい解説ページを探してみよう。
> 俺が参考にしたのはこのあたり
> http://ufcpp.net/study/csharp/sp_delegate.html
> あとやっぱり MSDN ページは必読。
>
> 4.5 ぢゃないから async/await は使えないのか・・・
何度も回答ありがとうございます!
.NET Framework は 3.5ですね…
こちらの環境では、いまだに2008が主流となってしまっています(ToT)
参考URLありがとうございます。この辺りもやはり設定のサンプルで取得のサンプルがないですね(><)
いろいろキャストして試してみてるのですが、まだうまくいってません…

引用返信 編集キー/
■75111 / inTopicNo.13)  Re[8]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ 774RR (227回)-(2015/02/27(Fri) 14:52:30)
> 取得のサンプルがないですね
C# のラムダ式で取得の例は No75103 で出しておいたんだけど?
俺マヂで VB わからない(っていうか覚える気がない)んで・・・
引用返信 編集キー/
■75112 / inTopicNo.14)  Re[8]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ shu (702回)-(2015/02/27(Fri) 15:34:08)
No75106 (ハルハル さん) に返信

設定を
Sub メソッド名(Value As T)
の形で書くとすると

取得は
Function 関数名() As T
と書けることは分かりますでしょうか?


Invokeを使わないでこれらを呼びたい場合、
メソッド名(Value)
Result = 関数名()
のように書けるのは分かりますでしょうか?


Invokeを使った場合
Me.Invoke(DirectCast(AddressOf メソッド名, Action(Of T)), Value)
Result = Me.Invoke(DirectCast(AddressOf 関数名, Func(Of T)))
のように書けます。ただしInvokeの戻りがObjectであるため正確にはResultに設定する前に
キャスト(CTypeなど)を行う必要があります。

引用返信 編集キー/
■75118 / inTopicNo.15)  Re[9]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ ハルハル (7回)-(2015/02/27(Fri) 18:12:03)
No75112 (shu さん) に返信
> ■No75106 (ハルハル さん) に返信
>
> 設定を
> Sub メソッド名(Value As T)
> の形で書くとすると
>
> 取得は
> Function 関数名() As T
> と書けることは分かりますでしょうか?
>
>
> Invokeを使わないでこれらを呼びたい場合、
> メソッド名(Value)
> Result = 関数名()
> のように書けるのは分かりますでしょうか?
>
>
> Invokeを使った場合
> Me.Invoke(DirectCast(AddressOf メソッド名, Action(Of T)), Value)
> Result = Me.Invoke(DirectCast(AddressOf 関数名, Func(Of T)))
> のように書けます。ただしInvokeの戻りがObjectであるため正確にはResultに設定する前に
> キャスト(CTypeなど)を行う必要があります。
>
なんとかできました(^o^) ありがとうございます!
理解するまでに時間が掛かった…というより、まだ理解しきれてませんが(--;
この他に、Enable の制御と ComboBoxのアイテムの追加とクリアもあることが判明しましたorz
テキストプロパティーの設定と取得はなんとなくわかったのですが、
Enableは一緒?だとしても、アイテムの追加とクリアはまた違う感じなんですよね?

>>774RRさん
VBだからか Textプロパティーだと、キャストしなくてもいけちゃいました…
キャスト部分は、また勉強します!



引用返信 編集キー/
■75119 / inTopicNo.16)  Re[10]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ Azulean (440回)-(2015/02/27(Fri) 23:29:39)
No75118 (ハルハル さん) に返信
> この他に、Enable の制御と ComboBoxのアイテムの追加とクリアもあることが判明しましたorz
> テキストプロパティーの設定と取得はなんとなくわかったのですが、
> Enableは一緒?だとしても、アイテムの追加とクリアはまた違う感じなんですよね?

そもそも、DataReceived イベントの中から何度も Invoke するぐらいなら、イベント受信直後に Invoke して、その先ですべての処理を書くぐらいの意識でよさそうに感じます。
その方法は検討されているのでしょうか?
引用返信 編集キー/
■75121 / inTopicNo.17)  Re[9]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ 774RR (228回)-(2015/02/28(Sat) 16:31:28)
Azulean 氏のコメントに追記するなら
Controls.Invoke で自作の「やりたいことをする関数」を呼んでいいってこと。

SerialPort1_DataReceived(...) {
    if (欲しいデータが一式そろった) {
        そろったデータを Byte 配列等に整形して;
        Invoke( 今回のデータで何か処理する関数, データ一式 );
    }
}
こうすると 「今回のデータで何か処理する関数」 は UI スレッド上で実行される。
UI スレッド上だから、任意の部品に任意の操作を何回加えても(性能的)問題ない。

今回のデータで何か処理する関数(データ一式) {
   // いつも UI スレッドで「操作」をするときと同じく
   ComboBox1.Text += 各言語に適合したフォーマットに加工した [データ一式];
   button1.Enabled = true;
}
とか書いていい。

みんな、こういう処理する時ってラムダ式とか「ワンライナー」を工夫しようとするんだけど
そんなことしなくてもいいよ、ってことだ。

引用返信 編集キー/
■75124 / inTopicNo.18)  Re[11]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ ハルハル (8回)-(2015/03/02(Mon) 10:19:48)
No75119 (Azulean さん) に返信
> ■No75118 (ハルハル さん) に返信
>>この他に、Enable の制御と ComboBoxのアイテムの追加とクリアもあることが判明しましたorz
>>テキストプロパティーの設定と取得はなんとなくわかったのですが、
>>Enableは一緒?だとしても、アイテムの追加とクリアはまた違う感じなんですよね?
>
> そもそも、DataReceived イベントの中から何度も Invoke するぐらいなら、イベント受信直後に Invoke して、その先ですべての処理を書くぐらいの意識でよさそうに感じます。
> その方法は検討されているのでしょうか?
引用返信 編集キー/
■75125 / inTopicNo.19)  Re[11]: スレッドセーフにて、ComboBoxの値取得方法
□投稿者/ ハルハル (9回)-(2015/03/02(Mon) 10:22:45)
2015/03/02(Mon) 10:24:20 編集(投稿者)

No75119 (Azulean さん) に返信
> ■No75118 (ハルハル さん) に返信
>>この他に、Enable の制御と ComboBoxのアイテムの追加とクリアもあることが判明しましたorz
>>テキストプロパティーの設定と取得はなんとなくわかったのですが、
>>Enableは一緒?だとしても、アイテムの追加とクリアはまた違う感じなんですよね?
>
> そもそも、DataReceived イベントの中から何度も Invoke するぐらいなら、イベント受信直後に Invoke して、その先ですべての処理を書くぐらいの意識でよさそうに感じます。
> その方法は検討されているのでしょうか?
回答ありがとうございます!
頭の片隅にも思い浮かびませんでした(^^;

確かにその方法で実現出来そうな雰囲気がしますので、検討してみたいと思います!

> 774RR さん
補足ありがとうございます!

引用返信 編集キー/
■75127 / inTopicNo.20)  Re[12]: スレッドセーフにて、ComboBoxの値取得方法
 
□投稿者/ shu (705回)-(2015/03/02(Mon) 14:37:44)
2015/03/02(Mon) 14:37:59 編集(投稿者)
No75125 (ハルハル さん) に返信

DataReceivedがなぜ別スレッドで動いているのかは考慮しておいた方がよいです。

まとめて書くなら
Do While ループ条件
    Invoke(AddressOf コントロール情報取得)
    受信処理
    Invoke(AddressOf コントロール設定処理)
    必要に応じてループを進める処理
Loop

※Invokeに正確に指定するものは省略
※ループになるかどうかは処理内容による。


のようにControlにアクセスする部分を中心としデータ受信部はInvokeに含めないように
した方がよいと思います。

引用返信 編集キー/

次の20件>
トピック内ページ移動 / << 0 | 1 >>

管理者用

- Child Tree -