4

次のコードでは、「クロス スレッド操作」の例外が発生します。「form2.ResumeLayout(false)」という理由だけで。そして、このステートメントがコメント化されている場合、ブラウザーがフォームで表示されません。ResumeLayout(false) の必要性はわかっていますが、解決策はありますか?

namespace WindowsFormsApplication1
{
public partial class Form1:  Form
{
    private System.ComponentModel.IContainer components = null;
    protected override void Dispose(bool disposing)
    { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); }
    private System.Windows.Forms.Button button1;

    public Form1()
    {
        this.button1 = new System.Windows.Forms.Button();
        this.SuspendLayout();
        this.button1.Location = new System.Drawing.Point(64, 47);
        this.button1.Text = this.button1.Name = "button1";
        this.button1.Size = new System.Drawing.Size(75, 23);
        this.button1.Click += new System.EventHandler(this.button1_Click);
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.ClientSize = new System.Drawing.Size(284, 262);
        this.Controls.Add(this.button1);
        this.Text = this.Name = "Form1";
        this.ResumeLayout(false);
    }
    private void button1_Click(object sender, EventArgs e)
    {
        Class1 clss = new Class1();
        clss.startme();
    }
}

class Class1
{
    public void startme()
    {
        Thread thread = new Thread(new ParameterizedThreadStart(Run));
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start(null);
    }
    private void Run(object j)
    {
        WebBrowser webBrowser1 = new WebBrowser();
        webBrowser1.Dock = DockStyle.Fill;
        webBrowser1.Navigate("https://dshift.sharepoint.com");

        Form form2 = new Form();
        form2.SuspendLayout();
        form2.Controls.Add(webBrowser1);
        form2.ResumeLayout(false);
        Application.OpenForms["Form1"].Invoke(new MethodInvoker(delegate
        {
            form2.ShowDialog(Application.OpenForms["Form1"]);
        }));
    }
}
}
4

1 に答える 1

5

WebBrowser.Navigate()呼び出しが問題です。これにより、コントロールのネイティブウィンドウハンドルが強制的に作成され、ワー​​カースレッドで発生します。しばらくして、ShowDialog()呼び出しを使用して、フォームのネイティブウィンドウを強制的に作成します。しかし、それは別のスレッド、Invoke()呼び出しのおかげでメインUIスレッドで発生します。

不一致があり、フォームのウィンドウはメインスレッドによって所有されていますが、ブラウザーのウィンドウはワーカースレッドによって所有されています。Winformsは、これが違法であることを通知するためにステップインします。子ウィンドウは、コンテナウィンドウと同じスレッドによって所有されている必要があります。回避策は、Navigate呼び出しをanonymousメソッド内に移動することです。

Invoke()を呼び出さずにダイアログを表示しようとすると、IllegalOperationExceptionも発生したため、このコードにたどり着いた可能性があります。実際にワーカースレッドでダイアログを実行したい場合は、これが通常のことです。Winformsは、ウィンドウの所有者が別のスレッドのウィンドウになることを好まないため、例外を発生させます。これは実際にはWindowsで合法ですが、Winformsはチェックを失敗させます。

SetParent()をピンボークすることで、これを回避できます。そして、この非常に特殊なケースでは、一時的にControl.CheckForIllegalCrossThreadCallsをfalseに設定して、他の状況では決してこれを行わないでください。一時的に強調します。フォームが実際にメインスレッドのウィンドウにモーダルであり、ダイアログが消える前に再度有効になるようにするには、追加の作業が必要です。

var owner = Application.OpenForms["Form1"];
form2.Load += delegate {
    // NOTE: just as a workaround for the Owner bug!!
    Control.CheckForIllegalCrossThreadCalls = false;
    form2.Owner = owner;
    Control.CheckForIllegalCrossThreadCalls = true;
    owner.BeginInvoke(new Action(() => owner.Enabled = false));

};
form2.FormClosing += new FormClosingEventHandler((s, ea) => {
    if (!ea.Cancel) {
        owner.Invoke(new Action(() => owner.Enabled = true));
        form2.Owner = null;
    }
});
form2.ShowDialog();
于 2012-05-26T16:27:56.907 に答える