どのように可能ですか?System.Windows.Forms.Form から派生した Windows Form コントロールがあり、このフォームに WebBrowser コントロールが含まれています。Webbrowser オブジェクトのインスタンスは、フォームのコンストラクター (InitializeComponent() メソッド内) で作成されます。次に、バックグラウンド スレッドで WebBrowser のコンテンツを操作すると、場合によっては Form.InvokeRequired == false であるのに対し、WebBrowser.InvokeRequired == true であることがわかりました。どうしてですか?
3 に答える
Form.InvokeRequired
false
フォームが表示される前に戻ります。
簡単なテストを行いました:
Form2 f2 = new Form2();
Thread t = new Thread(new ThreadStart(() => PrintInvokeRequired(f2)));
t.Start();
t.Join();
f2.Show();
t = new Thread(new ThreadStart(() => PrintInvokeRequired(f2)));
t.Start();
t.Join();
ヘルパーと
private void PrintInvokeRequired(Form form)
{
Console.WriteLine("IsHandleCreated: " + form.IsHandleCreated + ", InvokeRequired: " + form.InvokeRequired);
}
出力は
IsHandleCreated: False、InvokeRequired: False
IsHandleCreated: True、InvokeRequired: True
また、これはMSDNで多少文書化されていることにも注意してください。
コントロールのハンドルがまだ存在しない場合、InvokeRequired は、ウィンドウ ハンドルを持つコントロールまたはフォームが見つかるまで、コントロールの親チェーンを検索します。適切なハンドルが見つからない場合、InvokeRequired メソッドは false を返します。
つまり、InvokeRequired は、Invoke が必要でない (呼び出しが同じスレッドで発生する) 場合、またはコントロールが別のスレッドで作成されたが、コントロールのハンドルがまだ作成されていない場合に、false を返すことができます。
コントロールのハンドルがまだ作成されていない場合は、コントロールのプロパティ、メソッド、またはイベントを単純に呼び出すべきではありません。これにより、コントロールのハンドルがバックグラウンド スレッドで作成され、メッセージ ポンプのないスレッドでコントロールが分離され、アプリケーションが不安定になる可能性があります。
バックグラウンド スレッドで InvokeRequired が false を返したときに IsHandleCreated の値も確認することで、このケースを防ぐことができます。コントロール ハンドルがまだ作成されていない場合は、作成されるまで待ってから Invoke または BeginInvoke を呼び出す必要があります。通常、これは、フォームが表示される前、または Application.Run が呼び出される前に、(Application.Run(new MainForm()) のように) アプリケーションのプライマリ フォームのコンストラクターでバックグラウンド スレッドが作成された場合にのみ発生します。
あなたの解決策は、 もチェックすることですIsHandleCreated
。
編集:
WebBrowser コントロール
のHandle
内部または外部でいつでも作成できます。これは、親フォームのハンドルを自動的に作成しません。
私は例を作成しました:
public Form2()
{
InitializeComponent();
Button button1 = new Button();
this.Controls.Add(button1);
Console.WriteLine("button1: " + button1.IsHandleCreated + " this: " + this.IsHandleCreated);
var tmp = button1.Handle; // Forces the Handle to be created.
Console.WriteLine("button1: " + button1.IsHandleCreated + " this: " + this.IsHandleCreated);
}
出力で:
button1: False this: False
button1: True this: False
対応するより一般的な問題の詳細な調査は次のとおりです:http ://www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html
私はこれと同じ奇妙な行動を調査してきました。さまざまなスレッドからいくつかのコントロールを操作する必要があります(たとえば、ホストに接続されているデバイスに関する情報を表示したり、さまざまなデバイスの状態に応じてアクションをトリガーしたりします)。
このリンクは私に良いヒントを与えました:http: //csharpfeeds.com/post/2898/Control.Trifecta_InvokeRequired_IsHandleCreated_and_IsDisposed.aspx
私はまだMSの人々が自分のものをどのように利用しようとしていたのかわかりません(そして多くの面で同意しません)が、あるアプリケーションでは次の汚くて汚い回避策を作らなければなりませんでした:
- メインスレッドでコントロール/フォームを作成します(メインスレッドであることを確認してください)。
- 同じ手順で、コントロールハンドルを確認します。この簡単なチェックにより、適切なスレッドで作成されます。
どれほど醜いですね。他の誰かがそれを行うためのより良い方法を持っているかどうか知りたいです。
_my_control = new ControlClass( );
_my_control.Owner = this;
IntPtr hnd;
// Force Handle creation by reading it.
if ( !_my_control.IsHandleCreated || _my_control.Handle == IntPtr.Zero )
hnd = _my_control.Handle;
(このやや古い投稿に投稿して申し訳ありませんが、誰かに役立つと思いました)。