Benutzer mit den meisten Antworten
Filesystemwatcher - Datei ist bereits in Zugriff durch anderen Prozess - Exception

Frage
-
Hallo zusammen,
ich steh vor folgendem Problem: Mein Programm läuft von der Syntax her soweit ohne Probleme, sofern man nur ein bzw. wenige Dateien in den zu überwachenden Ordner ablegt. Sobald dann aber eine größere Anzahl von Dateien verschoben werden und es zu bearbeiten gilt, steigt das Programm an verschiedenen Stellen aus mit der Begründung, dass ein anderer Prozess die Datei verwendet. Ich nehme stark an, dass der Filesystemwatcher sich da selbst irgendwo in die Quere kommt. Wenn man das Programm debuggt und in Einzelschritten testet, kommt der Fehler nie auf, mit Sleeps möchte ich aber ungern arbeiten. Streamreader und so sollten eigentlich alle auch geclosed sein, sofern ich einen aufgemacht hab. Eine einfache Möglichkeit alle momentanen Zugriffe abzubrechen gibt es nicht, oder?
Die Fehler tauchen immer auf, wo ich try and catch momentan gesetzt habe, also am Anfang von workonfile() und in startCreate().
Anbei der etwas gekürzte Code, vielleicht könnt Ihr mir da ja etwas auf die Sprünge helfen:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Linq; using System.Text; using System.IO; namespace TypeSort { class Program { string source, ziel1, ziel2, ziel3, archiv, logfile, logdir; string filecontent, filetype, destination, filepath, writethis, filename, date, logentry, matParam, tempFile; FileSystemWatcher fsw001, fsw002; string[] content, content_folgejob, content_hauptjob, existingFiles; StreamReader reader; StreamWriter writer; string job, material; DateTime dt; int tempState; static void Main(string[] args) { Program run = new Program(); run.process(); } /// <summary> /// Konstruktor /// Zwei Filewatcher, je für die 001 und 002 Files /// </summary> public void process() { tempFile = matParam = logentry = date = job = material = filecontent = filetype = destination = filepath = writethis = filename = null; tempState = 0; content = new string[54]; content_folgejob = new string[54]; content_hauptjob = new string[54]; fsw001 = new FileSystemWatcher(); fsw002 = new FileSystemWatcher(); source = @"C:\source\"; ziel1= @"C:\ziel1\"; ziel2= @"C:\ziel2\"; ziel3= @"C:\ziel3\"; archiv = @"C:\archiv\"; logfile = @"C:\log\log.txt"; logdir = @"C:\log\"; fsw001.Path = source; fsw002.Path = source; fsw001.Filter = "*.001"; fsw002.Filter = "*.002"; fsw001.NotifyFilter = NotifyFilters.FileName | NotifyFilters.CreationTime; fsw002.NotifyFilter = NotifyFilters.FileName | NotifyFilters.CreationTime; fsw001.Created += new FileSystemEventHandler(fsw_Created001); fsw002.Created += new FileSystemEventHandler(fsw_Created002); existingFiles = Directory.GetFiles(source, "*.001"); foreach (string file in existingFiles) { filetype = "001"; tempFile = Path.GetFileName(file); workonfile(tempFile, filetype); } existingFiles = Directory.GetFiles(source, "*.002"); foreach (string file in existingFiles) { filetype = "002"; tempFile = Path.GetFileName(file); workonfile(tempFile, filetype); } fsw001.EnableRaisingEvents = true; fsw002.EnableRaisingEvents = true; Console.Title = "Sortierung läuft"; Console.ReadLine(); } /// <summary> /// Event wenn eine 001 Datei abgelegt wurde /// </summary> /// <param name="sender"></param> /// <param name="e">Filename</param> private void fsw_Created001(object sender, FileSystemEventArgs e) { filename = e.Name; filetype = "001"; workonfile(filename, filetype); } /// <summary> /// Event wenn eine 002 Datei abgelegt wurde /// </summary> /// <param name="sender"></param> /// <param name="e">Filename</param> private void fsw_Created002(object sender, FileSystemEventArgs e) { filename = e.Name; filetype = "002"; workonfile(filename, filetype); } /// <summary> /// Lese die Datei aus in ein Array und lösche sie /// Splitte die Dateien bei den Rauten # und kürzer Leerzeichen /// Unterscheide ob Folgejob verarbeitet wird oder nicht /// Rufe die Methode sort auf und übergebe den Array /// </summary> /// <param name="filename">Pfad zur bearbeitenden Datei</param> /// <param name="filetype">Differenzierung 001 oder 002</param> private void workonfile(string filename, string filetype) { try { reader = new StreamReader(source + filename); filecontent = reader.ReadToEnd(); reader.Close(); File.Delete(@source + filename); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } content = filecontent.Split('#'); for (int i = 0; i < content.Length; i++) { content[i] = content[i].Trim(); content_folgejob[i] = content[i]; content_hauptjob[i] = content[i]; } // temp 1 nur hauptjob // temp 2 haupt und folgejob if (filetype == "002") { content_hauptjob[50] = ""; content_hauptjob[51] = ""; content_hauptjob[52] = ""; tempState = 1; } else //filetype == "001" { content_hauptjob[50] = ""; content_hauptjob[51] = ""; content_hauptjob[52] = ""; tempState = 1; if (content_folgejob[50] != "" && content_folgejob[50] != " ") { content_folgejob[1] = content_folgejob[50]; content_folgejob[2] = content_folgejob[51]; content_folgejob[3] = content_folgejob[52]; content_folgejob[50] = ""; content_folgejob[51] = ""; content_folgejob[52] = ""; tempState = 2; } } if (mappingMatches(content) == true && mappingMatches(content_folgejob) == true && mappingMatches(content_hauptjob) == true) { if (content_hauptjob[3] == "1") { matParam = "K"; } else { matParam = "M"; } sort(content_hauptjob, matParam + filename); if (tempState == 2) { if (content_folgejob[3] == "1") { matParam = "K"; } else { matParam = "M"; } sort(content_folgejob, matParam + filename); } startCreate("archiv", content, filename); // ins Archiv } else { startCreate("fehler", content, filename); // ins Log } // Arrays leeren, weiterarbeiten Array.Clear(content, 0, content.Length); Array.Clear(content_hauptjob, 0, content_hauptjob.Length); Array.Clear(content_folgejob, 0, content_folgejob.Length); } /// <summary> /// Unterscheidung der verschiedenen Mappings zur Sortierung /// Aufruf der Verschiebe/Kopierfunktion /// </summary> /// <param name="content">zu verarbeitendes Array</param> /// <param name="filename">Dateiname</param> private void sort(string[] content, string filename) { // ... // setze destination // ... startCreate(destination, content, filename); } /// <summary> /// Erstellt die Dateien neu in den passenden Ordnern /// Ruft writeLog danach auf /// </summary> /// <param name="destination">Zielordner</param> /// <param name="content">zu verarbeitendes Array</param> /// <param name="filename">Dateiname</param> private void startCreate(string destination, string[] content, string filename) { // ... // setze filepath // ... for (int k = 0; k < content.Length; k++) { writethis = writethis + content[k] + "#"; } try { while (File.Exists(filepath) == true) { filepath = filepath + ".001"; filename = filename + ".001"; } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } try { writer = File.CreateText(filepath); writer.Write(writethis); writer.Close(); writethis = ""; } catch (Exception ex) { Console.WriteLine(ex.ToString()); } writeLog(destination, filename, content[1], content[3], content[0]); } /// <summary> /// schreibt einen Logeintrag mit Uhrzeit und ein paar Angaben zu den verarbeiteten Jobs /// in eine Textdatei /// </summary> /// <param name="destination">Zielordner</param> /// <param name="filename">benutzer Dateiname</param> /// <param name="job">Welcher Job</param> /// <param name="material">Welches Material</param> /// <param name="anzahl">Wie viele Schilder</param> private void writeLog(string destination, string filename, string job, string material, string anzahl) { dt = DateTime.Now; date = dt.ToString(); logentry = date + " : " + destination + "; " + filename + "; " + job + "; " + material + "; " + anzahl + "; "; if (File.Exists(logfile) == true) { using (StreamWriter sw = File.AppendText(logfile)) { sw.WriteLine(logentry); sw.Close(); } } else // Datei erzeugen { using (StreamWriter sw = File.CreateText(logfile)) { sw.WriteLine(logentry); sw.Close(); } } Console.WriteLine(logentry); } /// <summary> /// Überprüft das übergebene Array in Position 1 und 3, also auf Job und Material, /// ob es mit dem vorgegebenen Mapping übereinstimmt und antwortet /// </summary> /// <param name="checkContent">zu prüfendes Array</param> /// <returns>true wenn alles passt</returns> private bool mappingMatches(string[] checkContent) { // ... // verschiedene Mappingmöglichkeiten werden getestet // ... }
Bin noch nicht lange beim Programmieren, suche mir auch vieles zusammen, aber hier hackt es leider...
Gruß,
Florian
Antworten
-
Hallo Florian,
Ich nehme stark an, dass der Filesystemwatcher sich da selbst irgendwo in die Quere kommt.
Du kannst diese Annahme leicht überprüfen:
Lade Mark Russinovichs handle.exe herunter und führe folgende Befehlszeile aus:handle.exe [Name_der_gesperrten_Datei]
Ich vermute den Fehler weniger beim FileSystemWatcher als in deiner Anwendungslogik. Der Fehler könnte sich z.B. ereignen, wenn man mit der Bearbeitung der Datei beginnt, bevor diese vollständig übertragen wurde. Beachte bitte, dass manche Ereignisse mehrfach ausgelöst werden könnten, d.h. Du mußt sicherstellen, dass die Bearbeitung nicht mehrfach erfolgt.
Mark Russinovich - Handle v3.46
http://technet.microsoft.com/en-us/sysinternals/bb896655Gruß
Marcel- Als Antwort markiert Robert BreitenhoferModerator Mittwoch, 18. April 2012 17:26
-
Florian,
hier in diesem Falle ist es eigentlich gar kein .NET-Problem, sondern liegt daran wie das native Win32 API und Windows Dateisystem funktioniert!
(der FSW nutzt Win32 http://msdn.microsoft.com/en-us/library/aa365465.aspx )Oft wird es darauf hinauslaufen, dass man sich nicht direkt auf den FSW stützt,
sondern (leider!)
1.) eine intelligente Polling-Schleife, die nach den gewünschten Dateien (wohl deine *.001/002) sucht,
2.) Schleife, die mehrfach versucht (try-catch!) die gefundenen Dateien per exclusive (FileShare.None) zu öffnen/lesen.
Damit beides nicht total zum CPU-Fresser gerät, sind zB Delays einzubauen und ggf max. Versuche zu limitieren.
Erst dann wird man typisch diesen Vorgang noch von aussen per FSW 'aufwecken' lassen.Das Thema ist nichts neues, es finden sich massenhaft wie:
http://stackoverflow.com/questions/50744/
http://stackoverflow.com/questions/699538/- Als Antwort markiert Robert BreitenhoferModerator Mittwoch, 18. April 2012 17:26
Alle Antworten
-
Hallo Florian
der FSW ist nicht dazu geacht, einen gesicherten, lückenlosen Ablauf (journaling) von Datei-Transaktion zu bieten.
Der FSW selber -lockt- auch keine Dateien, da musst du selber für einen geordneten, koordinierten Dateizugriff sorgen, und zwar nicht nur auf deiner Seite sondern auch in Bezug auf alle weitere Prozesse, die deine Dateien erstellen/beschreiben!
(zB per exclusive Open http://msdn.microsoft.com/en-us/library/system.io.fileshare.aspx )Kurz, der FSW taugt eher nur für eine 'Notification' (im Sinne eines 'aufwecken').
-
Hallo Thomas,
danke für Deine Antwort. Den Dateizugriff und was dazu gehört gucke ich mir grad an. Deine Aussage geht aber ja dahin, dass der FSW eigentlich dafür nicht geeignet ist. Gibt es sonst eine Möglichkeit mein Vorhaben zu realisieren? Alle Möglichkeiten zu finden ist ja fast unmöglich bei den ganzen Bibliotheken und Klassen die .net für C# mitbringt ;-) (zumindest für mich bis dato).
Es sollte also eine Liveüberwachung von einem Ordner entstehen, die sobald eine Textdatei in dem Ordner liegt (bzw. eventuell sogar bei Programmstart da vorhanden war) einen Bearbeitungsvorgang dieser Textdatei anstößt.
Gruß,
Florian
-
Florian,
hier in diesem Falle ist es eigentlich gar kein .NET-Problem, sondern liegt daran wie das native Win32 API und Windows Dateisystem funktioniert!
(der FSW nutzt Win32 http://msdn.microsoft.com/en-us/library/aa365465.aspx )Oft wird es darauf hinauslaufen, dass man sich nicht direkt auf den FSW stützt,
sondern (leider!)
1.) eine intelligente Polling-Schleife, die nach den gewünschten Dateien (wohl deine *.001/002) sucht,
2.) Schleife, die mehrfach versucht (try-catch!) die gefundenen Dateien per exclusive (FileShare.None) zu öffnen/lesen.
Damit beides nicht total zum CPU-Fresser gerät, sind zB Delays einzubauen und ggf max. Versuche zu limitieren.
Erst dann wird man typisch diesen Vorgang noch von aussen per FSW 'aufwecken' lassen.Das Thema ist nichts neues, es finden sich massenhaft wie:
http://stackoverflow.com/questions/50744/
http://stackoverflow.com/questions/699538/- Als Antwort markiert Robert BreitenhoferModerator Mittwoch, 18. April 2012 17:26
-
Hallo Florian,
Ich nehme stark an, dass der Filesystemwatcher sich da selbst irgendwo in die Quere kommt.
Du kannst diese Annahme leicht überprüfen:
Lade Mark Russinovichs handle.exe herunter und führe folgende Befehlszeile aus:handle.exe [Name_der_gesperrten_Datei]
Ich vermute den Fehler weniger beim FileSystemWatcher als in deiner Anwendungslogik. Der Fehler könnte sich z.B. ereignen, wenn man mit der Bearbeitung der Datei beginnt, bevor diese vollständig übertragen wurde. Beachte bitte, dass manche Ereignisse mehrfach ausgelöst werden könnten, d.h. Du mußt sicherstellen, dass die Bearbeitung nicht mehrfach erfolgt.
Mark Russinovich - Handle v3.46
http://technet.microsoft.com/en-us/sysinternals/bb896655Gruß
Marcel- Als Antwort markiert Robert BreitenhoferModerator Mittwoch, 18. April 2012 17:26
-
Hallo Froqqle,
Ich gehe davon aus, dass die Antworten Dir weitergeholfen haben.
Solltest Du noch "Rückfragen" dazu haben, so gib uns bitte Bescheid.Grüße,
RobertRobert Breitenhofer, MICROSOFT
Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „Entwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können. -
Hallo Froqqle,
Freut uns dass Du hier Hilfe gefunden hast, von den besten Experten unser Community, teste und melde Dich wann immer Du möchtest.
**************************************************************************************************************************
Jetzt kannst Du auch Bilder in die Beiträge einfügen: [Info] Neue Grafikfunktion für MSDN Forenbeiträge | Neue Forum Feature: Bilder in einem Beitrag einfügen
- Immer für eine Neue Frage einen Neuen Thread aufmachen
Grundsätzlich ist zu jedem neuen Thema ein eigener Thread zu öffnen, denn dadurch wird die Übersicht gewahrt.
Ein eigener Thread nutzt der Kommunikation, verhindert Spannungen und Streit und trägt der Übersichtlichkeit in einem Forum bei.
- Lösungsbeiträge als „Die Antwort“ markieren
Bitte markieren Sie den Beitrag, der zur Lösung geführt hat, als "Die Antwort". Durch Bewerten eines Beitrags als "Die Antwort" können andere Teilnehmer die Lösung schneller finden. Außerdem können Sie dem Benutzer, der die Antwort eingereicht hat, für seinen Beitrag danken und zur Steigerung der Antwortqualität in der Diskussionsgruppe beitragen.
[Quelle: Forenregeln]
Wie bewerte ich einen Beitrag? Um einen Beitrag als hilfreich zu bewerten, klicken Sie in einem beliebigen Beitrag auf Als hilfreich bewerten. Sie können Ihre Stimme nur einmal für einen Beitrag abgeben.
[Quelle: Häufig gestellte Fragen]
Grüße,
Robert
Robert Breitenhofer, MICROSOFT
Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „Entwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können. -
Hallo Florian,
Damit dir das Testen leichter fällt, habe ich einen kleinen .NET-Wrapper (s. Klasse HandleTool) für das Tool von Mark Russinovich geschrieben.
Über die Methode HandleTool.FindHandleByPartialObjectName(partialName) kannst Du dir eine List<HandleToolFindResults> zurückgeben lassen. Diese Liste enthält dann Details zu den Prozessen, die ein Handle auf das angegebene Objekt halten (Prozessname, PID, Objekttyp, Objekthandle, Objektpfad).
Der Code ist natürlich experimentell, aber damit kriegst Du schon mal einen ersten Eindruck darüber, was man mit dem Tool machen kann:using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; namespace HandleWrapper { class Program { static void Main(string[] args) { try { if (args.Length == 0) PrintUsage(); else { var results = HandleTool.FindHandleByPartialObjectName(args[0]); if (results.Count > 0) { Console.SetBufferSize(250, Math.Max(Console.WindowHeight, (results.Count * 6) + 10)); foreach (var result in results) { Console.WriteLine("Process: {0}", result.ProcessName); Console.WriteLine("ID: {0}", result.Pid); Console.WriteLine("Type: {0}", result.Type); Console.WriteLine("Handle: {0}", result.Handle); Console.WriteLine("Path: {0}", result.Path); Console.WriteLine(); } } else { Console.WriteLine("Keine Ergebnisse."); } } } catch (Exception ex) { Console.WriteLine(ex); } Console.ReadKey(true); } private static void PrintUsage() { Console.WriteLine("HandleWrapper v1.0"); Console.WriteLine("A simple wrapper/parser to use with Mark Russinovich's handle.exe v3.46"); Console.WriteLine("You must run this program as an administrator."); Console.WriteLine("(handle.exe path must be in your PATH environment variable)\n"); Console.WriteLine("\nUsage:\n\nHandleWrapper.exe [partial_filename]\n"); } } public sealed class HandleTool { public static IList<HandleToolFindResults> FindHandleByPartialObjectName(string objectName) { // Tokens for parsing const string tokenProlog = "www.sysinternals.com\r\n\r\n"; const string tokenNoHandleFound = "No matching handles found."; const string tokenColon = ":"; const string tokenSpace = " "; const string tokenPid = "pid" + tokenColon; const string tokenObjectType = "type" + tokenColon; // ToDo: Specify full path to executable if not // in PATH environmental variable const string handleToolExe = "handle.exe"; // Set console encoding Console.OutputEncoding = Encoding.Default; // Start handle.exe process using (Process process = new Process { StartInfo = new ProcessStartInfo { FileName = handleToolExe, Arguments = objectName, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true } }) { process.Start(); // Read output stream string processOutput = process.StandardOutput.ReadToEnd(); process.WaitForExit(); // Return if no handle found if (processOutput.Contains(tokenNoHandleFound)) return null; // Keep only post-prolog output processOutput = processOutput.Substring(processOutput.IndexOf(tokenProlog) + tokenProlog.Length); if (processOutput.ToLower().Contains("error")) throw (new Exception(processOutput)); // Split the output by lines, removing empty ones string[] lines = processOutput.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); // Holds the results List<HandleToolFindResults> results = new List<HandleToolFindResults>(); // Parse each line foreach (var line in lines) { // Determine token positions int posTokenPid = line.IndexOf(tokenPid); if (posTokenPid == -1) continue; int posTokenObjectType = line.IndexOf(tokenObjectType, posTokenPid); int posTokenLastColon = line.LastIndexOf(tokenColon); int posTokenPath = line.LastIndexOf(tokenColon, posTokenLastColon - 1); int posTokenColon = line.LastIndexOf(tokenSpace, posTokenLastColon - 1); int posTokenLastPrecolonSpace = line.LastIndexOf(tokenSpace, posTokenColon - 1) - 1; string outputProcessName = line.Substring(0, posTokenPid); string outputPid = line.Substring(posTokenPid + tokenPid.Length, posTokenObjectType - posTokenPid - tokenObjectType.Length).Trim(); string outputType = line.Substring(posTokenObjectType + tokenObjectType.Length, posTokenPath - posTokenObjectType - (posTokenLastColon - posTokenLastPrecolonSpace)).Trim(); string outputHandle = line.Substring(posTokenLastPrecolonSpace, posTokenColon - posTokenLastPrecolonSpace - 1).Trim(); string outputPath = line.Substring(posTokenPath + posTokenLastColon - posTokenPath - 1).Trim(); // Package parsed line into result object HandleToolFindResults result = new HandleToolFindResults { ProcessName = outputProcessName, Pid = outputPid, Type = outputType, Handle = outputHandle, Path = outputPath }; // Add result to results list results.Add(result); } return results; } } } public struct HandleToolFindResults { public string ProcessName { get; set; } public string Pid { get; set; } public string Type { get; set; } public string Handle { get; set; } public string Path { get; set; } } }
Edit: Für die korrekte Ausführung von handle.exe (und implizit des obigen Wrappers) benötigt man Administratorrechte.
Gruß
Marcel
- Bearbeitet Marcel RomaModerator Donnerstag, 19. April 2012 10:19