it-swarm-eu.dev

"Errore durante la creazione dell'handle della finestra"

Stiamo lavorando a un'applicazione composita .NET WinForms di grandi dimensioni, non CAB, ma un framework simile sviluppato internamente. Stiamo eseguendo un ambiente Citrix e RDP in esecuzione su Windows Server 2003. 

Stiamo iniziando a incappare in errori casuali e difficili da riprodurre "Errore nella creazione della finestra" che sembra essere una perdita di gestione obsoleta nella nostra applicazione. Stiamo facendo un uso intensivo dei controlli di terze parti (Janus GridEX, Infralution VirtualTree e .NET Magic docking) e eseguiamo un sacco di caricamento e rendering dinamico dei contenuti in base ai metadati nel nostro database.

Ci sono molte informazioni su Google su questo errore, ma non ci sono molte indicazioni su come evitare problemi in quest'area.

La community StackOverflow ha una buona guida per me per la creazione di app winforms compatibili con le maniglie?

25
user8133

Ho rintracciato un sacco di problemi con le interfacce utente che non si stanno scaricando come previsto in WinForms. 

Ecco alcuni suggerimenti generali:

  • un sacco di tempo, un controllo rimarrà in uso perché gli eventi di controllo non vengono rimossi correttamente (il provider del tooltip ci ha causato problemi molto grandi qui) o i controlli non sono stati smaltiti correttamente. 
  • usa "usa" i blocchi attorno a tutte le finestre di dialogo modali per assicurarti che siano eliminati
  • ci sono alcune proprietà di controllo che imporranno la creazione dell'handle della finestra prima che sia necessario (ad esempio, l'impostazione della proprietà ReadOnly di un controllo TextBox costringerà il controllo a essere realizzato)
  • usa uno strumento come . Net Memory profiler per ottenere i conteggi delle classi che vengono create. Le versioni più recenti di questo strumento tracceranno anche gli oggetti GDI e USER.
  • cercare di ridurre al minimo l'utilizzo delle chiamate API Win (o altre chiamate DllImport). Se hai bisogno di usare interop, prova a racchiudere queste chiamate in modo che il pattern using/Dispose funzioni correttamente.
28
Jack Bolding

Ho avuto questo errore quando ho sottoclassato NativeWindow e chiamato manualmente CreateHandler. Il problema è che mi sono dimenticato di aggiungere base.WndProc (m) nella mia versione sovrascritta di WndProc. Ha causato lo stesso errore

6
aderesh

Ho incontrato questa eccezione perché il ciclo infinito creava un nuovo controllo dell'interfaccia utente e ne impostava le proprietà. Dopo averlo ripetuto più volte, questa esction veniva generata quando control control proprietà visibile. Ho trovato sia User Object che GDI Object (da Task Manager) sono abbastanza grandi.

Immagino che il tuo problema sia un motivo simile per cui le risorse di sistema sono esaurite da quei controlli dell'interfaccia utente.

5
sliu

Capire questo errore

Spingere i limiti di Windows: USER e GDI Oggetti - Parte 1 di Mark Russinovich: https://blogs.technet.Microsoft.com/markrussinovich/2010/02/24/ spingendo-i-limiti-di-windows-utente-e-gdi-oggetti-parte-1/

Risolvendo questo errore

Devi essere in grado di riprodurre il problema. Ecco un modo per registrare i passaggi per farlo https://stackoverflow.com/a/30525957/495455 .

Il modo più semplice per capire cosa sta creando così tanti handle è avere TaskMgr.exe aperto. In TaskMgr.exe è necessario disporre delle colonne Oggetto UTENTE, GDI Oggetto e Gestisci come mostrato, per farlo selezionare Menu Visualizza> Seleziona Colonne:

 enter image description here

Passare attraverso i passaggi per causare il problema e guardare il conteggio degli oggetti USER aumentare a circa 10.000 o GDI Gli oggetti o le maniglie raggiungono i loro limiti.

Quando vedi aumentare l'oggetto o le maniglie (in genere in modo drammatico) puoi interrompere l'esecuzione del codice in Visual Studio facendo clic sul pulsante Sospendi.

Quindi tieni premuto F10 o F11 per navigare attraverso il codice guardando quando i conteggi Oggetto/Maniglia aumentano drasticamente.

Lo strumento migliore che ho trovato finora è GDIView di NirSoft, che interrompe i campi GDI Gestisci:

 enter image description here

L'ho rintracciato fino a questo codice utilizzato quando si imposta Larghezza colonne 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

Questa è la traccia dello stack:

a System.Windows.Forms.Control.CreateHandle () a System.Windows.Forms.ComboBox.CreateHandle () a System.Windows.Forms.Control.get_Handle () a System.Windows.Forms.ComboBox.InvalidateEverything () in System.Windows.Forms.ComboBox.OnResize (EventArgs e) in System.Windows.Forms.Control.OnSizeChanged (EventArgs e ) in System.Windows.Forms.Control.UpdateBounds (Int32 x, Int32 y, Int32 width, Int32 height, Int32 clientWidth, Int32 clientHeight) su System.Windows. Forms.Control.UpdateBounds (Int32 x, Int32 y, Int32 Larghezza, Int32 altezza) a System.Windows.Forms.Control.SetBoundsCore (Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specificato) in System.Windows.Forms.ComboBox.SetBoundsCore (Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specificato) in System.Windows.Forms.Control.SetBounds (Int32 x, Int32 y, Int32 larghezza, Altezza Int32, Specifici specificati) a System.Windows.Forms.Control.set_Width (valore Int32)

Ecco il punto cruciale di un utile articolo di Fabrice che mi ha aiutato a superare i limiti:

