2008-09-18 12 views
20

Estamos trabajando en una aplicación compuesta .NET WinForms muy grande, no en CAB, sino en un entorno similar. Nos estamos ejecutando en un entorno Citrix y RDP que se ejecuta en Windows Server 2003."Error al crear el controlador de ventana"

Estamos empezando a tropezar con un error aleatorio y difícil de reproducir "Error al crear el identificador de ventana" que parece ser un antiguo identificador de fuga en nuestro solicitud. Estamos haciendo un uso intensivo de los controles de terceros (Janus GridEX, Infralution VirtualTree y .NET Magic docking) y realizamos una gran cantidad de carga dinámica y representación de contenido basada en metadatos en nuestra base de datos.

Hay mucha información en Google acerca de este error, pero no hay mucha orientación sólida acerca de cómo evitar problemas en esta área.

¿La comunidad stackoverflow tiene alguna buena guía para la construcción de aplicaciones winforms amigables?

+0

Vea también [este artículo mío sobre "Error al crear el identificador de ventana"] [1] y cómo se relaciona con los Objetos USUARIO y el Heap de Escritorio. Proporciono algunas soluciones. [1]: http://weblogs.asp.net/fmarguerie/archive/2009/08/07/cannot-create-window-handle-desktop-heap.aspx – Fabrice

Respuesta

26

He rastreado un montón de problemas con las IU que no se descargan como se esperaba en WinForms.

Éstos son algunos consejos generales:

  • mucho del tiempo, un control permanecerá en su uso debido a eventos controles no se eliminan adecuadamente (el proveedor de información sobre herramientas nos causó problemas muy grandes aquí) o los controles no son Dispuesto correctamente.
  • uso 'utilizar' bloques alrededor de todos los cuadros de diálogo modales para asegurarse de que están dispuestos
  • hay algunas propiedades de control que obligará a la creación de la manija de la ventana antes de que sea necesario (para establecer la propiedad ReadOnly de un control de cuadro de texto ejemplo forzará el control para que se realice)
  • utilice una herramienta como .Net Memory profiler para obtener recuentos de las clases que se crean. Las versiones más recientes de esta herramienta también rastrearán los objetos GDI y USER.
  • intente minimizar el uso de las llamadas a la API de Windows (u otras llamadas a DllImport). Si necesita usar la interoperabilidad, intente ajustar estas llamadas de tal manera que el patrón usar/Eliminar funcione correctamente.
+0

¡No sabía sobre el truco del readonly! ¡Estupendo! +1! – giammin

3

Estoy usando los controles de Janus en el trabajo. Son extremadamente problemáticos en cuanto a deshacerse de ellos mismos. Le recomendaría que se asegure de que se eliminen correctamente. Además, el enlace con ellos a veces no se libera, por lo que debe desvincular manualmente el objeto para deshacerse del control.

5

Me encontré con esta excepción porque el bucle sin fin creaba un nuevo control de IU y establecía sus propiedades. Después de un bucle muchas veces, esta excption fue lanzada cuando la propiedad visible de control de cambios. Encontré que el objeto de usuario y el objeto GDI (desde el administrador de tareas) son bastante grandes.

Supongo que su problema es una razón similar por la que los recursos del sistema son agotados por esos controles de la interfaz de usuario.

1

Me enfrenté a esta excepción al agregar controles en el panel, porque en el panel controles secundarios no se borraron. Si descarta los controles secundarios en el panel, se corrige el error.

For k = 1 To Panel.Controls.Count 
    Panel.Controls.Item(0).Dispose() 
Next 
0

Me encontré con el mismo error de tiempo de ejecución .Net, pero mi solución fue diferente.

Mi Escenario: Desde una ventana emergente de diálogo que devuelve un DialogResult, el usuario haga clic en un botón para enviar un mensaje de correo electrónico. Agregué un hilo para que la IU no se bloqueara mientras generaba el informe en segundo plano. Este escenario terminó recibiendo ese mensaje de error inusual.

El código que dio lugar a un problema: El problema con este código es que el hilo se inicia inmediatamente y regresa lo que se traduce en el DialogResult siendo devuelto que dispone el cuadro de diálogo antes de que el hilo puede agarrar correctamente los valores de los campos.

private void Dialog_SendEmailSummary_Button_Click(object sender, EventArgs e) 
{ 
    SendSummaryEmail(); 
    DialogResult = DialogResult.OK; 
} 

