Search code examples
c#filesystemwatcher

Using file.move to rename new files in C#


I am very new to coding and I'm writing an application which will rename files by appending milliseconds to the existing filename of files which have been scanned in from an MFD. The folder is a shared folder and the renamed file should stay within it and not be copied elsewhere.

From doing a lot of searching, I know that File.Move is my way forward, however, I can't seem to get it to work.

Here's my code:

private void MonitorToggle_Click(object sender, EventArgs e)
    {
       // Create a new FileSystemWatcher object.
        FileSystemWatcher fsWatcher = new FileSystemWatcher();

        switch (MonitorToggle.Text)
        {
            // Start Monitoring…
            case startMonitoring:
                if (!FilePathField.Text.Equals(String.Empty))
                {
                    //Set the watched folder path equal to the file path variable
                    fsWatcher.Path = FilePathField.Text;

                    // Set Filter.
                    fsWatcher.Filter = (FileTypeField.Text.Equals(String.Empty))? "*.*" : FileTypeField.Text;

                    // Monitor files and subdirectories.
                    fsWatcher.IncludeSubdirectories = true;

                    // Monitor all changes specified in the NotifyFilters.
                    fsWatcher.NotifyFilter = NotifyFilters.LastWrite;

                    fsWatcher.EnableRaisingEvents = true;

                    // Raise Event handlers.
                    fsWatcher.Changed += new FileSystemEventHandler(OnChanged);
                    fsWatcher.Created += new FileSystemEventHandler(OnCreated);
                }
                else
                {
                    MessageBox.Show("Please select a folder to monitor.", "Warning",MessageBoxButtons.OK, MessageBoxIcon.Warning );
                }
                break;

            // Stop Monitoring…
            case stopMonitoring:
            default:

                fsWatcher.EnableRaisingEvents = false;
                fsWatcher = null;
                break;
        }

    }

    public void OnChanged (object sender, FileSystemEventArgs e)
    {
        FileInfo file = new FileInfo(e.Name);
        string dateStamp = DateTime.Now.ToString("fff");
        string fName = file.Name;
        string newFile = string.Concat(fName, dateStamp);
        File.Move(fName,newFile);            
    }

    public void OnCreated(object sender, FileSystemEventArgs e)
    {
        FileInfo file = new FileInfo(e.Name);
        string dateStamp = DateTime.Now.ToString("fff");
        string fName = file.Name;
        string newFile = string.Concat(fName, dateStamp);
        File.Move(fName, newFile);
    }

    private void BrowseButton_Click(object sender, EventArgs e)
    {
        // Create FolderBrowserDialog object.
        FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
        // Show a button to create a new folder.
        folderBrowserDialog.ShowNewFolderButton = true;
        DialogResult dialogResult = folderBrowserDialog.ShowDialog();
        // Get selected path from FolderBrowserDialog control.
        if (dialogResult == DialogResult.OK)
        {
            FilePathField.Text = folderBrowserDialog.SelectedPath;
            Environment.SpecialFolder root = folderBrowserDialog.RootFolder;
        }
    }

Whenever I create a new file in the folder I'm watching, it does absolutely nothing. At first I thought it may be because I only had the "OnCreated" method, so I then copied it into the "OnChanged" method (I wasn't sure if copying an existing file into the folder counted as it being "created" but I've had no luck).

Out of interest, I'm also getting an exception thrown if I don't specify a type in the filter, but that's much less pressing at the minute.

If anyone can offer any pointers of where I may be going wrong, it'd be greatly appreciated.


Solution

  • There are a few problems with your code.

    First, you should use e.FullPath instead of e.Name, otherwise the code will try to rename the file in the current directory, instead of watched directory.

    Second, to receive Created event you should include NotifyFilters.FileName.

    However, this will not help you much, because the file is locked in the created and changed events until the file is copied, so you'll get an exception. Also, you'll probably receive more than one Changed event (in my tests I always receive two, regardless of the file size, but it may be different on the different versions of Windows or .Net framework).

    To fix this, you may use timers or threads to accomplish the task. Here's an example using ThreadPool thread. Whenever created is fired, you create a new thread. In the thread you check whether a file is locked (trying to open file), and when the file is unlocked, do the rename.

    public class FileMon
    {
        public static void Run()
        {
            FileSystemWatcher fsWatcher = new FileSystemWatcher();
            fsWatcher.Path = @"C:\Test\";        
            fsWatcher.Filter = "*.*" ;
            fsWatcher.IncludeSubdirectories = true;
    
            // Monitor all changes specified in the NotifyFilters.
            fsWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName ;
    
            fsWatcher.EnableRaisingEvents = true;
    
            // Raise Event handlers.
            fsWatcher.Changed += OnChanged;
            fsWatcher.Created += OnCreated;
            Console.WriteLine("[Enter] to end"); Console.ReadLine();
            fsWatcher.EnableRaisingEvents = false;
        }
    
        static void Worker(object state)
        {
            FileSystemEventArgs fsArgs = state as FileSystemEventArgs;
            bool done = false;
            FileInfo fi = new FileInfo(fsArgs.FullPath);
    
            do
            {
                try
                {
                    using (File.Open(fsArgs.FullPath, FileMode.Open))
                    {
                        done = true;
                    }
                }
                catch
                {
                    done = false;
                }
                Thread.Sleep(1000);
            } while (!done);
            Console.WriteLine("DOne");
            string dateStamp = DateTime.Now.ToString("fff");
            string fName = fi.FullName;
            string newFile = fsArgs.FullPath + dateStamp;
            File.Move(fsArgs.FullPath, newFile);
        }
    
        private static void OnCreated(object sender, FileSystemEventArgs e)
        {
            Console.WriteLine($"Created {e.ChangeType} : {e.Name}");
            ThreadPool.QueueUserWorkItem(Worker, e);
        }
    
        static void OnChanged(object sender, FileSystemEventArgs e)
        {
            Console.WriteLine($"{e.ChangeType} : {e.FullPath}");
        }
    }