2009-09-10 17 views
22

से मुक्त होने के लिए मैं इतना है कि ss.Save() एक नया एक के साथ अधिलेखित कर सकते हैं फ़ाइल नि: शुल्क होने के लिए इंतजार कर रहे हैं। यदि मैं यह दो बार करीब एक साथ (ish) चलाने मैं एक generic GDI+ त्रुटि मिलती है।प्रतीक्षा प्रक्रिया

///<summary> 
    /// Grabs a screen shot of the App and saves it to the C drive in jpg 
    ///</summary> 
    private static String GetDesktopImage(DevExpress.XtraEditors.XtraForm whichForm) 
    { 
     Rectangle bounds = whichForm.Bounds; 

     // This solves my problem but creates a clutter issue 
     //var timeStamp = DateTime.Now.ToString("ddd-MMM-dd-yyyy-hh-mm-ss"); 
     //var fileName = "C:\\HelpMe" + timeStamp + ".jpg"; 

     var fileName = "C:\\HelpMe.jpg"; 
     File.Create(fileName); 
     using (Bitmap ss = new Bitmap(bounds.Width, bounds.Height)) 
     using (Graphics g = Graphics.FromImage(ss)) 
     { 
      g.CopyFromScreen(whichForm.Location, Point.Empty, bounds.Size); 
      ss.Save(fileName, ImageFormat.Jpeg); 
     } 

     return fileName; 
    } 
+3

