Mam trochę kodu i kiedy się wykonuje, rzuca IOException
, mówiąc to
Proces nie może uzyskać dostępu do pliku „nazwa_pliku”, ponieważ jest używany przez inny proces
Co to oznacza i co mogę z tym zrobić?
c#
.net
language-agnostic
ioexception
Adriano Repetti
źródło
źródło
Odpowiedzi:
Jaka jest przyczyna?
Komunikat o błędzie jest dość jasny: próbujesz uzyskać dostęp do pliku i nie jest on dostępny, ponieważ inny proces (lub nawet ten sam proces) coś z nim robi (i nie pozwala na żadne udostępnianie).
Debugowanie
Może to być dość łatwe do rozwiązania (lub dość trudne do zrozumienia), w zależności od konkretnego scenariusza. Zobaczmy trochę.
Twój proces jest jedynym, który ma dostęp do tego pliku.
Masz pewność, że drugi proces to Twój własny proces. Jeśli wiesz, że otwierasz ten plik w innej części programu, to przede wszystkim musisz sprawdzić, czy po każdym użyciu poprawnie zamykasz uchwyt pliku. Oto przykład kodu z tym błędem:
Na szczęście
FileStream
implementujeIDisposable
, więc łatwo jest opakować cały kod wusing
instrukcję:Ten wzorzec zapewni również, że plik nie zostanie otwarty w przypadku wyjątków (może to być powód, dla którego plik jest używany: coś poszło nie tak i nikt go nie zamknął; zobacz przykład w tym poście ).
Jeśli wszystko wydaje się w porządku (jesteś pewien, że zawsze zamykasz każdy otwierany plik, nawet w przypadku wyjątków) i masz wiele działających wątków, masz dwie opcje: przerobić kod w celu serializacji dostępu do plików (nie zawsze jest to wykonalne i nie zawsze potrzebne) lub zastosuj wzorzec ponawiania . To dość powszechny wzorzec dla operacji I / O: próbujesz coś zrobić, aw przypadku błędu czekasz i próbujesz ponownie (czy zadałeś sobie pytanie, dlaczego na przykład powłoka systemu Windows potrzebuje trochę czasu, aby poinformować Cię, że plik jest używany i nie można go usunąć?). W C # jest to dość łatwe do zaimplementowania (zobacz także lepsze przykłady dotyczące operacji we / wy dysków , sieci i dostępu do bazy danych ).
Zwróć uwagę na typowy błąd, który bardzo często widzimy w StackOverflow:
W tym przypadku
ReadAllText()
nie powiedzie się, ponieważ plik jest używany (File.Open()
w linii przed). Wcześniejsze otwarcie pliku jest nie tylko niepotrzebne, ale także złe. To samo odnosi się do wszystkichFile
funkcji, które nie zwracają się uchwyt do pliku, nad którym pracujesz z:File.ReadAllText()
,File.WriteAllText()
,File.ReadAllLines()
,File.WriteAllLines()
i innych (takichFile.AppendAllXyz()
funkcji) będzie wszystko otwarte i zamknij plik przez siebie.Twój proces nie jest jedynym, który ma dostęp do tego pliku.
Jeśli Twój proces nie jest jedynym, który ma dostęp do tego pliku, interakcja może być trudniejsza. Wzór ponawiania pomoże (jeśli plik nie powinien być otwarty przez kogoś innego, ale to jest, to trzeba podobnego narzędzia Process Explorer, aby sprawdzić , kto robi to, co ).
Sposoby uniknięcia
W stosownych przypadkach zawsze używaj instrukcji using do otwierania plików. Jak wspomniano w poprzednim akapicie, pomoże ci to aktywnie uniknąć wielu typowych błędów (zobacz ten post, aby zobaczyć przykład, jak go nie używać ).
Jeśli to możliwe, spróbuj zdecydować, kto jest właścicielem dostępu do określonego pliku i scentralizuj dostęp za pomocą kilku dobrze znanych metod. Jeśli na przykład masz plik danych, w którym program czyta i zapisuje, to cały kod I / O powinien być umieszczony w jednej klasie. Ułatwi to debugowanie (ponieważ zawsze możesz umieścić tam punkt przerwania i zobaczyć, kto robi co), a także będzie to punkt synchronizacji (jeśli jest to wymagane) dla wielokrotnego dostępu.
Nie zapominaj, że operacje we / wy mogą zawsze kończyć się niepowodzeniem, typowy przykład jest następujący:
Jeśli ktoś usunie plik po,
File.Exists()
ale wcześniejFile.Delete()
, wyrzuci onIOException
miejsce, w którym możesz niesłusznie czuć się bezpiecznie.Jeśli to możliwe, zastosuj wzorzec ponawiania , a jeśli używasz
FileSystemWatcher
, rozważ odroczenie akcji (ponieważ otrzymasz powiadomienie, ale aplikacja może nadal pracować wyłącznie z tym plikiem).Scenariusze zaawansowane
Nie zawsze jest to takie proste, więc może być konieczne udostępnienie dostępu komuś innemu. Jeśli na przykład czytasz od początku i piszesz do końca, masz co najmniej dwie możliwości.
1) udostępniać to samo
FileStream
z odpowiednimi funkcjami synchronizacji (ponieważ nie jest bezpieczny wątkowo ). Zobacz przykład tego i tego postu.2) użyj
FileShare
wyliczania, aby poinstruować system operacyjny, aby zezwalał innym procesom (lub innym częściom twojego własnego procesu) na jednoczesny dostęp do tego samego pliku.W tym przykładzie pokazałem, jak otworzyć plik do zapisu i udostępnić do czytania; należy pamiętać, że podczas odczytywania i zapisywania nakładania się, skutkuje to niezdefiniowanymi lub nieprawidłowymi danymi. Jest to sytuacja, z którą trzeba sobie poradzić podczas czytania. Należy również pamiętać, że nie zapewnia to dostępu do
stream
wątku bezpiecznego, więc ten obiekt nie może być udostępniany wielu wątkom, chyba że dostęp jest w jakiś sposób zsynchronizowany (zobacz poprzednie linki). Dostępne są inne opcje udostępniania, które otwierają bardziej złożone scenariusze. Więcej informacji można znaleźć w witrynie MSDN .Ogólnie N procesów może czytać z tego samego pliku razem, ale tylko jeden powinien pisać, w kontrolowanym scenariuszu możesz nawet włączyć współbieżne zapisy, ale nie można tego uogólnić w kilku akapitach tekstu w tej odpowiedzi.
Czy można odblokować plik używany przez inny proces? Nie zawsze jest to bezpieczne i nie jest takie łatwe, ale tak, jest to możliwe .
źródło
File.Create(path)
, powinieneś dodać.Close()
na końcu tego, zanim napiszesz do niego. Opróczusing
instrukcji dotyczących zapisywania plików, a następnie usuwania po nich, są takie pułapki . W swoim pytaniu powinieneś zamieścić kod dotyczący sposobu tworzenia i usuwania pliku. Ale prawdopodobnie wpisuje się w coś wspomnianego powyżej.Directory.SetCreationTimeUTC()
ale kończy się niepowodzeniem, gdy Eksplorator plików jest otwarty, twierdząc, że katalog jest używany przez inny proces. Jak mam sobie z tym poradzić?Korzystanie z FileShare rozwiązało mój problem z otwieraniem pliku, nawet jeśli jest otwierany przez inny proces.
źródło
Wystąpił problem podczas przesyłania obrazu, nie można go usunąć i znalazłem rozwiązanie. gl hf
źródło
Otrzymałem ten błąd, ponieważ robiłem File.Move do ścieżki pliku bez nazwy pliku, muszę określić pełną ścieżkę w miejscu docelowym.
źródło
Błąd wskazuje, że inny proces próbuje uzyskać dostęp do pliku. Może ty lub ktoś inny otworzył ją, gdy próbujesz do niej napisać. „Czytaj” lub „Kopiuj” zwykle tego nie powoduje, ale pisanie do niego lub wywoływanie usuwania na nim tak.
Jest kilka podstawowych rzeczy, których należy unikać, o czym wspomniały inne odpowiedzi:
W
FileStream
operacjach umieść go wusing
bloku zFileShare.ReadWrite
trybem dostępu.Na przykład:
Pamiętaj, że
FileAccess.ReadWrite
nie jest to możliwe, jeśli używaszFileMode.Append
.Natknąłem się na ten problem, gdy używałem strumienia wejściowego do wykonania,
File.SaveAs
gdy plik był używany. W moim przypadku nie musiałem w ogóle zapisywać go z powrotem w systemie plików, więc skończyło się na tym, że po prostu to usunąłem, ale prawdopodobnie mogłem spróbować utworzyć FileStream wusing
instrukcji zFileAccess.ReadWrite
, podobnie jak kod powyżej.Zapisanie danych jako inny plik i powrót do usunięcia starego, gdy okaże się, że nie jest już używany, a następnie zmiana nazwy pliku, który został pomyślnie zapisany, na nazwę oryginalnego jest opcją. Sposób testowania używanego pliku odbywa się za pomocą
linii w moim kodzie poniżej i można to zrobić w usłudze Windows, w pętli, jeśli masz określony plik, który chcesz regularnie oglądać i usuwać, gdy chcesz go zastąpić. Jeśli nie zawsze masz ten sam plik, można zaktualizować plik tekstowy lub tabelę bazy danych, aby usługa zawsze sprawdzała nazwy plików, a następnie przeprowadza sprawdzenie procesów, a następnie wykonuje zabijanie i usuwanie procesów na nim, jak to opisuję w następnej opcji. Zauważ, że będziesz potrzebować nazwy użytkownika i hasła do konta, które ma uprawnienia administratora na danym komputerze, oczywiście, aby usunąć i zakończyć procesy.
Jeśli nie wiesz, czy plik będzie używany, gdy próbujesz go zapisać, możesz zamknąć wszystkie procesy, które mogą go używać, takie jak Word, jeśli jest to dokument programu Word, przed zapisaniem.
Jeśli jest lokalny, możesz to zrobić:
Jeśli jest zdalny, możesz to zrobić:
gdzie
txtUserName
jest w postaciDOMAIN\user
.Powiedzmy, że nie znasz nazwy procesu, który blokuje plik. Następnie możesz to zrobić:
Zauważ, że
file
musi to być ścieżka UNC:\\computer\share\yourdoc.docx
aby programProcess
mógł dowiedzieć się, na jakim komputerze się znajduje ip.MachineName
jest ważny.Poniżej znajduje się klasa, której używają te funkcje, do której należy dodać odwołanie
System.Management
. Kod został pierwotnie napisany przez Erica J .:źródło
Jak wskazywały inne odpowiedzi w tym wątku, aby rozwiązać ten błąd, musisz dokładnie sprawdzić kod, aby zrozumieć, gdzie plik jest blokowany.
W moim przypadku wysyłałem plik jako załącznik do wiadomości e-mail przed wykonaniem operacji przenoszenia.
Plik został więc zablokowany na kilka sekund, aż klient SMTP zakończył wysyłanie wiadomości e-mail.
Rozwiązaniem, które zastosowałem, było najpierw przeniesienie pliku, a następnie wysłanie e-maila. To rozwiązało problem.
Innym możliwym rozwiązaniem, jak wskazał wcześniej Hudson, byłoby pozbycie się przedmiotu po użyciu.
źródło
File.Move()
, nie działa i wyświetla ten sam błąd. Jeśli po prostu dodam plik do wiadomości e-mail, nie sądzę, że wystąpi błąd podczas używania podczasAttachments.Add()
operacji, ponieważ jest to tylko operacja kopiowania. Jeśli z jakiegoś powodu tak się stało, możesz skopiować go do katalogu Temp, dołączyć kopię, a następnie usunąć skopiowany plik. Ale nie sądzę, że jeśli OP chce zmodyfikować plik i użyć tego, to tego rodzaju rozwiązanie (którego nie pokazałeś kodu, tylko część załączająca) zadziałałoby..Dispose()
jest zawsze dobrym pomysłem, ale nie ma tu znaczenia, chyba że plik zostanie otwarty we wcześniejszej operacji.Miałem następujący scenariusz, który powodował ten sam błąd:
Większość plików miała mały rozmiar, jednak kilka było dużych, więc próba ich usunięcia spowodowała brak dostępu do pliku błąd .
Nie było to łatwe do znalezienia, jednak rozwiązanie było tak proste, jak oczekiwanie „na zakończenie realizacji zadania”:
źródło
Mój poniższy kod rozwiązuje ten problem, ale sugeruję Przede wszystkim musisz zrozumieć, co powoduje ten problem i wypróbować rozwiązanie, które możesz znaleźć, zmieniając kod
Mogę podać inny sposób rozwiązania tego problemu, ale lepszym rozwiązaniem jest sprawdzenie struktury kodowania i próba przeanalizowania, co tak się dzieje, jeśli nie znajdziesz żadnego rozwiązania, możesz przejść z tym kodem poniżej
źródło
GC.*()
, prawdopodobnie masz inne problemy z kodem. 2) Wiadomość jest zlokalizowana i krucha , zamiast tego użyj HRESULT. 3) Możesz chcieć spaćTask.Delay()
(a dziesięć sekund to w wielu przypadkach zbyt wiele). 4) Nie masz warunku wyjścia: ten kod może zawiesić się na zawsze. 5) Na pewno nie potrzebujeszgoto
tutaj. 6) ZłapanieException
jest zwykle złym pomysłem, w tym przypadku także dlatego, że ... 6) Jeśli zdarzy się coś innego, to połykasz błąd.GC.Collect()
, dotyczył niektórych obiektów COM.