4

スレッドからフォームを閉じる必要があり、Close() メソッドを呼び出すためにフォームの Invoke メソッドを使用しています。

問題は、閉じるときにフォームが破棄され、「ウィンドウ ハンドルが作成されるまで、Invoke または BeginInvoke をコントロールで呼び出すことはできません。」というメッセージとともに InvalidOperationExecption が返されることです。

Close メソッドで「Step Into」を使用してデバッグする場合にのみこの例外が発生しましたが、通常の実行時にエラーが発生する可能性があるという危険を冒したくありません。

これは、それを再現するためのサンプル コードです。

 private void Form1_Load(object sender, EventArgs e)
 {
     Thread thread = new Thread(CloseForm);
     thread.Start();
 }

 private void CloseForm()
 {
     this.Invoke(new EventHandler(
         delegate
         {
             Close(); // Entering with a "Step Into" here it crashes.
         } 
     ));
 }

フォームは、フォームの自動生成コードに配置されます (これは変更したくありません)。

    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

誰かがこれまたは別のスレッドからフォームを閉じる別の方法の解決策を教えていただければ幸いです。

4

5 に答える 5

5

この方法を使用します:

// Inspired from: http://stackoverflow.com/a/12179408/1529139
public static void InvokeIfRequired(Control control, MethodInvoker action)
{
    if (control.IsDisposed)
    {
        return;
    }

    if (control.InvokeRequired)
    {
        try
        {
            control.Invoke(action);
        }
        catch (ObjectDisposedException) { }
        catch (InvalidOperationException e)
        {
            // Intercept only invokation errors (a bit tricky)
            if (!e.Message.Contains("Invoke"))
            {
                throw e;
            }
        }
    }
    else
    {
        action();
    }
}

使用例:

Functions.InvokeIfRequired(anyControl, (MethodInvoker)delegate()
{
    // UI stuffs
});
于 2013-10-31T14:47:14.103 に答える
1

この場合のこれまでの最善の解決策は、SynchronizationContext メカニズムを使用することでした。別のスレッドからフォーム コントロールを更新するには、Invoke または SynchronizationContext を使用する必要がありますか? にヒントがありました。.

サンプルコードは次のようになります。

private void Form1_Load(object sender, EventArgs e)
{
    Thread thread = new Thread(MethodThread);
    thread.Start(SynchronizationContext.Current);
}

private void MethodThread(Object syncronizationContext)
{
    ((SynchronizationContext)syncronizationContext).Send(CloseForm,null);
}

private void CloseForm(Object state)
{
    Close();
}
于 2012-04-11T19:30:48.283 に答える
0

私は今朝、Invoke 呼び出しで Close を呼び出し、Close メソッドが戻ろうとしたときに InvalidOperationException を取得していた、同様の状況に遭遇しました。Invoke メソッドは、破棄されているため、呼び出し元に値を返すことができません。この問題を解決するために、代わりに BeginInvoke を使用しました。これにより、フォームが閉じられる前にスレッドが戻ることができました。

于 2013-04-19T20:16:08.117 に答える
0

プラットフォームの相互運用なしでこれを行うためのクリーンな方法があるに違いないと感じていますが、それが何であるかはわかりません。それまでの間、p/invoke を気にしないと仮定して、確かに機能するアプローチを示すコードをいくつか示します...

public partial class Form1 : Form
{
    private const uint WM_CLOSE = 0x0010;
    private IntPtr _myHandle;

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        var t = new Thread(ThreadProc);
        t.Start();
    }

    protected override void OnHandleCreated(EventArgs e)
    {
        _myHandle = this.Handle;
        base.OnHandleCreated(e);
    }

    [return: MarshalAs(UnmanagedType.Bool)]
    [DllImport("user32.dll", SetLastError = true)]
    static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    private void ThreadProc(object o)
    {
        Thread.Sleep(5000);
        PostMessage(_myHandle, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
    }
}
于 2012-04-11T08:00:09.340 に答える
0

最も明白なコメントは、読み込みが完了する前にフォームを閉じる必要がある明らかな理由がないということです。理由が何であれ、他のより良い方法があります。

しかし、あなたが尋ねたので...

エラーはあなたに答えを与えます-それが構築されるまで閉じないでください。フォーム タイマーを設定する - 誰の WM_TIMER メッセージは、他のすべてのフォーム作成メッセージが処理されるまで処理されません。

private System.Windows.Forms.Timer _timer;
protected override void OnLoad(EventArgs args)
{
    _timer = new Timer { Interval = 1 };
    _timer.Tick += (s, e) => new Thread(CloseForm).Start();
    _timer.Start();

    base.OnLoad(args);
}
于 2012-04-10T07:30:25.960 に答える