संभव डुप्लिकेट [क्या कोई फ़ाइल जांचने का कोई तरीका है?] (http://stackoverflow.com/questions/876473/is-there-a-way-to-check-if-a-file-is-in -use) –

+0

इस कोड में 'फ़ाइल। क्रेट (फ़ाइल नाम)' के साथ एक साधारण बग है। जवाब उस बिंदु को याद कर रहे हैं। बंद होने की प्रतीक्षा करना जरूरी नहीं है। – usr

उत्तर

45

इस तरह एक समारोह में यह करना होगा:

public static bool IsFileReady(String sFilename) 
    { 
     // If the file can be opened for exclusive access it means that the file 
     // is no longer locked by another process. 
     try 
     { 
      using (FileStream inputStream = File.Open(sFilename, FileMode.Open, FileAccess.Read, FileShare.None)) 
      { 
       if (inputStream.Length > 0) 
       { 
        return true; 
       } 
       else 
       { 
        return false; 
       } 

      } 
     } 
     catch (Exception) 
     { 
      return false; 
     } 
    } 

चिपका थोड़ी देर के पाश में और आप कुछ है जो जब तक फ़ाइल पहुँचा जा सकता है अवरुद्ध कर देगा है

+0

धन्यवाद! मैंने इसे वहां फेंक दिया 'var isReady = false; जबकि (! IsReady) { isReady = IsFileReady (fileName); } ' और सभी ठीक दिखते हैं। –

+61

आप 'वापसी इनपुटस्ट्रीम। लम्बाई> 0;' भी कर सकते हैं। मुझे उन लोगों को कभी पसंद नहीं आया 'अगर (हालत) सच हो जाती है; और झूठी वापसी; '.. – Default

+6

@Default मुझे लगता है कि सच/झूठी वापसी अधिक पढ़ने योग्य –

2

वहां कोई फ़ंक्शन नहीं है जो आपको लिखने के लिए उपलब्ध किसी विशेष हैंडल/फ़ाइल सिस्टम स्थान पर प्रतीक्षा करने की अनुमति देगा। अफसोस की बात है, आप जो भी कर सकते हैं वह लिखने के लिए हैंडल मतदान है।

3
bool isLocked = true; 
while (isLocked) 
try { 
    System.IO.File.Move(filename, filename2); 
    isLocked = false; 
} 
catch { } 
System.IO.File.Move(filename2, filename); 
2

आप दे सकते हैं प्रणाली इंतजार , प्रक्रिया बंद होने तक।

इस रूप में

बस के रूप में सरल:

Process.Start("the path of your text file or exe").WaitForExit();

8

आप फाइल करने के लिए किसी अन्य प्रक्रिया लेखन फिर से पहुँच छीन सकता है इससे पहले कि आप अपने लिखने ऐसा करने के लिए प्रबंधन से पहले पहुँच को चेक करते हैं। वजह मैं निम्नलिखित दो में से एक सुझाव है:

  1. लपेटें क्या आप एक पुनः प्रयास गुंजाइश है कि किसी भी अन्य त्रुटि
  2. छिपा नहीं होगा एक आवरण विधि इंतजार कर रहा है कि जब तक आप एक धारा प्राप्त कर सकते हैं बनाने में करना चाहते हैं और कि धारा

का उपयोग एक धारा हो रही

private FileStream GetWriteStream(string path, int timeoutMs) 
{ 
    var time = Stopwatch.StartNew(); 
    while (time.ElapsedMilliseconds < timeoutMs) 
    { 
     try 
     { 
      return new FileStream(path, FileMode.Create, FileAccess.Write); 
     } 
     catch (IOException e) 
     { 
      // access error 
      if (e.HResult != -2147024864) 
       throw; 
     } 
    } 

    throw new TimeoutException($"Failed to get a write handle to {path} within {timeoutMs}ms."); 
} 

तो इस तरह इसका इस्तेमाल:

using (var stream = GetWriteStream("path")) 
{ 
    using (var writer = new StreamWriter(stream)) 
     writer.Write("test"); 
} 

पुन: प्रयास करें गुंजाइश

private void WithRetry(Action action, int timeoutMs = 1000) 
{ 
    var time = Stopwatch.StartNew(); 
    while(time.ElapsedMilliseconds < timeoutMs) 
    { 
     try 
     { 
      action(); 
      return; 
     } 
     catch (IOException e) 
     { 
      // access error 
      if (e.HResult != -2147024864) 
       throw; 
     } 
    } 
    throw new Exception("Failed perform action within allotted time."); 
} 

और फिर WithRetry का उपयोग करें (() => File.WriteAllText (Path.Combine (_directory, नाम), सामग्री));

+0

मैंने इस व्यवहार को लपेटने वाली कक्षा के लिए एक गिस्ट भी बनाया है। इस बात को ध्यान में रखें कि ऐसा करने से इसका अर्थ यह हो सकता है कि कई आर्किटेक्चर में समस्याएं हैं यदि कई कक्षाएं एक ही फाइल को एक विवादित तरीके से पढ़ रही हैं और लिख रही हैं। आप इस तरह से डेटा खोना समाप्त कर सकते हैं। https://gist.github.com/ddikman/667f309706fdf4f68b9fab2827b1bcca – Almund

+0

मुझे नहीं पता कि यह स्वीकार्य उत्तर क्यों नहीं है। कोड अधिक सुरक्षित है; 'IsFileReady' को' while 'लूप में कॉल करना, क्योंकि गॉर्डन थॉम्पसन के उत्तर से पता चलता है कि संभावित रूप से असफल हो सकता है। एक और प्रक्रिया फाइल को लॉक कर सकती है जब लूप की स्थिति जांचती है तो यह उपलब्ध होता है और आपकी प्रक्रिया वास्तव में इसका उपयोग करने की कोशिश करती है। केवल एक चीज, 'ई.एचआरसल्ट' पहुंच योग्य नहीं है क्योंकि यह 'संरक्षित' है। –

+0

समर्थन के लिए धन्यवाद हालांकि मेरे सुझाए गए समाधान तुलना में काफी खराब है। मुझे इसके बारे में बहुत कुछ पसंद नहीं है, हालांकि आपके द्वारा कुछ विकल्पों के साथ छोड़े गए ढांचे में कोई भी समर्थन नहीं है। मैं एचआरएसल्ट का उपयोग कर रहा था, हालांकि, फ्रेमवर्क संस्करणों के बीच शायद अलग हो सकता है, मुझे यकीन है कि कुछ अन्य संपत्ति है जिसका उपयोग यह पता लगाने के लिए किया जा सकता है कि IOException में कौन सी त्रुटि है। – Almund

2

यहाँ एक समाधान है जो कुछ प्रयोक्ताओं के लिए overkill हो सकता है। मैंने एक नई स्थैतिक कक्षा बनाई है जिसमें एक ऐसी घटना है जो फ़ाइल को कॉपी करने पर ही तभी ट्रिगर होती है।

उपयोगकर्ता फ़ाइलों जो वे FileAccessWatcher.RegisterWaitForFileAccess(filePath) फोन करके देखना पसंद करेंगे, पंजीकृत करता है। फ़ाइल पहले से ही देखा नहीं किया जा रहा है एक नया कार्य शुरू कर दिया है जो बार-बार फ़ाइल की जाँच करता है देखने के लिए अगर इसे खोला जा सकता है। प्रत्येक बार जब यह जांचता है तो यह फ़ाइल आकार भी पढ़ता है। यदि पूर्व-निर्धारित समय (फ़ाइल मेरे उदाहरण में 5 मिनट) में फ़ाइल का आकार नहीं बढ़ता है तो लूप निकल जाता है।

जब फ़ाइल से पाश बाहर निकलता सुलभ किया जा रहा है या समय समाप्ति से FileFinishedCopying घटना शुरू हो रहा है।

public class FileAccessWatcher 
{ 
    // this list keeps track of files being watched 
    private static ConcurrentDictionary<string, FileAccessWatcher> watchedFiles = new ConcurrentDictionary<string, FileAccessWatcher>(); 

    public static void RegisterWaitForFileAccess(string filePath) 
    { 
     // if the file is already being watched, don't do anything 
     if (watchedFiles.ContainsKey(filePath)) 
     { 
      return; 
     } 
     // otherwise, start watching it 
     FileAccessWatcher accessWatcher = new FileAccessWatcher(filePath); 
     watchedFiles[filePath] = accessWatcher; 
     accessWatcher.StartWatching(); 
    } 

    /// <summary> 
    /// Event triggered when the file is finished copying or when the file size has not increased in the last 5 minutes. 
    /// </summary> 
    public static event FileSystemEventHandler FileFinishedCopying; 

    private static readonly TimeSpan MaximumIdleTime = TimeSpan.FromMinutes(5); 

    private readonly FileInfo file; 

    private long lastFileSize = 0; 

    private DateTime timeOfLastFileSizeIncrease = DateTime.Now; 

    private FileAccessWatcher(string filePath) 
    { 
     this.file = new FileInfo(filePath); 
    } 

    private Task StartWatching() 
    { 
     return Task.Factory.StartNew(this.RunLoop); 
    } 

    private void RunLoop() 
    { 
     while (this.IsFileLocked()) 
     { 
      long currentFileSize = this.GetFileSize(); 
      if (currentFileSize > this.lastFileSize) 
      { 
       this.lastFileSize = currentFileSize; 
       this.timeOfLastFileSizeIncrease = DateTime.Now; 
      } 

      // if the file size has not increased for a pre-defined time limit, cancel 
      if (DateTime.Now - this.timeOfLastFileSizeIncrease > MaximumIdleTime) 
      { 
       break; 
      } 
     } 

     this.RemoveFromWatchedFiles(); 
     this.RaiseFileFinishedCopyingEvent(); 
    } 

    private void RemoveFromWatchedFiles() 
    { 
     FileAccessWatcher accessWatcher; 
     watchedFiles.TryRemove(this.file.FullName, out accessWatcher); 
    } 

    private void RaiseFileFinishedCopyingEvent() 
    { 
     FileFinishedCopying?.Invoke(this, 
      new FileSystemEventArgs(WatcherChangeTypes.Changed, this.file.FullName, this.file.Name)); 
    } 

    private long GetFileSize() 
    { 
     return this.file.Length; 
    } 

    private bool IsFileLocked() 
    { 
     try 
     { 
      using (this.file.Open(FileMode.Open)) { } 
     } 
     catch (IOException e) 
     { 
      var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1); 

      return errorCode == 32 || errorCode == 33; 
     } 

     return false; 
    } 
} 