private void SendSummaryEmail() 
{ 
    var t = new Thread(() => SendSummaryThread(Textbox_Subject.Text, Textbox_Body.Text, Checkbox_IncludeDetails.Checked)); 
    t.Start(); 
} 

private void SendSummaryThread(string subject, string comment, bool includeTestNames) 
{ 
    // ... Create and send the email. 
} 

La solución para este escenario: La solución es tomar y almacenar los valores antes de pasarlos al método que crea el hilo.

private void Dialog_SendEmailSummary_Button_Click(object sender, EventArgs e) 
{ 
    SendSummaryEmail(Textbox_Subject.Text, Textbox_Body.Text, Checkbox_IncludeDetails.Checked); 
    DialogResult = DialogResult.OK; 
} 

private void SendSummaryEmail(string subject, string comment, bool includeTestNames) 
{ 
    var t = new Thread(() => SendSummaryThread(subject, comment, includeTestNames)); 
    t.Start(); 
} 

private void SendSummaryThread(string subject, string comment, bool includeTestNames) 
{ 
    // ... Create and send the email. 
} 
1

La comprensión de este error

Superación de los límites de las ventanas: de usuario y objetos GDI - Parte 1 de Mark Russinovich: https://blogs.technet.microsoft.com/markrussinovich/2010/02/24/pushing-the-limits-of-windows-user-and-gdi-objects-part-1/

solucionar este error

Necesita ser capaz de reproduce el problema Aquí hay una forma de registrar los pasos para hacer eso https://stackoverflow.com/a/30525957/495455.

La manera más fácil de resolver qué está creando tantos controles es tener TaskMgr.exe abierto. En Taskmgr.exe es necesario tener el objeto de usuario, GDI y objetos Maneja columnas visibles, como se muestra, para ello seleccione el menú Vista> Seleccionar columnas:

enter image description here

ir a través de los pasos para la causa del problema y el reloj el recuento de objetos del USUARIO aumenta a alrededor de 10.000 o los objetos o asas GDI alcanzan sus límites.

Cuando vea que el Objeto o los Controles aumentan (típicamente de forma espectacular) puede detener la ejecución del código en Visual Studio haciendo clic en el botón Pausa.

Luego, mantenga presionada la tecla F10 o F11 para recorrer el código y ver cuando el recuento de objetos/asa aumenta dramáticamente.

La mejor herramienta que he encontrado hasta ahora es GDIView de NirSoft, se rompe el IDG Handle campos:

enter image description here

que lo rastreó a este código utilizado al establecer anchos de columna DataGridViews:

If Me.Controls.ContainsKey(comboName) Then 
    cbo = CType(Me.Controls(comboName), ComboBox) 
    With cbo 
     .Location = New System.Drawing.Point(cumulativeWidth, 0) 
     .Width = Me.Columns(i).Width 
    End With 
    'Explicitly cleaning up fixed the issue of releasing USER objects. 
    cbo.Dispose() 
    cbo = Nothing 
End If 

Este es el seguimiento de la pila:

en System.W indows.Forms.Control.CreateHandle() en System.Windows.Forms.ComboBox.CreateHandle() en System.Windows.Forms.Control.get_Handle() en System.Windows.Forms.ComboBox.InvalidateEverything() en System.Windows.Forms.ComboBox.OnResize (EventArgs e) en System.Windows.Forms.Control.OnSizeChanged (EventArgs e) en System.Windows.Forms.Control.UpdateBounds (Int32 x, Int32 y, Int32 ancho, altura Int32, Int32 clientWidth, Int32 clientHeight) en System.Windows.Forms.Control.UpdateBounds (Int32 x , Int32 y, Int32 anchura, altura Int32) en System.Windows.Forms.Control.SetBoundsCore (Int32 x, Int32 y, Int32 anchura, altura Int32, BoundsSpecified especificado) en System.Windows.Forms.ComboBox.SetBoundsCore (Int32 x, Int32 y, Int32 ancho, altura Int32, BoundsSpecified especificado) en System.Windows.Forms.Control.SetBounds (ancho Int32 x, Int32 y, Int32, alto Int32, BoundsSpecified especificado) en System.Windows .Forms.Control.set_Width (Int32 val ue)

Aquí es el quid de a helpful article by Fabrice que me ayudó a trabajar en los límites:

"identificador de ventana Error creando"
Cuando una gran aplicación de Windows Forms que estoy trabajando para un cliente se usa activamente, los usuarios a menudo obtienen excepciones de "Error al crear el identificador de ventana".