"Errore durante la creazione dell'handle della finestra"
Quando una grande applicazione Windows Form su cui sto lavorando per un client viene utilizzata attivamente, gli utenti ottengono spesso le eccezioni "Errore nella creazione di finestre".

A parte il fatto che l'applicazione consuma troppe risorse, che è una questione separata che stiamo già affrontando, abbiamo avuto difficoltà nel determinare quali risorse si stavano esaurendo e quali sono i limiti per queste risorse. Abbiamo inizialmente pensato di tenere d'occhio il contatore Handles nel Task Manager di Windows. Questo perché abbiamo notato che alcuni processi tendevano a consumare più di queste risorse di quanto normalmente dovrebbero. Tuttavia, questo contatore non è il buono perché tiene traccia delle risorse come file, socket, processi e thread. Queste risorse sono denominate oggetti kernel.

Gli altri tipi di risorse da tenere d'occhio sono GDI Oggetti e Oggetti utente. È possibile ottenere una panoramica delle tre categorie di risorse su MSDN.

Oggetti utente
I problemi di creazione della finestra sono direttamente correlati agli oggetti utente.

Abbiamo cercato di determinare qual è il limite in termini di oggetti utente che un'applicazione può utilizzare. Esiste una quota di 10.000 handle utente per processo. Questo valore può essere modificato nel Registro di sistema, tuttavia questo limite non è stato il vero ostacolo alla presentazione nel nostro caso. L'altro limite è 66.536 handle utente per sessione Windows. Questo limite è teorico. In pratica, noterai che non può essere raggiunto. Nel nostro caso, abbiamo ricevuto l'temuta eccezione "Errore durante la creazione dell'handle della finestra" prima che il numero totale di oggetti utente nella sessione corrente raggiungesse 11.000.

Heap desktop
Abbiamo quindi scoperto quale limite era il vero colpevole: era il "Desktop Heap". Per impostazione predefinita, tutte le applicazioni grafiche di una sessione utente interattiva vengono eseguite in quello che viene chiamato "desktop". Le risorse allocate a tale desktop sono limitate (ma configurabili).

Nota: gli oggetti utente sono ciò che consuma la maggior parte dello spazio di memoria dell'host del desktop. Questo include Windows. Per ulteriori informazioni su Desktop Heap, è possibile fare riferimento agli ottimi articoli pubblicati sul blog NTDebugging MSDN:

Qual è la vera soluzione? Sii verde!
Aumentare l'Heap desktop è una soluzione efficace, ma non è il massimo. La vera soluzione è consumare meno risorse (meno maniglie delle finestre nel nostro caso). Posso indovinare quanto tu possa essere deluso con questa soluzione. È davvero tutto ciò che posso inventare ?? Beh, non c'è un grande segreto qui. L'unica via d'uscita è essere magri. Avere interfacce utente meno complicate è un buon inizio. È buono per le risorse, è buono anche per l'usabilità. Il prossimo passo è evitare gli sprechi, preservare le risorse e riciclarle!

Ecco come lo stiamo facendo nell'applicazione del mio cliente:

Utilizziamo TabControls e creiamo il contenuto di ogni scheda al volo, quando diventa visibile; Utilizziamo regioni espandibili/comprimibili e di nuovo le riempiamo con controlli e dati solo quando necessario; Noi rilasciare le risorse il prima possibile (usando il metodo Dispose). Quando una regione è compresso, è possibile cancellare i suoi controlli figlio. Lo stesso per una scheda quando diventa nascosta; Usiamo il modello di progettazione MVP, che aiuta a rendere possibile quanto sopra perché separa i dati dalle viste; Utilizziamo i motori di layout, il FlowLayoutPanel standard e TableLayoutPanel, o personalizzati, invece di creare gerarchie profonde di pannelli nidificati, GroupBox e Splitter (uno splitter vuoto consuma tre handle di finestra ...). Quanto sopra sono solo suggerimenti su cosa puoi fare se è necessario creare schermate Windows Form avanzate. Non c'è dubbio che tu possa trovare altri approcci. La prima cosa che dovresti fare a mio avviso è costruire le tue applicazioni intorno a casi d'uso e scenari. Ciò aiuta a visualizzare solo ciò che è necessario in un dato momento e per un dato utente.

Certo, un'altra soluzione sarebbe quella di utilizzare un sistema che non si basa su maniglie ... WPF chiunque?

3
Jeremy Thompson

Sto usando Janus Controls al lavoro. Sono estremamente buggy per quanto riguarda l'eliminazione di se stessi. Ti raccomando di assicurarti che vengano smaltiti correttamente. Inoltre, il collegamento con essi a volte non viene rilasciato, quindi è necessario separare manualmente l'oggetto per disporre del controllo.

3
MagicKat

Ho affrontato questa eccezione mentre aggiungevo i controlli al pannello, perché nel pannello i controlli figlio non venivano cancellati. Se disponga i controlli figlio nel pannello, allora il problema è risolto.

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

Mi sono imbattuto nello stesso errore di runtime .Net ma la mia soluzione era diversa. 

My Scenario: Da una finestra di dialogo popup che ha restituito un DialogResult, l'utente farebbe clic su un pulsante per inviare un messaggio di posta elettronica. Ho aggiunto un thread in modo che l'interfaccia utente non si bloccasse durante la generazione del report in background. Questo scenario ha finito per ottenere quell'insolito messaggio di errore.

Il codice che ha provocato un problema: Il problema con questo codice è che il thread inizia immediatamente e restituisce il risultato del DialogResult che viene restituito che dispone la finestra di dialogo prima che il thread possa afferrare correttamente i valori dal campi.

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 correzione per questo scenario: La correzione è di afferrare e memorizzare i valori prima di passarli nel metodo che crea il thread. 

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.
}
0
GrayDwarf