Also ein ziemlich einfacher Teil des Programms für Windows. Es gibt eine Datei mit mehreren Einträgen. Und sie müssen auf bestimmte Weise gefiltert werden.
Die Lösung ist ganz einfach: Öffnen Sie die Datei, lesen Sie die Datensätze nacheinander, wir schreiben die erforderlichen in eine temporäre Datei. Schließen Sie die Datei. Wir löschen es. Benennen Sie das temporäre in das Original um. Alles ist so einfach, dass ich nicht einmal den Code gebe. Ist das wirklich Grund genug für den Artikel?
Während alles funktioniert, gibt es wirklich keinen Grund, darüber zu schreiben. Aber dann fällt eines Tages plötzlich "alles", weil Das Umbenennen erfolgt nicht aufgrund eines Fehlers, dem der Zugriff verweigert wurde. Dies kommt sehr selten vor, aber immer noch viel häufiger, um kosmische Strahlen zu vermuten.
Wir beginnen mit der Ausgrabung. Der erste gefundene Hinweis: Während des Grabens warnte dieses Zitat aus der Microsoft-Dokumentation:
Die Funktion DeleteFile markiert eine Datei zum Löschen beim Schließen. Daher erfolgt das Löschen der Datei erst, wenn das letzte Handle der Datei geschlossen wurde. Nachfolgende Aufrufe von CreateFile zum Öffnen der Datei schlagen mit ERROR_ACCESS_DENIED fehl.
In Bezug auf die Symptome ist es sehr ähnlich, aber woher kommen diese "anderen Handler", wenn außer uns niemand etwas mit dieser Datei macht und nicht tun sollte? Und wir haben keine anderen Threads mit Threads, die etwas mit dieser Datei machen würden?
Der Schuldige wurde dank SysInternals und ihrem ProcessMonitor gefunden. Wir starten es, installieren den Filter in unserer langmütigen Datei und versuchen, ihn lange und dauerhaft zu reproduzieren. Wir reproduzieren. Wir schauen. Und was sehen wir dort?
01.2: 30: 28.3162097 PM our_prog.exe 1288 CreateFile our.file ERFOLG Gewünschter Zugriff: Allgemeines Lesen / Schreiben
02.2: 25: 28.3164513 PM our_prog.exe 1288 WriteFile our.file SUCCESS Offset: 0, Länge: 898, Priorität: Normal
...
34.2: 25: 28.3173405 PM our_prog.exe 1288 WriteFile our.file SUCCESS Offset: 35.290, Länge: 1.113
35.2: 25: 28.3173493 PM our_prog.exe 1288 WriteFile our.file SUCCESS Offset: 36.403, Länge: 1.128
36.2: 25: 28.3173736 PM our_prog.exe 1288 FlushBuffersFile our.file SUCCESS
37.2: 25: 28.3174212 PM our_prog.exe 1288 WriteFile our.file SUCCESS Offset: 0, Länge: 40.960,
38.2: 25: 28.3175927 PM Explorer.EXE 1884 QueryBasicInformationFile our.file SUCCESS
39.2: 25: 28.3176144 PM Explorer.EXE 1884 CloseFile our.file SUCCESS
40.2: 25: 28.3263642 PM Explorer.EXE 1884 CreateFile our.file ERFOLG Gewünschter Zugriff: Attribute lesen,
41.2: 25: 28.3294990 PM our_prog.exe 1288 CloseFile our.file SUCCESS
42.2: 25: 28.3351356 PM our_prog.exe 1288 CreateFile our.file ERFOLG Gewünschter Zugriff: Attribute lesen, löschen,
43.2: 25: 28.3351856 PM our_prog.exe 1288 QueryAttributeTagFile our.file SUCCESS Attribute: A, ReparseTag: 0x0
44.2: 25: 28.3352020 PM our_prog.exe 1288 SetDispositionInformationFile our.file SUCCESS Delete: True
45.2: 25: 28.3352218 PM our_prog.exe 1288 CloseFile our.file SUCCESS
46.2: 25: 28.3358275 PM our_prog.exe 1288 CreateFile our.file DELETE PENDING Gewünschter Zugriff: Generisches Lesen / Schreiben,
47.2: 25: 28.3362207 PM our_prog.exe 1288 CreateFile our.file DELETE PENDING Gewünschter Zugriff: Generisches Lesen / Schreiben,
48.2: 25: 28.3367696 PM Explorer.EXE 1884 QueryBasicInformationFile our.file SUCCESS
49.2: 25: 28.4279152 PM Explorer.EXE 1884 CloseFile our.file SUCCESS
50.2: 25: 28.4282859 PM Explorer.EXE 1884 CreateFile our.file NAME NICHT GEFUNDEN Gewünschter Zugriff: Attribute lesen,
...
83.2: 25: 29.3497760 PM our_prog.exe 1288 CreateFile our.file ERFOLG Gewünschter Zugriff: Allgemeines Lesen / Schreiben,
Und wir sehen dort Folgendes (zusätzliche Daten wurden gelöscht, um keine Unordnung zu verursachen). Zeilen 1 bis 36 - Wir erstellen eine Datei, schreiben darauf und machen Flush. Das interessanteste beginnt in den Zeilen 38-40. Explorer.exe erscheint aus dem Nichts und beginnt mit dem Lesen unserer Datei.
In Zeile 41 schließen wir unsere Datei. In Zeile 42 - löschen. Und da explorer.exe es noch liest, wird die Datei nicht gelöscht. Was wir in den Zeilen 46 und 47 sehen können, wenn wir versuchen, unsere temporäre Datei in die Hauptdatei umzubenennen (Status DELETE PENDING anstelle von SUCCESS).
Explorer.exe beendet das Lesen nur in Zeile 49. Erst in diesem Moment wird die Datei physisch gelöscht (wie Zeile 50 indirekt mitteilt, versucht unsere persistente explorer.exe erneut, die Datei zum Lesen zu öffnen, schlägt jedoch fehl, weil die Datei nicht mehr).
Was folgt daraus? Dank Microsoft Windows muss jetzt sogar ein einfacher Vorgang zum Löschen von Dateien im Stil der paranoiden Programmierung durchgeführt werden. Sie riefen die Löschfunktion auf, stellten sicher, dass sie OK zurückgab, und traten in den Wartezyklus ein, "bis die Datei noch physisch gelöscht wurde". Nun ja, ohne zu wissen, dass "sie ein Neon im Inneren hat", kann fast nichts getan werden ...
Aber jetzt nagt der Zweifel an mir, dass ein solcher Ansatz eine allgemein akzeptierte Praxis ist. Um bei jeder Arbeit mit Dateien in Windows zu berücksichtigen, dass das Betriebssystem jederzeit beschließt, ohne Ihr Wissen etwas mit den Dateien zu tun, und Code zu schreiben, der dagegen resistent ist. In diesem Zusammenhang ist die Umfrage niedriger.