0

I wrote program that listens on a directory (GUI - WPF). When the new file show up in this directory is sent to the printer. The problem occurs when I try to save a large file to this directory. I have to wait until the file is closed, and then send it to the printer. I have a function that checks if the file is open. But when I use it in the whole GUI hangs. How do I use this function asynchronously?

 protected void newfile(object fscreated, FileSystemEventArgs Eventocc)
        {
            try
            {
                    string CreatedFileName = Eventocc.Name;
                    FileInfo createdFile = new FileInfo(CreatedFileName);
                    string extension = createdFile.Extension;
                    string eventoccured = Eventocc.ChangeType.ToString();
                    fsLastRaised = DateTime.Now;

                        this.Dispatcher.Invoke((Action)(() =>
                        {
                            String file = "";
                            file = watchingFolder + "\\" + CreatedFileName;                                  
                            //printing
                            this.Dispatcher.Invoke((Action)(() =>
                            {
                                FileInfo info = new FileInfo(file);
                                while (!IsFileReady(info)) { }
                                var t = new Thread(() => printFile(file, extension)); //send to printer
                                t.Start();
                            }));

                        }));
                    }

            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show("Error");
            }
        }

IsFileReady function:

 public static bool IsFileReady(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;
        }

And printfile

 public void printFile(string filepath, string ext)
        {
                    ProcessStartInfo info = new ProcessStartInfo();
                    info.Verb = "print";
                    info.FileName = filepath;
                    info.CreateNoWindow = true;
                    info.WindowStyle = ProcessWindowStyle.Hidden;

                    Process p = new Process();
                    p.StartInfo = info;
                    p.Start();

                    p.WaitForInputIdle();
                    System.Threading.Thread.Sleep(3000);

                    if (false == p.CloseMainWindow())
                        p.Kill();

                }
        }

How can I correct this code to work with large files without hangs up?

EDIT:

For check new file I use FileSystemWatcher

private void start(object sender, RoutedEventArgs e)
        {


            if (watchingFolder == null)
            {

            }
            else
            {
                fs = new FileSystemWatcher(watchingFolder, "*.*");
                fs.EnableRaisingEvents = true;
                fs.IncludeSubdirectories = true;
                fs.Created += new FileSystemEventHandler(newfile);
                btnSatrt.IsEnabled = false;
                btnStop.IsEnabled = true;

            }
        }
lukassz
  • 3,135
  • 7
  • 32
  • 72
  • I use this class, @TimRutter I update my code – lukassz Jan 27 '17 at 14:22
  • Possible duplicate of [Wait Until File Is Completely Written](http://stackoverflow.com/questions/10982104/wait-until-file-is-completely-written) – Tim Rutter Jan 27 '17 at 14:28
  • while (!IsFileReady(info)) { } You should use some System.thread.sleep(xxx ms) insteed of running in a crazy loop ? The IsFileReady could also be run prior all other actions => new FileInfo(CreatedFileName); – Laurent Lequenne Jan 27 '17 at 14:51

3 Answers3

1

You're executing while (!IsFileReady(info)) { } through Dispatcher.Invoke, that executes the code on the UI thread so it will block for sure the app.

You aren't interacting at all with the UI, so the correct approach is to execute it asynchronously, via Tasks and awaits or with a background thread via the ThreadPool and not using at all Dispatcher.Invoke.

Gusman
  • 14,905
  • 2
  • 34
  • 50
0

Try to execute all code in the newfile event handler on a background thread by starting a new task:

protected async void newfile(object fscreated, FileSystemEventArgs Eventocc)
{
    try
    {
        await Task.Run(() =>
        {
            string CreatedFileName = Eventocc.Name;
            FileInfo createdFile = new FileInfo(CreatedFileName);
            string extension = createdFile.Extension;
            string eventoccured = Eventocc.ChangeType.ToString();
            fsLastRaised = DateTime.Now;

            string file = watchingFolder + "\\" + CreatedFileName;
            FileInfo info = new FileInfo(file);
            while (!IsFileReady(info)) { }
            printFile(file, extension);
        });
    }
    catch (Exception ex)
    {
        System.Windows.Forms.MessageBox.Show("Error");
    }
}
mm8
  • 163,881
  • 10
  • 57
  • 88
  • Ok, I try this. How cat I chage `while (!IsFileReady(info)) { }`? It's don't look good. – lukassz Jan 27 '17 at 14:59
  • You can check whether the file exists using the File.Exists method: https://msdn.microsoft.com/en-us/library/system.io.file.exists(v=vs.110).aspx. But you should still catch any excpetions that may occur. If the file actually exists you should be able to read it using a FileStream class even if the file is in use by another process: http://stackoverflow.com/questions/9759697/reading-a-file-used-by-another-process. But please ask a new question if you have another issue. Your original question was about not blocking the UI thread. – mm8 Jan 27 '17 at 15:06
  • I get `task does not contain a definition for run` error with this code. Wrong .Net version? – lukassz Jan 29 '17 at 21:41
  • Ok, I change .net to 4.5 but when file is created I get `the calling thread can not access this object because it belongs to another thread` – lukassz Jan 29 '17 at 21:46
  • On what line are you getting this exception? – mm8 Jan 30 '17 at 10:35
0

Use a BackgroundWorker instead of Dispatcher.Invoke.