Non ho postato la traccia dello stack eccezione, ma mi aspetto che sembrava qualcosa di simile:
System.InvalidOperationException: Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on.
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Control.set_WindowText(String value)
at System.Windows.Forms.TextBoxBase.set_WindowText(String value)
at System.Windows.Forms.Control.set_Text(String value)
at System.Windows.Forms.TextBoxBase.set_Text(String value)
at System.Windows.Forms.TextBox.set_Text(String value)
at WindowsFormsApplicationcSharp2015.Form1.<.ctor>b__0_0() in D:\test\WindowsFormsApplicationcSharp2015\Form1.cs:line 27
possiamo vedere che l'eccezione viene generata dalla proprietà Control.Handle
getter. E infatti, se guardiamo la source code per la proprietà, non v'è, come ci si aspettava:
public IntPtr Handle {
get {
if (checkForIllegalCrossThreadCalls &&
!inCrossThreadSafeCall &&
InvokeRequired) {
throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall,
Name));
}
if (!IsHandleCreated)
{
CreateHandle();
}
return HandleInternal;
}
}
La parte interessante è quando guardiamo il codice che chiama Control.Handle
. In questo caso, questa è la proprietà Control.WindowText setter:
set {
if (value == null) value = "";
if (!WindowText.Equals(value)) {
if (IsHandleCreated) {
UnsafeNativeMethods.SetWindowText(new HandleRef(window, Handle), value);
}
else {
if (value.Length == 0) {
text = null;
}
else {
text = value;
}
}
}
}
Si noti che la proprietà Handle
viene richiamato solo se è IsHandleCreated
true
.
E per completezza, se guardiamo il codice per IsHandleCreated vediamo quanto segue:
public bool IsHandleCreated {
get { return window.Handle != IntPtr.Zero; }
}
Quindi, la ragione per cui non si ottiene l'eccezione, è perché per il momento le Task
eseguita, il handle della finestra non è stato ancora creato, il che è prevedibile dal momento che il Task
inizia nel costruttore del form, cioè prima che il modulo venga visualizzato.
Prima che venga creato l'handle della finestra, la modifica di una proprietà non richiede ancora alcun intervento dal thread dell'interfaccia utente. Quindi, durante questa piccola finestra temporale all'inizio del programma, sembrerebbe che sia possibile richiamare i metodi sulle istanze di controllo da un thread non dell'interfaccia utente senza ottenere l'eccezione "cross thread". Ma chiaramente, l'esistenza di questa piccola finestra temporale speciale non cambia il fatto che dovremmo sempre assicurarci di invocare metodi di controllo dal thread dell'interfaccia utente per essere sicuri.
Per dimostrare che il tempo della creazione della maniglia della finestra è il fattore determinante nell'ottenere (o meno) l'eccezione "cross thread", provare a modificare l'esempio per forzare la creazione dell'handle della finestra prima di iniziare l'attività , e notare come si intende ora ottenere costantemente l'eccezione prevista, anche in assenza di un sonno:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Force creation of window handle
var dummy = txtHello.Handle;
Task.Run(() =>
{
txtHello.Text = "Hello"; // kaboom
});
}
}
documentazione pertinente: Control.Handle
Se la maniglia non è ancora stato creato, fa riferimento a questa proprietà forzerà il gestire per essere creato.
Questa sarà una sorpresa anche per me se è vera! –