until the studio sqlserver2008sp1 que puede management llamar kb968369 identificador haya hasta has esn debe created creado control cannot called been aplicar winforms multithreading

winforms - the - No se puede llamar a Invoke o BeginInvoke en un control hasta que se haya creado el identificador de ventana



sql server 2008 service pack 1 (8)

Tengo un método de extensión de control de SafeInvoke similar al que Greg D discute aquí (menos la comprobación IsHandleCreated).

Lo estoy llamando desde System.Windows.Forms.Form siguiente manera:

public void Show(string text) { label.SafeInvoke(()=>label.Text = text); this.Show(); this.Refresh(); }

A veces (esta llamada puede provenir de una variedad de hilos) esto da como resultado el siguiente error:

System.InvalidOperationException produjo

Message = "Invoke o BeginInvoke no pueden invocarse en un control hasta que se haya creado el identificador de ventana".

Source = "System.Windows.Forms"

StackTrace: at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous) at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args) at System.Windows.Forms.Control.Invoke(Delegate method) at DriverInterface2.UI.WinForms.Dialogs.FormExtensions.SafeInvoke[T](T control, Action`1 action) in C:/code/DriverInterface2/DriverInterface2.UI.WinForms/Dialogs/FormExtensions.cs:line 16

¿Qué está pasando y cómo lo soluciono? Lo sé tanto como que no es un problema de creación de formas, ya que a veces funcionará una vez y fallará la próxima vez, entonces, ¿cuál podría ser el problema?

PD. Realmente soy muy malo en WinForms, ¿alguien sabe una buena serie de artículos que explican todo el modelo y cómo trabajar con él?


Aquí está mi answer a una question similar:

Creo (todavía no del todo seguro) que esto se debe a que InvokeRequired siempre devolverá falso si el control aún no se ha cargado / mostrado. He hecho una solución alternativa que parece funcionar por el momento, que es hacer referencia simple al control del control asociado en su creador, así:

var x = this.Handle;

(Ver http://ikriv.com/en/prog/info/dotnet/MysteriousHang.html )


El método en la publicación que se vincula a las llamadas Invocar / Iniciar invocar antes de verificar si el control del mango se ha creado en el caso en que se lo está llamando desde un hilo que no creó el control.

Por lo tanto, obtendrá la excepción cuando se llame a su método desde un hilo que no sea el que creó el control. Esto puede ocurrir desde eventos remotos o elementos de usuario en cola de trabajo ...

EDITAR

Si marca InvokeRequired y HandleCreated antes de llamar a invocar, no debería obtener esa excepción.


Encontré que InvokeRequired no es confiable, así que simplemente uso

if (!this.IsHandleCreated) { this.CreateHandle(); }


Es posible que esté creando sus controles en el hilo equivocado. Considere la siguiente documentación de MSDN :

Esto significa que InvokeRequired puede devolver false si no se requiere Invoke (la llamada se produce en el mismo subproceso), o si el control se creó en un subproceso diferente pero el asa del control aún no se ha creado.

En el caso donde el mango del control aún no se haya creado, no debería simplemente llamar propiedades, métodos o eventos en el control. Esto podría hacer que el control se cree en el hilo de fondo, aislando el control en un hilo sin una bomba de mensajes y haciendo que la aplicación sea inestable.

Puede protegerse contra este caso también verificando el valor de IsHandleCreated cuando InvokeRequired devuelve falso en una cadena de fondo. Si aún no se ha creado la palanca de control, debe esperar hasta que se haya creado antes de llamar a Invoke o BeginInvoke. Normalmente, esto ocurre solo si se crea un hilo de fondo en el constructor del formulario primario para la aplicación (como en Application.Run (nuevo MainForm ()), antes de que se muestre el formulario o se haya invocado Application.Run.

Veamos qué significa esto para ti. (Esto sería más fácil de razonar si viéramos también su implementación de SafeInvoke)

Suponiendo que su implementación es idéntica a la referenciada, a excepción de la comprobación contra IsHandleCreated , sigamos la lógica:

public static void SafeInvoke(this Control uiElement, Action updater, bool forceSynchronous) { if (uiElement == null) { throw new ArgumentNullException("uiElement"); } if (uiElement.InvokeRequired) { if (forceSynchronous) { uiElement.Invoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); }); } else { uiElement.BeginInvoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); }); } } else { if (uiElement.IsDisposed) { throw new ObjectDisposedException("Control is already disposed."); } updater(); } }

Considere el caso en el que SafeInvoke a SafeInvoke desde el subproceso no gui para un control cuyo identificador no se ha creado.

uiElement no es nulo, entonces verificamos uiElement.InvokeRequired . Según los documentos de MSDN (en negrita) InvokeRequired devolverá false porque, aunque se haya creado en un subproceso diferente, ¡el descriptor no se ha creado! Esto nos envía a la condición else donde comprobamos IsDisposed o inmediatamente procedemos a llamar a la acción enviada ... desde el hilo de fondo .

En este punto, todas las apuestas están desactivadas en ese control porque su identificador ha sido creado en un hilo que no tiene una bomba de mensaje para él, como se menciona en el segundo párrafo. Quizás este es el caso que te encuentras?


Estaba experimentando el mismo error. Estaba llamando a la invocación desde el constructor de Form ().

Resolví este problema llamando a la invocación desde el evento Form_Load en su lugar.

No vi ninguna excepción después de hacer este cambio.


Haga referencia al controlador del control asociado en su creador, de esta forma:

Nota : Tenga cuidado con esta solución. Si un control tiene un mango, es mucho más lento hacer cosas como establecer el tamaño y la ubicación del mismo. Esto hace que InitializeComponent sea ​​mucho más lento. Una solución mejor es no hacer nada antes de que el control tenga un control.


Si va a usar un Control de otro subproceso antes de mostrar o hacer otras cosas con el Control , considere forzar la creación de su identificador dentro del constructor. Esto se hace usando la función CreateHandle .

En un proyecto de subprocesos múltiples, donde la lógica del "controlador" no está en un WinForm, esta función es instrumental en los constructores de Control para evitar este error.


Tuve este problema con este tipo de forma simple:

public partial class MyForm : Form { public MyForm() { Load += new EventHandler(Form1_Load); } private void Form1_Load(Object sender, EventArgs e) { InitializeComponent(); } internal void UpdateLabel(string s) { Invoke(new Action(() => { label1.Text = s; })); } }

Luego, para n otros hilos asincrónicos, estaba usando new MyForm().UpdateLabel(text) para intentar invocar el hilo de UI, pero el constructor no da ningún control a la instancia de hilo de UI, por lo que otros hilos obtienen otros identificadores de instancia, que son Object reference not set to an instance of an object o Invoke or BeginInvoke cannot be called on a control until the window handle has been created . Para resolver esto, utilicé un objeto estático para mantener el identificador de UI:

public partial class MyForm : Form { private static MyForm _mf; public MyForm() { Load += new EventHandler(Form1_Load); } private void Form1_Load(Object sender, EventArgs e) { InitializeComponent(); _mf = this; } internal void UpdateLabel(string s) { _mf.Invoke((MethodInvoker) delegate { _mf.label1.Text = s; }); } }

Supongo que está funcionando bien, hasta ahora ...