Ar galima patikrinti, ar naudojamas failas?

Rašau programą C #, kuri turėtų pakartotinai kreiptis į 1 vaizdo failą. Dažniausiai jis veikia, bet jei mano kompiuteris yra greitas, jis bandys pasiekti failą, kol jis bus išsaugotas atgal į failų sistemą, ir išmeta klaidą: „Failas naudojamas kitame procese“.

Norėčiau rasti būdą, kaip tai padaryti, bet visa mano „googling“ suteikė galimybę sukurti patikrinimus naudojant išimties tvarkymą. Tai yra prieš mano religiją, todėl man įdomu, ar kas nors turėjo geresnį būdą tai padaryti?

711
18 мая '09 в 9:37 2009-05-18 09:37 Dawsy paklausė gegužės 18 d., 09:37, 2009-05-18 09:37
@ 20 atsakymų

Atnaujinta šio sprendimo pastaba . „ FileAccess.ReadWrite “ patikrinimas baigsis tik skaitymui skirtų failų klaida, todėl sprendimas buvo pakeistas, kad patikrintumėte „ FileAccess.Read . Nors šis sprendimas veikia, nes bandymas patikrinti su FileAccess.Read nepavyks, jei failas turi rašymo arba skaitymo užraktą, tačiau šis sprendimas neveiks, jei ant jo nėra rašymo ar skaitymo užrakto, t.y. (skaityti ar rašyti) su FileShare.Read arba FileShare.Write access.

ORIGINALIS: naudoju šį kodą per pastaruosius kelerius metus, ir aš neturėjau jokių problemų.