उदाहरण उपयोग:

// register the event 
FileAccessWatcher.FileFinishedCopying += FileAccessWatcher_FileFinishedCopying; 

// start monitoring the file (put this inside the OnChanged event handler of the FileSystemWatcher 
FileAccessWatcher.RegisterWaitForFileAccess(fileSystemEventArgs.FullPath); 

FileFinishedCopyingEvent संभाल:

private void FileAccessWatcher_FileFinishedCopying(object sender, FileSystemEventArgs e) 
{ 
    Console.WriteLine("File finished copying: " + e.FullPath); 
} 
0

तुम एक डमी चर के साथ एक ताला बयान इस्तेमाल कर सकते हैं, और यह बहुत अच्छा काम करने के लिए लगता है।

here देखें।

0

@Gordon थॉम्पसन के जवाब का उपयोग करना, आप इस तरह के नीचे दिए गए कोड के रूप में एक पाश बनाने के लिए:

public static bool IsFileReady(string sFilename) 
{ 
    try 
    { 
     using (FileStream inputStream = File.Open(sFilename, FileMode.Open, FileAccess.Read, FileShare.None)) 
      return inputStream.Length > 0; 
    } 
    catch (Exception) 
    { 
     return false; 
    } 
} 

while (!IsFileReady(yourFileName)) ; 

मैंने पाया एक अनुकूलित रास्ता है कि मेमोरी लीक का कारण नहीं:

public static bool IsFileReady(this string sFilename) 
{ 
    try 
    { 
     using (FileStream inputStream = File.Open(sFilename, FileMode.Open, FileAccess.Read, FileShare.None)) 
      return inputStream.Length > 0; 
    } 
    catch (Exception) 
    { 
     return false; 
    } 
} 

SpinWait.SpinUntil(yourFileName.IsFileReady); 
संबंधित मुद्दे