2012-04-10 15 views
3

Ich muss ein Formular aus einem Thread schließen und ich verwende die Methode Invoke des Formulars zum Aufrufen der Close() -Methode.Schließen Sie ein Formular aus einem externen Thread mithilfe der Invoke-Methode

Das Problem ist, dass beim Schließen das Formular entsorgt wird und ich eine InvalidOperationException mit der Nachricht "Invoke oder BeginInvoke kann auf einem Steuerelement nicht aufgerufen werden, bis das Fensterhandle erstellt wurde.".

Ich habe diese Ausnahme nur beim Debuggen mit einem "Step Into" in der Close-Methode, aber ich möchte nicht mit einem möglichen Fehler beim normalen Ausführen riskieren.

Dies ist ein Beispiel-Code, es zu reproduzieren:

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. 
     } 
    )); 
} 

Das Formular wird in dem automatischen erzeugte Code für das Formular angeordnet sind (die ich nicht ändern möchte):

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

I Ich würde es begrüßen, wenn jemand mir eine Lösung für diese oder eine andere Möglichkeit geben könnte, ein Formular von einem anderen Thread zu schließen.

+0

Kein Repro Mit dem geposteten Code habe ich keinen erwartet. Der generelle Grund für diese Ausnahme besteht darin, dass Ihr Thread weiterhin Begin/Invoke * aufruft, nachdem * das Formular bereits geschlossen wurde. http: // Stapelüberlauf.com/questions/1731384/how-to-stop-backgroundwork-on-forms-closing-event/1732361 # 1732361 –

+0

Kann nicht reproduzieren Ausnahme mit Code, den Sie zur Verfügung gestellt –

+0

Es ist sehr einfach zu reproduzieren, nur mit dem "Step Into" Option und stellen Sie sicher, dass es in die Dispose-Methode eintritt (möglicherweise kann ein Breakpoint helfen), bevor Sie die Invoke-Methode verlassen. – Alfort

Antwort

0

Der offensichtlichste Kommentar ist - es gibt keinen ersichtlichen Grund, warum Sie ein Formular schließen müssen, bevor es überhaupt geladen wurde. Es gibt andere, bessere Möglichkeiten, mit dem umzugehen, was auch immer der Grund ist.

da Sie jedoch gefragt ...

Der Fehler gibt Ihnen die Antwort - nicht schließen, bis es gebaut wurde. Einrichten eines Formularzeitgebers - Wer die WM_TIMER-Nachricht erstellt, wird erst verarbeitet, wenn alle anderen Formularerstellungsmeldungen vorliegen.

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); 
} 
+0

Danke für Ihre Antwort. Vielleicht habe ich kein einfaches Beispiel gegeben. Mein Code ist natürlich anders. Ich glaube, dass das Load-Ereignis erstellt wird, nachdem das Handle des Formulars erstellt wurde, aber trotzdem ist der Fehler immer noch vorhanden und mit Ihrem Beispiel-Code wird die Ausnahme ebenfalls ausgelöst. – Alfort

0

Während ich fühle, dass es eine saubere Möglichkeit geben muss, dies ohne Plattform Interop zu tun, kann ich nicht denken, was es ist. In der Zwischenzeit, hier ist ein Code, einen Ansatz zeigt, die sicherlich funktioniert, Sie die p nicht unter der Annahme, Geist/aufrufen ...

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); 
    } 
} 
+0

Danke, ich würde diese Lösung im letzten Fall nehmen ... – Alfort

1

Bisher ist die beste Lösung für diesen Fall hat die SynchronizationContext Mechanismus zu verwenden. Ich hatte den Tipp in Should I use Invoke or SynchronizationContext to update form controls from another thread?.

Der Code Beispiel würde wie folgt sein:

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(); 
} 
0

Ich lief in einer ähnlichen Situation heute Morgen, wo ich in einem Invoke Anruf und bekommen die InvalidOperationException Schließen Aufruf, wenn die Close-Methode zurückzukehren versucht. Die Invoke-Methode ist nicht in der Lage, einen Wert an den Aufrufer zurückzugeben, da dieser entfernt wurde. Um dieses Problem zu beheben, habe ich stattdessen BeginInvoke verwendet, was es meinem Thread ermöglicht hat, zurückzukehren, bevor das Formular geschlossen wurde.

3

Von jedem anderen Thread können Sie diese ausführen (von .NET 2.0):

myform.Invoke((MethodInvoker)delegate() { myform.Close(); }); 

Hinweis: Wenn Sie Fehler haben Invoke or BeginInvoke cannot be called on a control until the window handle has been created Sie haben wie dies zu tun:

if (InvokeRequired) 
{ 
    Invoke((MethodInvoker)delegate() 
    { 
     Close(); 
    }); 
} 
else 
{ 
    Close(); 
}