it-swarm-tr.com

"Pencere Kolu Oluşturma Hatası"

Çok büyük bir .NET WinForms kompozit uygulaması üzerinde çalışıyoruz - CAB değil, benzer bir evde yetiştirilen çerçeve. Windows Server 2003'te çalışan bir Citrix ve RDP ortamında çalışıyoruz. 

Bizim uygulamada eski bir moda tanıtıcı sızıntısı gibi görünen "Pencere tanıtıcısı oluşturulurken hata" hatasını çoğaltmak üzere rastgele ve yeniden üretilmeye başlıyoruz. 3. Parti kontrollerden (Janus GridEX, Infralution VirtualTree ve .NET Magic docking) yoğun bir şekilde faydalanıyoruz ve veritabanımızdaki meta verilere dayanarak çok fazla dinamik yükleme ve içerik oluşturma yapıyoruz.

Google’da bu hatayla ilgili pek çok bilgi var, ancak bu alandaki sorunlardan nasıl kaçınılacağınıza dair sağlam bir rehberlik yok.

Yığın dostu winforms uygulamaları oluşturmak için yığın akışı topluluğunun benim için iyi bir rehberliği var mı?

25
user8133

WinForms'ta beklendiği gibi boşaltma yapılmayan UI'lerle ilgili birçok sorunu izledim. 

İşte bazı genel ipuçları:

  • zaman zaman, kontrol olayları uygun şekilde kaldırılmadığından (araç ipucu sağlayıcısı burada gerçekten büyük sorunlara neden oldu) veya kontroller uygun şekilde atılmadığından kontrol kullanımda kalacaktır. 
  • i̇mha edildiklerinden emin olmak için tüm modal iletişim kutularının etrafında 'using' bloklarını kullanın
  • gerekli olmadan önce pencere tanıtıcısının oluşturulmasını zorlayacak bazı kontrol özellikleri vardır (örneğin bir TextBox kontrolünün ReadOnly özelliğini ayarlamak kontrolü gerçekleştirmeye zorlar)
  • oluşturulan sınıfların sayısını almak için .Net Memory profiler gibi bir araç kullanın. Bu aracın daha yeni sürümleri, GDI ve USER nesnelerini de izleyecektir.
  • win API çağrıları (veya diğer DllImport çağrıları) kullanımınızı en aza indirmeyi deneyin. Birlikte çalışma kullanmanız gerekiyorsa, bu çağrıları kullanım/Atma deseninin doğru çalışacağı şekilde sarmayı deneyin.
28
Jack Bolding

NativeWindow'u alt sınıfladığımda ve CreateHandler'i manuel olarak çağırdığımda bu hatayı aldım. Sorun WndProc benim geçersiz sürümünde base.WndProc (m) eklemek unuttum oldu. Aynı hataya neden oldu

6
aderesh

Bu istisna ile karşılaştım çünkü sonsuz döngü yeni UI kontrolü yarattı ve özelliklerini ayarladı. Birçok kez döngüden sonra, kontrol görünür özelliğini değiştirdiğinde bu dışlama atıldı. Hem Kullanıcı Nesnesini hem de GDI Nesne (Görev Yöneticisinden) oldukça büyük.

Sanırım sorununuz, sistem kaynaklarının bu UI denetimleri tarafından tüketilmesinin benzer bir nedeni.

5
sliu

Bu hatayı anlama

Windows Sınırlarını Zorlamak: USER ve GDI Nesneler - Mark Russinovich tarafından Bölüm 1: https://blogs.technet.Microsoft.com/markrussinovich/2010/02/24/ Windows-kullanıcı-ve-gdi-nesneler-part-1'in sınırlarını zorlayan/

Bu hatayı gidermek

Sorunu yeniden üretebilmeniz gerekir. İşte bunu yapmak için gereken adımları kaydetmenin bir yolu https://stackoverflow.com/a/30525957/495455 .

Bu kadar çok tanıtıcı oluşturanı bulmanın en kolay yolu TaskMgr.exe dosyasını açmaktır. TaskMgr.exe dosyasında USER Nesnesine sahip olmanız gerekir, GDI Nesne ve Kollar gösterildiği gibi görünür, bunu yapmak için Görünüm Menüsü> Sütun Seç:

 enter image description here

Soruna neden olma adımlarını izleyin ve USER Nesne sayısının yaklaşık 10.000'e yükseldiğini veya GDI Nesneler veya Kollar kendi sınırlarına ulaştığını izleyin.

Nesne veya Tutamaçların arttığını (genellikle çarpıcı şekilde) gördüğünüzde, Duraklat düğmesini tıklatarak kod yürütülmesini Visual Studio'da durdurabilirsiniz.

Ardından, Nesne/Tutamaç sayısı önemli ölçüde arttığında kod izleme sırasında gezinmek için F10 veya F11 tuşunu basılı tutun.