Suprasti savo abejones naudodami išimtis, bet negalite jų vengti visą laiką:

 protected virtual bool IsFileLocked(FileInfo file) { FileStream stream = null; try { stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None); } catch (IOException) { //the file is unavailable because it is: //still being written to //or being processed by another thread //or does not exist (has already been processed) return true; } finally { if (stream != null) stream.Close(); } //file is not locked return false; } 
459
02 июня '09 в 4:20 2009-06-02 04:20 atsakymą pateikė „ ChrisW“ birželio 02 d. , 09:10, 2009-06-02 04:20

Jūs galite susidurti su lenktynių trasos sąlygomis, kurios dokumentavo pavyzdžius, kaip tai naudojama kaip saugumo pažeidžiamumas. Jei patikrinate, ar failas yra prieinamas, bet tada bandykite jį naudoti, galite mesti tuo momentu, kurį užpuolikas gali naudoti norėdamas naudoti ir naudoti jūsų kode.

Geriausia pabandyti bandyti sugauti / galiausiai, kuri bando gauti failo deskriptorių.

border=0
 try { using (Stream stream = new FileStream("MyFilename.txt", FileMode.Open)) { // File/Stream manipulating code here } } catch { //check here why it failed and ask user to retry if the file is in use. } 
518
18 мая '09 в 9:57 2009-05-18 09:57 atsakymas į Spence gegužės 18 d., 09:57, 2009-05-18 09:57

Naudokite tai patikrinti, ar failas yra užrakintas:

 using System.IO; using System.Runtime.InteropServices; internal static class Helper { const int ERROR_SHARING_VIOLATION = 32; const int ERROR_LOCK_VIOLATION = 33; private static bool IsFileLocked(Exception exception) { int errorCode = Marshal.GetHRForException(exception)  ((1 << 16) - 1); return errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION; } internal static bool CanReadFile(string filePath) { //Try-Catch so we dont crash the program and can check the exception try { //The "using" is important because FileStream implements IDisposable and //"using" will avoid a heap exhaustion situation when too many handles //are left undisposed. using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { if (fileStream != null) fileStream.Close(); //This line is me being overly cautious, fileStream will never be null unless an exception occurs... and I know the "using" does it but its helpful to be explicit - especially when we encounter errors - at least for me anyway! } } catch (IOException ex) { //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!! if (IsFileLocked(ex)) { // do something, eg File.Copy or present the user with a MsgBox - I do not recommend Killing the process that is locking the file return false; } } finally { } return true; } } 

Veiksmingumo sumetimais rekomenduoju perskaityti tos pačios operacijos failo turinį. Štai keletas pavyzdžių:

 public static byte[] ReadFileBytes(string filePath) { byte[] buffer = null; try { using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { int length = (int)fileStream.Length; // get file length buffer = new byte[length]; // create buffer int count; // actual number of bytes read int sum = 0; // total number of bytes read // read until Read method returns 0 (end of the stream has been reached) while ((count = fileStream.Read(buffer, sum, length - sum)) > 0) sum += count; // sum is a buffer offset for next reading fileStream.Close(); //This is not needed, just me being paranoid and explicitly releasing resources ASAP } } catch (IOException ex) { //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!! if (IsFileLocked(ex)) { // do something? } } catch (Exception ex) { } finally { } return buffer; } public static string ReadFileTextWithEncoding(string filePath) { string fileContents = string.Empty; byte[] buffer; try { using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { int length = (int)fileStream.Length; // get file length buffer = new byte[length]; // create buffer int count; // actual number of bytes read int sum = 0; // total number of bytes read // read until Read method returns 0 (end of the stream has been reached) while ((count = fileStream.Read(buffer, sum, length - sum)) > 0) { sum += count; // sum is a buffer offset for next reading } fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP //Depending on the encoding you wish to use - I'll leave that up to you fileContents = System.Text.Encoding.Default.GetString(buffer); } } catch (IOException ex) { //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!! if (IsFileLocked(ex)) { // do something? } } catch (Exception ex) { } finally { } return fileContents; } public static string ReadFileTextNoEncoding(string filePath) { string fileContents = string.Empty; byte[] buffer; try { using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { int length = (int)fileStream.Length; // get file length buffer = new byte[length]; // create buffer int count; // actual number of bytes read int sum = 0; // total number of bytes read // read until Read method returns 0 (end of the stream has been reached) while ((count = fileStream.Read(buffer, sum, length - sum)) > 0) { sum += count; // sum is a buffer offset for next reading } fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP char[] chars = new char[buffer.Length / sizeof(char) + 1]; System.Buffer.BlockCopy(buffer, 0, chars, 0, buffer.Length); fileContents = new string(chars); } } catch (IOException ex) { //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!! if (IsFileLocked(ex)) { // do something? } } catch (Exception ex) { } finally { } return fileContents; } 

Išbandykite save:

 byte[] output1 = Helper.ReadFileBytes(@"c:\temp\test.txt"); string output2 = Helper.ReadFileTextWithEncoding(@"c:\temp\test.txt"); string output3 = Helper.ReadFileTextNoEncoding(@"c:\temp\test.txt"); 
81
16 июня '12 в 5:31 2012-06-16 05:31 atsakymą pateikė Jeremy Thompson birželio 16, 12 d. 5:31 2012-06-16 05:31

Čia yra priimto „PowerShell“ atsakymo versija.

 function IsFileLocked($filename) { $result = $false $fileinfo = [System.IO.FileInfo] (gi $filename).fullname try { $stream = $fileInfo.Open([System.IO.FileMode]"Open",[System.IO.FileAccess]"ReadWrite",[System.IO.FileShare]"None") $stream.Dispose() } catch [System.IO.IOException] { $result = $true } $result } 
15
08 июня '11 в 21:21 2011-06-08 21:21 atsakymą pateikė Frank Schwieterman, birželio 11 d., 11:21, 2011-06-08 21:21

Galbūt galėtumėte naudoti „ FileSystemWatcher“ ir stebėti „Changed“ įvykį.

Aš jo nepanaudojau, bet tai gali būti verta. Jei šiuo atveju failų sistemos analizatorius yra šiek tiek sunkus, norėčiau pereiti prie bandymo / sugavimo / miego ciklo.

6
18 мая '09 в 9:52 2009-05-18 09:52 atsakymą pateikė Karl Johan , gegužės 18 d., 09:52 2009-05-18 09:52

vienintelis būdas žinoti yra naudoti „Win32“ išskirtinę blokavimo API, kuri nėra pernelyg greita, tačiau yra pavyzdžių.

Dauguma žmonių, norėdami tai paprasčiausiai išspręsti, paprasčiausiai bandys / gaudys.

4
18 мая '09 в 9:42 2009-05-18 09:42 atsakymą pateikė Luke Schafer gegužės 09 d. 9:42 2009-05-18 09:42

Galite grąžinti užduotį, kuri suteikia jums srautą, kai tik ji tampa prieinama. Tai supaprastintas sprendimas, tačiau tai yra geras atspirties taškas. Tai saugus siūlas.

 private async Task<Stream> GetStreamAsync() { try { return new FileStream("sample.mp3", FileMode.Open, FileAccess.Write); } catch (IOException) { await Task.Delay(TimeSpan.FromSeconds(1)); return await GetStreamAsync(); } } 

Šį srautą galite naudoti kaip įprasta:

 using (var stream = await FileStreamGetter.GetStreamAsync()) { Console.WriteLine(stream.Length); } 
4
20 окт. Ivan Branets atsakymas spalio 20 d 2015-10-20 15:33 '15 15:33 2015-10-20 15:33

Tiesiog naudokite išimtį, kaip numatyta. Priimkite, kad failas naudojamas, ir bandykite dar kartą, kol veiksmas bus baigtas. Tai taip pat yra efektyviausia, nes prieš pradedant veikti nereikia išleisti jokių ciklų, kad patikrintumėte valstybę.

Pavyzdžiui, naudokite toliau nurodytą funkciją

 TimeoutFileAction(() => { System.IO.File.etc...; return null; } ); 

Pakartotinis metodas, kuris baigiasi po 2 sekundžių

 private T TimeoutFileAction<T>(Func<T> func) { var started = DateTime.UtcNow; while ((DateTime.UtcNow - started).TotalMilliseconds < 2000) { try { return func(); } catch (System.IO.IOException exception) { //ignore, or log somewhere if you want to } } return default(T); } 
4
26 мая '15 в 18:29 2015-05-26 18:29 atsakymas pateikiamas kernowcode gegužės 26, 15, 18:29 2015-05-26 18:29
 static bool FileInUse(string path) { try { using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate)) { fs.CanWrite } return false; } catch (IOException ex) { return true; } } string filePath = "C:\\Documents And Settings\\yourfilename"; bool isFileInUse; isFileInUse = FileInUse(filePath); // Then you can do some checking if (isFileInUse) Console.WriteLine("File is in use"); else Console.WriteLine("File is not in use"); 

Tikiuosi, kad tai padės!

4
22 авг. Julian atsakė rugpjūčio 22 d 2011-08-22 19:01 '11, 19:01, 2011-08-22 19:01

Galite naudoti savo biblioteką, kad galėčiau pasiekti failus iš kelių programų.

Galite ją įdiegti iš „nuget“: „Install-Package Xabe.FileLock“

Jei jums reikia daugiau informacijos apie tai, apsilankykite https://github.com/tomaszzmuda/Xabe.FileLock

 ILock fileLock = new FileLock(file); if(fileLock.Acquire(TimeSpan.FromSeconds(15), true)) { using(fileLock) { // file operations here } } 

FileLock.Acquire metodas grąžinamas tik tuo atveju, jei jis gali užrakinti šio objekto failą. Tačiau programa, įkelianti failą, turi tai padaryti failų užraktu. Jei objekto nėra, metodas grąžina klaidą.

2
07 сент. Atsakymas pateikiamas Tomasz Żmuda, rugsėjo 7 d. 2017-09-07 13:44 '17 13:44 pm 2017-09-07 13:44

Čia yra kodas, kuris, kiek galiu geriausiai pasakyti, daro tą patį, kaip ir priimtas atsakymas, bet mažiau kodo:

  public static bool IsFileLocked(string file) { try { using (var stream = File.OpenRead(file)) return false; } catch (IOException) { return true; } } 

Vis dėlto manau, kad svarbiau tai padaryti taip:

  public static void TryToDoWithFileStream(string file, Action<FileStream> action, int count, int msecTimeOut) { FileStream stream = null; for (var i = 0; i < count; ++i) { try { stream = File.OpenRead(file); break; } catch (IOException) { Thread.Sleep(msecTimeOut); } } action(stream); } 
2
22 авг. atsakymas pateikiamas 22 d. 2016-08-22 23:41 '16 at 11:41 pm 2016-08-22 23:41

Priimti aukščiau pateikti atsakymai turi problemų, jei failas buvo atidarytas rašyti FileShare.Read režimu arba jei faile yra tik skaitymo atributas, kodas neveiks. Šis pakeistas sprendimas patikimai veikia, atsižvelgiant į du dalykus (taip pat ir dėl priimto sprendimo):

  • Jis neveiks bendrai naudojamiems failams.
  • Tai neatsižvelgia į problemas, susijusias su siūlais, todėl turite blokuoti arba spręsti problemas su siūlais atskirai.

Turint tai omenyje, jis patikrina, ar failas užrakintas rašymui ar blokavimui, kad būtų išvengta skaitymo:

 public static bool FileLocked(string FileName) { FileStream fs = null; try { // NOTE: This doesn't handle situations where file is opened for writing by another process but put into write shared mode, it will not throw an exception and won't show it as write locked fs = File.Open(FileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None); // If we can't open file for reading and writing then it locked by another process for writing } catch (UnauthorizedAccessException) // https://msdn.microsoft.com/en-us/library/y973b725(v=vs.110).aspx { // This is because the file is Read-Only and we tried to open in ReadWrite mode, now try to open in Read only mode try { fs = File.Open(FileName, FileMode.Open, FileAccess.Read, FileShare.None); } catch (Exception) { return true; // This file has been locked, we can't even open it to read } } catch (Exception) { return true; // This file has been locked } finally { if (fs != null) fs.Close(); } return false; } 
2
15 окт. atsakymas pateikiamas rboy 15 oct. 2015-10-15 16:34 '15 16:34 2015-10-15 16:34

Be trijų linijų ir tik nuorodų: jei norite gauti visą informaciją - „Microsoft Dev Center“ yra nedidelis projektas:

https://code.msdn.microsoft.com/windowsapps/How-to-know-the-process-704839f4

Nuo įvadas:

„NET Framework 4.0“ sukurtas „C #“ pavyzdys padės išsiaiškinti, kuris procesas turi failo užrakinimą. RmStartSession funkcija , kuri yra įtraukta į failą rstrtmgr.dll, buvo sukurta paleisti paleisties tvarkyklės seansą, o naujas „Win32Exception“ objekto pavyzdys sukuriamas pagal grąžinimo rezultatą. Užregistravus resursus Restart Manager seansui, RmGetList funkcija yra pakviesta patikrinti, kurios programos naudoja tam tikrą failą, nurodydamos RM_PROCESS_INFO matricą per RmRegisterRescources funkciją.

Jis veikia jungiantis prie administratoriaus paleisties sesijos.

Paleidimo vadybininkas naudoja sesijoje užregistruotų išteklių sąrašą, kad nustatytų, kurios programos ir paslaugos turėtų būti išjungtos ir iš naujo paleistos. Ištekliai gali būti identifikuojami pagal failų pavadinimus, sutrumpintus paslaugų pavadinimus arba RM_UNIQUE_PROCESS struktūras, kurios apibūdina veikiančias programas.

Tai gali būti šiek tiek pervertinta dėl jūsų konkrečių poreikių ... Bet jei tai jums reikia, eikite į priekį ir pasiimkite prieš projektą.

1
09 окт. atsakymas duotas Bernhard 09 spalis. 2018-10-09 09:56 '18 at 9:56 2018-10-09 09:56

Mano patirtis, jūs paprastai norite tai padaryti, tada „apsaugoti“ savo failus, kad padarytumėte kažką fantastiško, ir tada naudokite „saugomus“ failus. Jei turite tik vieną failą, kurį norite naudoti tokiu būdu, galite naudoti „Jeremy Thompson“ atsakyme paaiškintą triuką. Tačiau, jei bandysite tai padaryti daugeliu failų (pvz., Rašydami diegimo programą), būsite labai skausmingi.

Labai elegantiškas būdas, kuriuo galite nuspręsti, yra tai, kad jūsų failų sistema neleis jums pakeisti aplanko pavadinimo, jei ten naudojamas vienas iš failų. Laikykite aplanką toje pačioje failų sistemoje, ir jis veiks kaip žavesys.

Atminkite, kad turėtumėte žinoti apie akivaizdžius šio produkto naudojimo būdus. Galiausiai failai nebus užrakinti. Taip pat atminkite, kad yra ir kitų priežasčių, dėl kurių jūsų Move operacija gali sugesti. Akivaizdu, kad tinkamas klaidų apdorojimas (MSDN) gali padėti čia.

 var originalFolder = @"c:\myHugeCollectionOfFiles"; // your folder name here var someFolder = Path.Combine(originalFolder, "..", Guid.NewGuid().ToString("N")); try { Directory.Move(originalFolder, someFolder); // Use files } catch // TODO: proper exception handling { // Inform user, take action } finally { Directory.Move(someFolder, originalFolder); } 

Atskirų failų atveju norėčiau laikytis Jeremy Thompson paskelbto blokavimo pasiūlymo.

1
02 сент. atsakymas pateikiamas atlaste 02 sep . 2014-09-02 13:31 '14 at 13:31 2014-09-02 13:31

Įdomu, ar tai sukelia WTF refleksus. Turiu procesą, kuris sukuria ir vėliau paleidžia PDF dokumentą iš konsolės programos. Tačiau susidūriau su silpnumu, kai vartotojas turėjo keletą kartų paleisti procesą, generuodamas tą patį failą be pirmojo uždaryto failo uždarymo, programa sukurtų išimtį ir miršta. Tai buvo gana paplitusi, nes failų pavadinimai yra pagrįsti pardavimo numeriais.

Vietoj to, kad nesugadintumėtės, aš nusprendžiau pasikliauti automatiniu papildomu failų versijos valdymu:

 private static string WriteFileToDisk(byte[] data, string fileName, int version = 0) { try { var versionExtension = version > 0 ? $"_{version:000}" : string.Empty; var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{fileName}{versionExtension}.pdf"); using (var writer = new FileStream(filePath, FileMode.Create)) { writer.Write(data, 0, data.Length); } return filePath; } catch (IOException) { return WriteFileToDisk(data, fileName, ++version); } } 

Tikriausiai gali prireikti papildomų atsargumo priemonių, kad sugautumėte catch kad būtų užtikrintas tinkamas IOException išimtis. Aš tikriausiai taip pat išvalysiu programos saugyklą paleidimo metu, nes šie failai vis tiek turi būti laikini.

Suprantu, kad tai už OP klausimų ribų, tik patikrinkite, ar naudojamas failas, bet tai tikrai buvo problema, kurią ieškojau, kai atvykau čia, todėl galbūt tai bus naudinga kitam.

0
05 апр. Atsakymą pateikė Vinney Kelly 05 Bal. 2018-04-05 15:07 '18, 15:07 pm 2018-04-05 15:07

jei reikia vengti blokuoti ir mesti blokus, galite turėti eilutes, kurios prideda failo kelią ir būseną (nepriklausomai nuo to, ar jis yra atviras, ar ne). todėl galite patikrinti, ar kelias yra prieinamas, ar ne iš kelio, saugomo styginių sąraše. Tikiuosi, kad teisingai pasiūlysiu, jei man tai labai svarbu.

0
05 дек. Atsakymą pateikė Nisha 05 dec. 2017-12-05 10:30 '17 at 10:30 2017-12-05 10:30

Pabandykite perkelti / kopijuoti failą į temp aplanką. Jei galite, jis neturi užrakto, ir jūs galite saugiai dirbti laikinajame aplanke be užrakinimo. Dar kartą pabandykite perkelti jį į x sekundes.

-2
18 мая '09 в 12:11 2009-05-18 12:11 Carra atsakė gegužės 18 d. 12 val. 12:11 2009-05-18 12:11

Naudoju šią problemą, bet aš turiu laiko tarpą, kai tikrinu failų užrakinimą su funkcija „IsFileLocked“ ir kai atidariu failą. Per šį laiką failas gali būti atidarytas kitame gijoje, todėl gausiu IOException.

Taigi, pridėjau papildomą kodą. Mano atveju noriu įkelti XDocument:

  XDocument xDoc = null; while (xDoc == null) { while (IsFileBeingUsed(_interactionXMLPath)) { Logger.WriteMessage(Logger.LogPrioritet.Warning, "Deserialize can not open XML file. is being used by another process. wait..."); Thread.Sleep(100); } try { xDoc = XDocument.Load(_interactionXMLPath); } catch { Logger.WriteMessage(Logger.LogPrioritet.Error, "Load working!!!!!"); } } 

Ką manote? Ar galiu kažką pakeisti? Gal man nereikėjo naudoti „IsFileBeingUsed“ funkcijos?

Ačiū

-2
17 янв. Atsakymas pateikiamas zzfima 17 jan. 2012-01-17 18:45 „12 at 18:45 pm 2012-01-17 18:45

Ar girdėjote apie sigletono klasę? jei priversite visus savo vaizdų manipuliavimus per šią klasę ir iškviesite visas funkcijas, turėsite atomų. vienintelė problema yra ta, kad jums reikia įdėti klasę į procesą.

-6
02 нояб. atsakymą pateikė MikeCharlieDelta 02 lapkričio. 2011-11-02 16:52 '11 at 16:52 2011-11-02 16:52

Turėjau panašią problemą, ir aš kažką, kas atrodė darbui, naudojo išimties tvarkymą, nors ...

Aš nustatiau skaitiklį, kad bandytumėte sustabdyti begalinę kilpą 100 kartų.

Žr. Žemiau ...

  private void uploadFiles(string filename) { try { string fromFileAndPath = Properties.Settings.Default.Path + "\\" + filename; string toFileAndPath = Properties.Settings.Default.CopyLocation + "\\" + filename; if (!File.Exists(toFileAndPath)) { FileInfo imgInfo = new FileInfo(fromFileAndPath); bool copied = false; int counter = 0; while (!copied  counter < 100) //While was added as I was getting "The process cannot access the file because it is being used by another process" errors. { try { counter++; imgInfo.CopyTo(toFileAndPath); copied = true; } catch { //If it cannot copy catch } } if (counter > 100) throw new Exception("Unable to copy file!"); Thread.Sleep(1); } } catch (Exception ex) { MessageBox.Show("An error occurred: " + ex.Message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error); } } 
-14
02 июня '09 в 4:03 2009-06-02 04:03 atsakymą pateikė „ Timbo“ birželio 02 d. 09:04 04:03 2009-06-02 04:03

Kiti klausimai apie žymes arba Ask a question