Aparte del hecho de que la aplicación consume demasiados recursos, lo cual es un tema aparte por completo que ya estamos abordando, hemos tenido dificultades con la determinación de qué recursos se estaban agotados, así como cuáles son los límites de estos recursos. Primero pensamos en vigilar el contador de Controles en el Administrador de tareas de Windows. Eso fue porque notamos que algunos procesos tendían a consumir más de estos recursos de lo que normalmente deberían. Sin embargo, este contador no es el correcto porque hace un seguimiento de recursos como archivos, sockets, procesos e hilos. Estos recursos se denominan objetos Kernel.

Los otros tipos de recursos que debemos tener en cuenta son los objetos GDI y los objetos del usuario. Puede obtener una descripción general de las tres categorías de recursos en MSDN.

usuario Objetos
problemas de creación de la ventana están directamente relacionados con Objetos de usuario.

Intentamos determinar cuál es el límite en términos de objetos de usuario que una aplicación puede usar. Hay una cuota de 10.000 identificadores de usuario por proceso. Este valor se puede cambiar en el registro, sin embargo, este límite no fue el verdadero obstáculo en nuestro caso. El otro límite es 66.536 identificadores de usuario por sesión de Windows. Este límite es teórico. En la práctica, notará que no se puede alcanzar. En nuestro caso, recibíamos la temida excepción "Error al crear el identificador de ventana" antes de que la cantidad total de Objetos de usuario en la sesión actual alcanzara los 11,000.

montón de escritorio
Entonces descubrimos qué límite era el verdadero culpable: era el "montón de escritorio". De forma predeterminada, todas las aplicaciones gráficas de una sesión de usuario interactiva se ejecutan en lo que se denomina "escritorio". Los recursos asignados a dicho escritorio son limitados (pero configurables).

Nota: Los objetos de usuario son los que consumen la mayor parte del espacio de memoria del montón de escritorio. Esto incluye ventanas. Para obtener más información acerca del Desktop Heap, puede consultar los muy buenos artículos publicados en el blog NTDebugging MSDN:

¿Cuál es la verdadera solución? ¡Ser verde!
Incrementar Desktop Heap es una solución efectiva, pero no es la mejor. La solución real es consumir menos recursos (menos controladores de ventanas en nuestro caso). Puedo adivinar lo decepcionado que puede estar con esta solución. ¿Es esto realmente todo lo que puedo pensar? Bueno, no hay un gran secreto aquí. La única salida es ser delgado. Tener UI menos complicadas es un buen comienzo. Es bueno para los recursos, también es bueno para la usabilidad. El siguiente paso es evitar el desperdicio, preservar los recursos y reciclarlos.

Aquí es cómo lo estamos haciendo esto en la solicitud de mi cliente:

Utilizamos TabControls y creamos el contenido de cada pestaña sobre la marcha, cuando se hace visible; Utilizamos regiones expandibles/plegables, y nuevamente las llenamos con controles y datos solo cuando es necesario; Lanzamos recursos lo antes posible (utilizando el método Dispose). Cuando una región se colapsa, es posible borrar sus controles secundarios. Lo mismo para una pestaña cuando se oculta; Usamos el patrón de diseño MVP, que ayuda a hacer posible lo anterior porque separa los datos de las vistas; Usamos motores de diseño, los estándar FlowLayoutPanel y TableLayoutPanel, o personalizados, en lugar de crear jerarquías profundas de paneles anidados, GroupBoxes y Splitters (un divisor vacío en sí mismo consume tres controladores de ventana ...). Lo anterior son solo sugerencias de lo que puede hacer si necesita crear pantallas ricas de Windows Forms. No hay duda de que puedes encontrar otros enfoques. Lo primero que debe hacer en mi opinión es crear sus aplicaciones en casos de uso y escenarios. Esto ayuda a mostrar solo lo que se necesita en un momento determinado y para un usuario determinado.

Por supuesto, otra solución sería usar un sistema que no dependa de controladores ... ¿WPF a alguien?

3

Tuve este error cuando subclasé NativeWindow y llamé CreateHandler manualmente. El problema fue que olvidé agregar base.WndProc (m) en mi versión modificada de WndProc. Causa el mismo error

+0

Tuve un ataque similar de olvido y esta solución me recordó volver y comprobar. Problema resuelto. Gracias. – Yonabart

Cuestiones relacionadas