Şu ana kadar bulduğum en iyi araç, NirSoft'tan GDIView, GDI Sap alanlarını ayırıyor:

 enter image description here

DataGridViews Sütun Genişlikleri ayarlanırken kullanılan bu koda kadar izledim:

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

Bu yığın izlemesidir:

system.Windows.Forms.Control.CreateHandle () şirketinde System.Windows.Forms.ComboBox.CreateHandle () şirketinde System.Windows.Forms.Control.get_Handle () [.____ şirketinde at .] System.Windows.Forms.ComboBox.InvalidateEverything () at At System.Windows.Forms.ComboBox.OnResize (EventArgs e), At System.Windows.Forms.Control.OnSizeChanged (EventArgs e ) at System.Windows.Forms.Control.UpdateBounds (Int32 x, Int32 y, Int32 genişlik, Int32 yükseklik, Int32 istemci genişliği, Int32 istemci yüksekliği). System.Windows. Formlar.Control.UpdateBounds (Int32 x, Int32 y, Int32 Genişlik, Int32 yükseklik) Öğesinde System.Windows.Forms.Control.SetBoundsCore (Int32 x, Int32 y, Int32 genişlik, Int32 yükseklik, Belirtilen Belirtilen) System.Windows.Forms.ComboBox.SetBoundsCore (Int32 x, Int32 y, Int32 genişlik, Int32 yükseklik, Belirtilen Belirtilen) System.Windows.Forms.Control.SetBounds (Int32 x, Int32 y, Int32 genişliği, .] Int32 yüksekliği, Sınırlar Belirtildi) System.Windows.Forms.Control.set_Width (Int32 değeri)

İşte Fabrice'in yararlı bir makalesinin sınırlarını çözmeme yardım eden -

"Pencere tanıtıcısı oluşturulurken hata"
Bir müşteri için üzerinde çalıştığım büyük bir Windows Forms uygulaması aktif olarak kullanıldığında, kullanıcılar genellikle "Pencere tanıtıcısı oluşturulurken hata" istisnaları alıyor.

Uygulamanın çok fazla kaynak tüketmesinin yanı sıra, halihazırda ele aldığımız tamamen ayrı bir konudur, bu kaynakların sınırlarının yanı sıra hangi kaynakların tükenmekte olduğunu tespit etmekte zorlandık. İlk önce Windows Görev Yöneticisi'ndeki Handles sayacına göz kulak olmayı düşündük. Bunun nedeni, bazı işlemlerin normalde olduğundan daha fazla kaynak tüketme eğiliminde olduğunu fark etmemizdi. Ancak, bu sayaç iyi değil çünkü dosyalar, soketler, işlemler ve iş parçacıkları gibi kaynakları izler. Bu kaynaklar Çekirdek Nesneleri olarak adlandırılır.

Dikkat etmemiz gereken diğer kaynaklar, GDI Nesneleri ve Kullanıcı Nesneleridir. MSDN'deki üç kaynak kategorisine genel bir bakış alabilirsiniz.

Kullanıcı Nesneleri
Pencere oluşturma sorunları doğrudan Kullanıcı Nesneleri ile ilgilidir.

Bir uygulamanın kullanabileceği Kullanıcı Nesneleri sınırının ne olduğunu belirlemeye çalıştık. İşlem başına 10.000 kullanıcı işleme kotası var. Bu değer kayıt defterinde değiştirilebilir, ancak bu sınır bizim durumumuzdaki gerçek gösteri durdurucu değildi. Diğer sınır Windows oturumu başına 66.536 kullanıcı tutamağıdır. Bu sınır teoriktir. Uygulamada, ulaşılamadığını fark edeceksiniz. Bizim durumumuzda, şu anki oturumdaki toplam Kullanıcı Nesnesi sayısı 11.000'e ulaşmadan önce korkunç "Pencere tanıtıcısı yaratırken" istisnası aldık.

Masaüstü Yığını
Ardından hangi limitin gerçek suçlu olduğunu keşfettik: “Masaüstü Yığını” idi. Varsayılan olarak, etkileşimli bir kullanıcı oturumunun tüm grafiksel uygulamaları "masaüstü" olarak adlandırılan şeyde yürütülür. Böyle bir masaüstüne tahsis edilen kaynaklar sınırlıdır (ancak yapılandırılabilir).

Not: Kullanıcı Nesneleri, Masaüstü Yığınının bellek alanını tüketen şeydir. Buna pencereler de dahildir. Masaüstü Yığını hakkında daha fazla bilgi için NTDebugging MSDN blogunda yayınlanan çok iyi makalelere bakabilirsiniz:

