Laden...

DirectoryInfo.GetFiles() ruft StackOverflowException hervor

Erstellt von gabifi vor 13 Jahren Letzter Beitrag vor 13 Jahren 7.829 Views
G
gabifi Themenstarter:in
54 Beiträge seit 2007
vor 13 Jahren
DirectoryInfo.GetFiles() ruft StackOverflowException hervor

Hallo,

mit einem kleinen Programm möchte ich einen Ordnerinhalt überwachen (Anzahl der Dateien).

Bei dem Aufruf DirectoryInfo.GetFiles() erhalte ich ab und zu eine StackOverflowException. Weiß jemand wieso bzw. wie kann die Exception abfangen? Die Version anbei funktioniert nicht so ganz.

Die Methode Dateien() wird über einen Timer alle 5s aufgerufen (Windows Server 2003 R2 x64)


        private int Dateien()
        {
            try
            {
                DirectoryInfo di = new DirectoryInfo("C:\test");
                return = di.GetFiles();                
            }
            catch (StackOverflowException ex)
            {
                return 0;
            }
        }

Danke für eure Hilfe
Gabriel

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

Die Version anbei funktioniert nicht so ganz.

Das heißt konkret was? Bitte beachte auch [Hinweis] Wie poste ich richtig? Punkt 5

Lass mal beim return = das = weg.

Bei dem Aufruf DirectoryInfo.GetFiles() erhalte ich ab und zu eine StackOverflowException.

Wieviel Dateien sinds denn ungefähr? Kann das Problem reproduziert werden? Trace bei Auftreten des Fehlers (also im Ex-Handler) die Anzahl der Dateien (und den Stacktrace) mit.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

6.862 Beiträge seit 2003
vor 13 Jahren

Hallo,

so wie der Code dasteht kann der Fehler nicht auftreten... er kompiliert noch nicht mal. Poste erstmal den kompletten Code. Neben dem = beim return kannst du eh nicht das Ergebnis von GetFiles als int zurückgeben, weils nen Array von FileInfos ist.

Baka wa shinanakya naoranai.

Mein XING Profil.

5.742 Beiträge seit 2007
vor 13 Jahren

Hallo gabifi,

siehe auch [Snippet] Verzeichnisse und Dateien rekursiv durchlaufen. Das löst auf jeden Fall schon mal ein paar andere Probleme.

Ansonsten hilft natürlich immer der Debugger!

699 Beiträge seit 2007
vor 13 Jahren

Hallo,

vieleicht hilft dir die FileSystemWatcher-Klasse weiter.

Damit lässt sich sehr einfach ein Verzeichnis überwachen, und ggf dann änderungen in einem Dictionary verwalten etc...

Dazu ist auch kein Polling in einem Thread nötig, denn der FileSystemWatcher läuft selbst schon in einem Thread.

Grüße Stephan

H
116 Beiträge seit 2008
vor 13 Jahren

Die Methode Dateien() wird über einen Timer alle 5s aufgerufen

Vielleicht dauert das Einsammeln der Information länger als 5 Sekunden, so dass zu viel Speicher auf dem Stack verbraucht wird?

Hinrich

G
gabifi Themenstarter:in
54 Beiträge seit 2007
vor 13 Jahren

Hallo,

zuerst sorry für den falschen Code, hatte zum posten aud das wesentliche gekürzt und dabei das ein oder andere übersehen.

Der Ordner, in dem die Dateien bestimmt werden, enthält max. 3-4 Dateien und keine Unterordner. Er liegt auf der lokalen Festplatte, Lese- und Schreibrechte sind vorhanden. Das Auslesen der Dateien geht schneller als 5s, auch ein Hochsetzen des Timers hat keine Auswirkungen.


private int Dateien()
        {
            try
            {
                DirectoryInfo di = new DirectoryInfo("C:\\test");
                return di.GetFiles();
            }
            catch (StackOverflowException ex)
            {
                return 0;
            }
        }

Die StackOverflowExeption (in mscorlib.dll) tritt nur sporadisch auf. Ich setze mal die Variable DirectoryInfo di als Membervaribale und poste erneut, falls der Fehler wieder auftritt.
Soweit schon mal ein herliches Dankeschön
Gabriel

6.862 Beiträge seit 2003
vor 13 Jahren

Hallo,

GetFiles sollte eigentlich keine StackOverflow Exception werfen laut Doku.
Kannst du mal deinen kompletten Code posten? Der neue ist auch gekürzt und ist genauso wenig kompilierbar wie der erste. GetFiles liefert kein Integer zurück.

Baka wa shinanakya naoranai.

Mein XING Profil.

T
415 Beiträge seit 2007
vor 13 Jahren

Hmm, deine gepostete Methode soll ein int zurückgeben. Du im return gibst du aber ein GetFiles() zurück. Funktioniert die Methode nicht mit

return di.GetFiles().Length;

?

edit: ah, eben erst den Hinweis von talla gelesen

G
gabifi Themenstarter:in
54 Beiträge seit 2007
vor 13 Jahren

Hallo,

hier der wesentliche Code.

