2013-05-14 29 views
14

Mam problem z udostępnianiem plików, w którym mój proces próbuje odczytać plik dziennika, podczas gdy obecnie jest otwarty przez NLog. W diagnozowaniu problemu znalazłem coś zaskakującego. Dodaje się nie powiedzie:Udostępnianie plików nie działa zgodnie z oczekiwaniami

using (var fileStream1 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read)) 
using (var fileStream2 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.Read)) 
{ 
} 

Drugi FileStream konstruktor połączenia nie powiedzie się z:

System.IO.IOException was unhandled 
    Message=The process cannot access the file 'c:\...\test.file' because it is being used by another process. 
    Source=mscorlib 
    StackTrace: 
     at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 
     at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath) 
     at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share) 

Dzieje się tak pomimo faktu, że pierwszy FileStream wskazuje gotowość do dzielenia się lekturą. Co znalazłem jeszcze bardziej zaskakujące było to, że to działa:

using (var fileStream1 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read)) 
using (var fileStream2 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
{ 
} 

Um, tak, prosząc więcej dostęp podczas otwierania drugi strumień rzeczywiście omija ten problem. Jestem całkowicie zaskoczony, dlaczego tak jest, i mogę tylko założyć, że coś nie rozumiem. Przeczytałem dokumenty API, ale one po prostu wspierają mój obecny model mentalny, by dowiedzieć się, jak to powinno działać, w przeciwieństwie do tego, jak działa.

Oto kilka cytatów z wspierające docs:

Typowym zastosowaniem tego wyliczenia jest określenie, czy dwa procesy może jednocześnie czytać z tego samego pliku. Na przykład, jeśli plik ma wartość otwarty, a odczyt jest określony, inni użytkownicy mogą otworzyć plik do odczytu , ale nie do zapisu.

Oto kolejna perełka:

Poniższy konstruktora FileStream otwiera istniejący plik i dotacje dostęp tylko do odczytu dla innych użytkowników (odczyt).

FileStream s2 = new FileStream(name, FileMode.Open, FileAccess.Read, FileShare.Read);

Czy ktoś może rzucić jakieś światło na ten problem. Testuję to na .NET 4% Windows XP.

Odpowiedz

15
var fileStream2 = new FileStream(..., FileShare.Read) 

Wywołuje wiele programistów. Wszyscy zakładają, że ten dodał udostępnianie odczytu. Nie udało się, pierwotne żądanie dostępu do plików już pozwoliło na odczyt, a jego ponowne podanie niczego nie zmienia. Zamiast tego zaprzecza udostępnieniu zapisu. A to nie działa, ponieważ ktoś już ma dostęp do zapisu. I używa go, nie możesz usunąć tego prawa. Twoje żądanie dostępu do pliku zakończy się niepowodzeniem.

Musisz musi zawierać FileShare.Write.

+1

Dzięki. Więc to nieporozumienie jest tak powszechne, że nawet doktorzy są w błędzie/wprowadzają w błąd? –

+0

Masz na myśli "FileShare.Write" w drugim strumieniu, prawda? – rookie1024

0

Czwarty parametr przekazać

akcji
stałą, która określa, jak plik zostanie udostępniony przez procesy.

określa, w jakim trybie inni mogą otwierać plik. Tak więc oczywiście - kiedy próbujesz otworzyć plik z trybem pracy w trybie "czytaj" i już otwierasz ten sam plik w trybie zapisu - operacja nie powiedzie się.

+0

Nie jest trzecia, jest czwarta. I 'FileShare.Read' _Alsze kolejne otwarcie pliku do odczytu._ –

+0

przepraszam, poprawiono :) – elevener

+0

FileShare.Read -" Pozwala na późniejsze otwarcie pliku do odczytu "- ale nie do zapisu. Masz już otwarty plik do pisania, co jest zabronione w tym trybie udostępniania. Co powinna zrobić funkcja? – elevener

1

Co się dzieje, to, że fileStream2 nie może zmienić późniejszego dostępu do pliku, który jest już otwarty do zapisu (lub dołączenia) przez fileStream1.

fileStream2 powodzeniem otworzyć plik pozostawiając FileShare.Read jako „dziedzictwo” dla późniejszego dostępu tylko wtedy, gdy nie istnieją procesy, które już Write pliku do niej dostęp. Co więcej, w naszym przykładzie mówimy o tym samym procesie. Nie ma większego sensu modyfikowanie właściwości strumienia plików z innego strumienia plików, czyż nie?

Może następujące porównanie wyjaśnia to jeszcze lepiej:

// works 
using (var fileStream1 = new FileStream("test.file", FileMode.OpenOrCreate, FileAccess.Read, FileShare.ReadWrite)) 
using (var fileStream2 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read)) 
using (var fileStream3 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
{ 
} 

// fails 
using (var fileStream1 = new FileStream("test.file", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite)) 
using (var fileStream2 = new FileStream("test.file", FileMode.Append, FileAccess.Write, FileShare.Read)) 
using (var fileStream3 = new FileStream("test.file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
{ 
} 

moim zdaniem opisie wyrażenie dla FileShare.Read:

Umożliwia późniejsze otwarcie pliku do odczytu.

należy odczytywać jako

Późniejszy dostęp do pliku jest ograniczony tylko do odczytu, tym dostęp już istniejących zamków.

[Aktualizacja]

Nie analizowany za pomocą kodu, ale wydaje się, że te dwa linki mogłyby rzucić światło na wewnętrzne funkcjonowanie konstruktora:

The internal FileStream ctor

The internal FileStream Init method

+0

Po kliknięciu odpowiedzi kilka razy jeszcze się nie kliknęło. Trudno mi uwierzyć, że dokumenty API są tak kompletnie błędne. na przykład. "Na przykład, jeśli plik jest otwarty i określono" Odczyt ", inni użytkownicy mogą otworzyć plik do odczytu, ale nie do zapisu." Dokładnie to próbowałem zrobić w moim przykładzie, ale to nie działa. –

+0

@KentBoogaart: Nie wiem, czy jest to istotne (nie rozumiem), ale kluczową frazą może być "inni użytkownicy". Lepszym testem "innych użytkowników" będzie test wielowątkowy. – Chris

+0

@ Chris: mój prawdziwy scenariusz jest wielowątkowy (próba zebrania logów w wątku diagnostyki w tle). Ponadto, dokumenty wyraźnie stwierdzają "przez ten proces lub inny proces" przy opisywaniu zachowania dzielenia się. –

1

Myślę, że znalazłem odpowiedź w dokumentacji dla CreateFile.

W dyskusji parametru dwShareMode, to mówi:

FILE_SHARE_READ 0x00000001 Umożliwia kolejne otwarte operacje na pliku lub urządzenia do żądania odczytu. W przeciwnym razie inne procesy nie mogą otworzyć pliku lub urządzenia, jeśli żądają dostępu do odczytu. Jeśli ta flaga nie jest określona, ​​ale plik lub urządzenie zostało otwarte w celu uzyskania dostępu do odczytu, funkcja nie działa.

FILE_SHARE_WRITE 0x00000002 Umożliwia kolejne otwarte operacje na pliku lub urządzenia do żądania dostępu do zapisu. W przeciwnym razie inne procesy nie mogą otworzyć pliku lub urządzenia, jeśli żądają dostępu do zapisu. Jeśli ta flaga nie jest określona, ​​ale plik lub urządzenie zostało otwarte dla dostępu do zapisu lub ma odwzorowanie pliku z dostępem do zapisu, funkcja nie działa.

To zasadniczo zmienia moje rozumienie sposobu działania udostępniania plików.

Powiązane problemy