Gerçek çözüm nedir? Yeşil ol!
Masaüstü Yığınını Artırmak etkili bir çözümdür, ancak nihai çözüm bu değildir. Asıl çözüm daha az kaynak tüketmektir (bizim durumumuzda daha az pencere kullanımı). Bu çözümle ne kadar hayal kırıklığına uğradığınızı tahmin edebilirim. Bulabildiğim her şey gerçekten bu mu? Eh, burada büyük bir sır yok. Çıkmanın tek yolu yalın olmak. Daha az karmaşık kullanıcı arayüzlerine sahip olmak iyi bir başlangıçtır. Kaynaklar için iyi, kullanılabilirlik için de iyi. Bir sonraki adım israftan kaçınmak, kaynakları korumak ve geri dönüştürmektir!

Müvekkilimin uygulamasında bunu nasıl yapıyoruz:

TabControls kullanıyoruz ve anında göründüğü zaman her sekmenin içeriğini oluşturuyoruz; Genişletilebilir/daraltılabilir bölgeler kullanıyor ve bunları yalnızca gerektiğinde kontroller ve verilerle dolduruyoruz; Kaynakları en kısa sürede serbest bırakın (Dispose yöntemini kullanarak). Bir bölge çöktügünde, onun çocuk kontrollerini temizlemek mümkündür. Gizlendiğinde sekme için de aynı; Yukarıdakileri mümkün kılmaya yardımcı olan MVP tasarım desenini kullanıyoruz; çünkü verileri görünümlerden ayırıyor; Düzenleme motorları, standart FlowLayoutPanel ve TableLayoutPanel iç içe paneller veya özel olanlar, iç içe panellerin derin hiyerarşileri oluşturmak yerine, GroupBox'lar ve Splitters (boş bir splitterin kendisi üç pencere tutamağı kullanır ...). Zengin Windows Forms ekranları oluşturmanız gerekir. Başka yaklaşımlar bulabileceğiniz konusunda hiçbir şüphe yok. Bence yapmanız gereken ilk şey, uygulamalarınızı kullanım senaryoları ve senaryolar etrafında oluşturmak. Bu, yalnızca belirli bir zamanda ve belirli bir kullanıcı için neyin gerekli olduğunu göstermeye yardımcı olur.

Tabii ki, başka bir çözüm kulplara dayanmayan bir sistem kullanmak olacaktır ... WPF kimse?

3
Jeremy Thompson

İşyerinde Janus Kontrollerini kullanıyorum. Kendilerini elden çıkardıkları sürece son derece adamışlardır. Doğru bir şekilde imha edildiklerinden emin olmanızı tavsiye ederim. Ayrıca, onlarla bağlanma bazen serbest kalmaz, bu nedenle kontrolü elden çıkarmak için nesneyi elle kaldırmanız gerekir.

3
MagicKat

Panele kontroller eklerken bu istisna ile karşılaştım, çünkü panelde çocuk kontrolleri temizlenmedi. Paneldeki çocuk kontrollerini elden çıkarırsanız hata giderilir.

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

winForm Uygulamamda iş parçacığı kullanmaya başladığımda da aynı hata oluştu, neyin atma hatası olduğunu bulmak için yığın izleme kullandım ve infragistics'in UltraDesktopAlert bileşeninin arkasında olduğunu öğrendim, böylece farklı bir şekilde başlattım ve hata gitti.

 this.Invoke((MethodInvoker)delegate
{
    //call your method here
});

tam kod böyle görünecek.

private void ultraButton1_Click(object sender, EventArgs e)
{
    Task.Factory.StartNew(() => myMethod1());
}

void myMethod1()
{
    //my logic

    this.Invoke((MethodInvoker)delegate
    {
        ultraDesktopAlert1.Show($"my message header", "my message");
    });

    //my logic
}

ayrıca, uygulamamın kaç işleyici oluşturduğunu bulmak için GDI yardımcı programını kullanamadım, ancak uygulamam (64bit) listede yoktu. başka bir çözüm ise HKEY’nin şu anki yerinde masaüstü öbek değerini SharedSection=1024,20480,768 olarak değiştirmekti.

Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems

ama benimki zaten aynı değerlere sahipti. sadece çağrı yöntemi delege benim için çalıştı. Umarım bu yardımcı oldu.

0
Abubakar Riaz

Aynı .Net çalışma zamanı hatasıyla karşılaştım ancak çözümüm farklıydı. 

Senaryom: Bir DialogResult döndüren açılan bir İletişim Kutusundan, kullanıcı bir e-posta mesajı göndermek için bir düğmeye basar. Bir iş parçacığı ekledim, böylece arka planda rapor oluştururken kullanıcı arayüzü kilitlenmedi. Bu senaryo olağandışı bir hata mesajı alıp sona erdi.

Sorunla sonuçlanan kod: Bu kodla ilgili sorun, iş parçacığının derhal başlaması ve geri döndürmesidir; alanlar.

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

Bu senaryonun düzeltmesi: Düzeltme, değerleri, ipliği oluşturan metoda geçirmeden önce bunları alıp saklamaktır. 

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