validar form etiquetas ejemplo descargar crear campos .net winforms handles

.net - form - errorprovider c# textbox



"Error al crear el controlador de ventana" (7)

Estamos trabajando en una aplicación compuesta .NET WinForms muy grande, no en CAB, sino en un framework similar. Nos estamos ejecutando en un entorno Citrix y RDP que se ejecuta en Windows Server 2003.

Estamos empezando a tropezar con un error aleatorio y difícil de reproducir "Error al crear el identificador de ventana" que parece ser una falla de identificador antigua en nuestra aplicación. 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 sobre este error, pero no hay mucha orientación sólida sobre cómo evitar problemas en esta área.

¿La comunidad stackoverflow tiene alguna buena guía para crear aplicaciones winforms amigables para el usuario?


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.


Conocí esta excepción porque un bucle sin fin creaba un nuevo control de UI y establecía sus propiedades. Después de un bucle muchas veces, esta excption fue lanzada cuando el control de cambio de propiedad visible. Encontré que el objeto de usuario y el objeto GDI (del 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 interfaz de usuario.


Me enfrenté a esta excepción al agregar controles en el panel, porque en los controles secundarios del panel 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


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

Mi escenario: desde un cuadro de diálogo emergente que devolvió un DialogResult, el usuario haría 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 como resultado el problema: el problema con este código es que el subproceso se inicia inmediatamente y devuelve los resultados en el DialogResult que se devuelve que dispone el diálogo antes de que el subproceso pueda apropiadamente tomar 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 capturar 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. }


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

Aquí hay algunos consejos generales:

  • muchas veces, un control permanecerá en uso debido a que los eventos de control no se eliminan correctamente (el proveedor de información sobre herramientas nos causó problemas realmente grandes aquí) o los controles no están adecuadamente Dispuestos.
  • use bloques ''usando'' alrededor de todos los cuadros de diálogo modales para asegurarse de que estén Dispuestos
  • hay algunas propiedades de control que obligarán a la creación del identificador de ventana antes de que sea necesario (por ejemplo, establecer la propiedad ReadOnly de un control TextBox obligará al control a realizarse)
  • 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 Win (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.

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


Comprender este error

Empujando los límites de Windows: objetos USER y GDI - Parte 1 por Mark Russinovich: https://blogs.technet.microsoft.com/markrussinovich/2010/02/24/pushing-the-limits-of-windows-user-and -gdi-objects-part-1 /

Solución de problemas de este error

Debes poder reproducir el problema. Aquí hay una forma de registrar los pasos para hacer eso https://.com/a/30525957/495455 .

La forma más fácil de resolver qué está creando tantos controles es abrir TaskMgr.exe. En TaskMgr.exe debe tener visibles las columnas Objeto del USUARIO, Objeto GDI y Manipulaciones como se muestra, para hacer esto elija Ver Menú> Seleccionar columnas:

Siga los pasos para causar el problema y observe cómo 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.

A continuación, mantenga presionadas las teclas F10 o F11 para navegar por el código y observar cuando el recuento de objetos / asa aumenta dramáticamente.

La mejor herramienta que he encontrado hasta ahora es GDIView de NirSoft, que divide los campos de GDI Handle:

Lo rastreé hasta este código utilizado al configurar los anchos de columna de 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 rastro de la pila:

en System.Windows.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 width, Int32 height, Int32 clientWidth, Int32 clientHeight) en System.Windows.Forms.Control.UpdateBounds (Int32 x, Int32 y, Int32 width, Int32 height) en System.Windows.Forms.Control.SetBoundsCore (Int32 x, Int32 y, Int32 width, Int32 height , BoundsSpecified especificado) en System.Windows.Forms.ComboBox.SetBoundsCore (Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified) en System.Windows.Forms.Control.SetBounds (Int32 x, Int32 y, Int32 width, Altura Int32, BoundsSpecified especificado) en System.Windows.Forms.Control.set_Width (valor Int32)

Aquí está el quid de un útil artículo de Fabrice que me ayudó a resolver los límites:

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

Aparte del hecho de que la aplicación consume demasiados recursos, que es un problema aparte que ya estamos abordando, tuvimos dificultades para determinar qué recursos se estaban agotando y cuáles eran los límites para estos recursos. Primero pensamos en vigilar el contador Handles 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 vigilar son los objetos GDI y los objetos del usuario. Puede obtener una descripción general de las tres categorías de recursos en MSDN.

Objetos de usuario
Los problemas de creación de ventanas están directamente relacionados con los objetos del 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.

Heap de escritorio
Luego descubrimos qué límite era el verdadero culpable: era el "montón de escritorio". Por defecto, 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 de Desktop Heap. Esto incluye ventanas. Para obtener más información acerca de Desktop Heap, puede consultar los muy buenos artículos publicados en el NTDebugging MSDN blog:

¿Cuál es la verdadera solución? ¡Ser verde!
Aumentar el 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.

Así es como estamos haciendo esto en la aplicación de mi cliente:

Usamos TabControls y creamos el contenido de cada pestaña sobre la marcha, cuando se vuelve visible; Usamos 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 de MVP, que ayuda a hacer posible lo anterior porque separa los datos de las vistas; Usamos motores de diseño, los estándares 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 identificadores de ventanas ...). Lo anterior es solo una pista de lo que puede hacer si necesita construir pantallas ricas de Windows Forms. No hay duda de que puedes encontrar otros enfoques. Lo primero que debe hacer en mi opinión es construir sus aplicaciones en torno a 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?