Kurz zur Erklärung: Das Programm überwacht mehrere Reihen von Simulationsläufen, welche über eine Batch-Datei gestartet werden.
Die Batchdatei enthält zudem Befehle, dass Sie am Anfang der Abarbeitung in einen defnierten Ordner eine Datei ablegt und nach der Abarbeitung diese löscht.

Das Programm hier überprüft anhand der Dateien, welche Simulationen laufen und startet neue Batch-Dateien, falls eine fertig ist.

Zudem simuliert es Benutzeringaben, um eventuelle Fehlermeldungen der Simulation (oder Aufforderungen zum Neustart) zu quittieren.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using System.Management;
using System.Runtime.InteropServices;



namespace SimWächter
{
    public partial class Form1 : Form
    {
        
        private DirectoryInfo di = null;

        [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
        static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
        static extern bool SetForegroundWindow(IntPtr hWnd);       
        
        public Form1()
        {
            InitializeComponent();
            AktualisiereSpeicherplatz();
            di = new DirectoryInfo(txt_OrdnerBelegt.Text);
        }

        private void StarteSimulation()
        {           
            System.Diagnostics.Process process = new System.Diagnostics.Process();
            process.StartInfo.FileName = ("C:\\Test\\batch.bat");
            System.Windows.Forms.Application.DoEvents();
            process.Start();
            return;
        }        

        private int LaufendeSimulationen()
        {
            try
            {                                 
                FileInfo[] imOrdner = di.GetFiles();
                int belegt = di.GetFiles().Length;
                return belegt;
            }
            catch (StackOverflowException ex)
            {
                return (int) Anz_Läufe_Max.Value;
            }
        }
        private void timer1_Tick(object sender, EventArgs e)
        {
            
            int belegt = LaufendeSimulationen();                               

            if (belegt < Anz_Läufe_Max.Value)
                StarteSimulation();                

            AktualisiereSpeicherplatz();                       

            Fehlerquittierung();
            UpdateQuittierung();
            return;
                
        }

        private void Fehlerquittierung()        
        {
            IntPtr wndHandle = FindWindowByCaption(IntPtr.Zero, "solver.exe - Fehler in Anwendung");
            if (wndHandle != IntPtr.Zero)
            {
                SetForegroundWindow(wndHandle);
                SendKeys.SendWait("{ENTER}");
            }

            IntPtr wndHandle2 = FindWindowByCaption(IntPtr.Zero, "solver.exe - Application Error");
            if (wndHandle2 != IntPtr.Zero)
            {
                SetForegroundWindow(wndHandle2);
                SendKeys.SendWait("{ENTER}");
            }
        }

        private void UpdateQuittierung()   
        {
            IntPtr wndHandle = FindWindowByCaption(IntPtr.Zero, "Automatic Updates");
            if (wndHandle != IntPtr.Zero)
            {
                SetForegroundWindow(wndHandle);
                SendKeys.SendWait("{TAB}");
                SendKeys.SendWait("{ENTER}");
            }           
        }

        private void AktualisiereSpeicherplatz()
        {
            DriveInfo[] Drives = DriveInfo.GetDrives(); // alle Laufwerke 
            double space = 0;
            foreach (DriveInfo d in Drives)
            {
                if (d.Name == "D:\\") // Größe von C (durch gewünschtes Laufwerk ersetzen) ausgeben
                    space = (double)d.TotalFreeSpace / (1024 * 1024 * 1024);
            }
            txt_FreierPlatz.Text = space.ToString("##0.0");
        }     
    }
}

Die StackOverflowException entsteht in der Zeile

FileInfo[] imOrdner = di.GetFiles();

.

Betriebssystem: Windows Server 2003 R2 x64

Danke für eure Hilfe
Gabriel

4.221 Beiträge seit 2005
vor 13 Jahren

Wieso ziehst Du Dir im LaufendeSimulationen die Liste der Files zweimal ?

ein simples return di.GetFiles().Length würde doch auch reichen.

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

G
gabifi Themenstarter:in
54 Beiträge seit 2007
vor 13 Jahren

stimmt, ist umsonst.
Den string[] benötige ich, da ich mir die Datein ausgeben lasse. Also jetzt:

FileInfo[] imOrdner = di.GetFiles();
return belegt = imOrdner.Length;
Gelöschter Account
vor 13 Jahren

Zeig mal den Callstack beim auftreten der Exception. Ich vermute, das das DoEvents da eine wesentliche Rolle spielt.

G
gabifi Themenstarter:in
54 Beiträge seit 2007
vor 13 Jahren

Hallo,

nachdem das System immer kaum reagiert, wenn der Fahler auftritt, erst mal nen Screenshot im Anhang.

Der Fehler tritt aber auch auf, wenn keine neue Simulation gestartet wurde, d.h. kein Aufruf von DoEvents.

Ich lass es mal laufen ohne DoEvents und schaue ob der Fahler trotzdem kommt.

6.862 Beiträge seit 2003
vor 13 Jahren

Do liegt denn das Directory wo du drauf zugreifst? Ist es nen ganz normaler Ordner im Filesystem oder ist es vielleicht ein Netzlaufwerk oder Soft/Hardlink im Dateisystem?

Baka wa shinanakya naoranai.

Mein XING Profil.

5.658 Beiträge seit 2006
vor 13 Jahren

Man sieht doch aus 10 Metern Entfernung, daß das Timer-Event mindestens 3x im CallStack liegt. Dann ist es wirklich kein Wunder, wenn es zu einer StackOverFlowException kommt.

Weeks of programming can save you hours of planning

656 Beiträge seit 2008
vor 13 Jahren

Mal ganz nebenbei: Heißt deine Form1 zufällig auch "Automatic Updates"?

4.939 Beiträge seit 2008
vor 13 Jahren

Und noch eine Kleinigkeit: die Schleife in "AktualisiereSpeicherplatz" ist auch ziemlich überflüssig, wenn du nur genau 1 Laufwerk abfragen willst.
Kürzer geht es mit:


DriveInfo d = new DriveInfo("D:");

space = d.TotalFreeSpace ...

G
gabifi Themenstarter:in
54 Beiträge seit 2007
vor 13 Jahren

Danke Th69 für die Anmerkung, ich werde es ändern. Habe es jetzt aber mal ganz rausgenommen, eventuell liegt ja darin das Problem.

Mein Programm bzw. die zugehörige Form heißt nicht "Automatic Updates".
Der Ordner liegt auf einer lokalen Festplatte.

Wie kann es denn kommen, dass das Timer-Event mehrere Male in der Aufrufliste liegt?
Kann es an der Methode UpdateQuittierung() liegen?

Ich werde mal nebenher die Abarbeitung im Auge haben.

U
1.688 Beiträge seit 2007
vor 13 Jahren

Wie kann es denn kommen, dass das Timer-Event mehrere Male in der Aufrufliste liegt?

Application.DoEvents könnte dafür verantwortlich sein. Wozu brauchst Du das überhaupt?

5.658 Beiträge seit 2006
vor 13 Jahren

Es sind ja bereits mehrere Vorschläge gemacht worden. Wäre wirklich sehr nett und auch zielführend, wenn du das eine oder andere mal ausprobiert hättest.

Weeks of programming can save you hours of planning

656 Beiträge seit 2008
vor 13 Jahren

Mein Programm bzw. die zugehörige Form heißt nicht "Automatic Updates".
[...]
Wie kann es denn kommen, dass das Timer-Event mehrere Male in der Aufrufliste liegt?
Kann es an der Methode UpdateQuittierung() liegen?

Darum meine Frage - eventuell findet dein P/Invoke call aus welchen Gründen auch immer dein aktuelles Formular. SendWait läuft in den Message Loop rein, wo wir grade schon drin sind, und der nächste Timer ausgelöst wird, und und und.

Schon mal versucht, den External Code anzuzeigen? Rechtsklick, Show External Code, dann siehst du vielleicht obs am DoEvents liegt oder nicht.

463 Beiträge seit 2009
vor 13 Jahren

Hat jetzt zwar nicht direkt etwas mit deinem Problem zu tun, aber es ist natürlich auch eine Lösung evtl. Fehlermeldungen automatisch wegzuklicken. Würde es nicht mehr Sinn machen - hier erst mal nach der Ursache der Fehler zu suchen?

Für mich ist dies ganz schlechter Programmierstil.

Stefan

G
gabifi Themenstarter:in
54 Beiträge seit 2007
vor 13 Jahren

Hallo Stefan,

ich verstehe nicht ganz was du mit schlechtem Programmierstil meinst. Die Fehler, die bestätigt werden, entstehen nicht in meinem C#-Programm, sondern im Rahmen einer komplexen Mehrkörpersimulation.
Natürlich sollten diese erst gar nicht auftreten, aber leider kommt es eben vereinzelt zu numerischen Problemen, sodass der Solver die Gleichungen nicht lösen kann. Dies kann ich mir dann ja anhand der Ergebnisdatein anschauen und untersuchen, wo eventuell der Fehler lag. Nur ist es ärgerlich, wenn ein solcher Ausnahmezustand die ganze Abarbeitungsreihe an Simulationen lahmlegt. Gerade nachts kann ich nicht von Hand den Fehler registrieren. Mit Programmieren haben die auftretenden Fehler aber nichts zu tun.

Nach Optimierung der Bestimmung des freien Festplattenplatzes trat keine weitere StackOverflowException auf. Da ich über meine alte Routine auch Netzlaufwerke eingezogen hatte ist es schon möglich, dass die Bestimmung hier lang gebraucht hat.
Soweit mal Danke für eure Hilfe, ich hoffe es lag wirklich daran.
Grüße
Gabriel

5.658 Beiträge seit 2006
vor 13 Jahren

Das Problem besteht nach wie vor und wird dir beizeiten wieder um die Ohren fliegen. Indem du die Festplatte optimiert, und den Zugriff dadurch beschleunigt hast, hast du nicht das Problem behoben! Und ja, es liegt an deinem Programmcode.

Weeks of programming can save you hours of planning