-1

I am working on a file-watcher project using C# and I am trying to copy changes files into new created files, but I am getting the error: Illegal characters in the path as shown below enter image description here

I think the problem comes from the white space before .4, which I have underlined in yellow. I am trying to get rid of this white space but I can't. The code is down below

    private void OnChanged(object source, FileSystemEventArgs e)
    {

        if (e.ChangeType == WatcherChangeTypes.Changed || e.ChangeType == WatcherChangeTypes.Created)
        {
            String targetname = null;
            String fn = "";
            try
            {
                bool copy = true;
                targetname = GetShortcutTarget(e.FullPath);
                if (targetname == null)
                {
                    Console.WriteLine($"File: {e.FullPath} {e.ChangeType}");
                }
                else
                {
                    c += 1;
                    fn = targetname.Replace('\\', '_').Replace(":", "_c_");
                    DateTime lmt = File.GetLastWriteTime(targetname);
                    if (lastSeen.ContainsKey(targetname))
                    {
                        if (lastSeen[targetname] < lmt)
                        {
                            copy = true;
                        }
                        else
                        {
                            copy = false;
                        }
                    }
                    else
                    {
                        lastSeen[targetname] = lmt;
                        copy = true;
                    }
                    if (copy)
                    {
                        Console.WriteLine($"Filec: {e.FullPath} {targetname} {e.ChangeType}");
                        //HERE
                        System.IO.File.Copy(targetname, $"{_lfile}\\{fn}.{c}", true);
                        // Only keep copy if copy worked
                        latest[fn] = c;
                    }
                    else
                    {
                        Console.WriteLine($"Have {e.FullPath} {targetname}");
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Failure to copy {e.FullPath} {targetname} {$"{_lfile}\\{fn}.{c}"} {ex.Message}");
            }
        }
        else
        {
            Console.WriteLine($"File: {e.FullPath} {e.ChangeType}");
        }
    }

I have noted a "HERE" comment to show you where the name of the file is created. I have tried many methods like Trim, Replace but it doesn't work. Can you please help me? I would be really grateful.

enter image description here

  namespace FileWatcher
{
    class Program
    {
        static void Main(string[] args)
        {
            String userName;
            String expt;
            if (args.Length < 2)
            {
                Console.WriteLine($"FileWatcher <user> <exptName>");
                Console.WriteLine($"Captures files into /temp/<exptName>-log and /temp/<exptName>-files");
                userName = "lashi";
                expt = "expt1";

        }
        else
        {
            userName = args[0];
            expt = args[1];
        }
        String lexpt = $"c:\\temp\\{expt}-log";
        String fexpt = $"c:\\temp\\{expt}-file";

        if (!Directory.Exists(fexpt))
        {
            Directory.CreateDirectory(fexpt);
        }
        if (!Directory.Exists(lexpt))
        {
            Directory.CreateDirectory(lexpt);
        }
        Watcher w = new Watcher(lexpt, fexpt, userName);
        w.Watch();
    }
}

class Watcher
{

    Dictionary<String, int> latest = new Dictionary<String, int>();

    String _username;
    String _ext;
    String _lfile;
    String _ffile;
    int c = 0;
    Dictionary<String, DateTime> lastSeen;


    public Watcher(String lfile, String fFile, String uName)
    {
        _username = uName;
        _ext = "*";
        _lfile = lfile;
        _ffile = fFile;
        lastSeen = new Dictionary<string, DateTime>();
        Console.CancelKeyPress += copyFiles;
    }

    public void copyFiles(object sender, ConsoleCancelEventArgs e)
    {
        Console.WriteLine("Acting on ctrl-c");
        copyFiles();
    }

    public void copyFiles()
    {
        foreach (KeyValuePair<string, int> entry in latest)
        {
            try
            {

                System.IO.File.Copy($"{_lfile}\\{entry.Key}.{entry.Value}", $"{ _ffile}\\{entry.Key}", true);
            }
            catch (Exception e)
            {
                Console.WriteLine($"Failure to take last copy {entry.Key} {entry.Value}");
            }
        }
    }

    public void Watch()
    {
        using (FileSystemWatcher watcher = new FileSystemWatcher($"C:\\Users\\{_username}\\AppData\\Roaming\\Microsoft\\Windows\\Recent", _ext))
        {
            //           watcher.Path = args[1];

            // Watch for changes in LastAccess and LastWrite times, and
            // the renaming of files or directories.
            watcher.NotifyFilter = NotifyFilters.LastAccess
                                 | NotifyFilters.LastWrite
                                 | NotifyFilters.FileName
                                 | NotifyFilters.DirectoryName;

            // Only watch text files.
            // watcher.Filter = "*.txt";
            watcher.IncludeSubdirectories = false;
            // Add event handlers.
            watcher.Changed += OnChanged;
            watcher.Created += OnChanged;
            watcher.Deleted += OnChanged;
            watcher.Renamed += OnRenamed;


            // Begin watching.
            watcher.EnableRaisingEvents = true;

            // Wait for the user to quit the program.
            Console.WriteLine("Press 'q' to quit the sample.");
            while (Console.Read() != 'q') ;
        }
        // After a q then 
    }

Failure to copy C:\Users\lashi\AppData\Roaming\Microsoft\Windows\Recent\MSIX+VHD.docx.lnk C:\Users\lashi\Desktop\Klera_Internship\MSIX+VHD.docx  c:\temp\expt1-log\C_c__Users_lashi_Desktop_Klera_Internship_MSIX+VHD.docx .1 Illegal characters in path.
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
Will Wost
  • 105
  • 2
  • 9
  • 3
    `System.IO.File.Copy(targetname, $"{_lfile}\\{fn}.{c}", true);` a) Store `$"{_lfile}\\{fn}.{c}"` in a variable and then pass it in. b) Share with us the exact value of `targetname` and the new variable you created in step a). **DO NOT GUESS**. – mjwills May 26 '21 at 21:59
  • 1
    @mjwills I have already tried to store the whole string in a new variable but it didn't work. Do you want me to share the final value from the terminal or just some code? – Will Wost May 26 '21 at 22:07
  • 1
    @mjwills If we consider the example from the picture I have added the target name is: C:\Users\aaaa\Desktop\aaaa_Internship and the file that follows is the new file I have created where I copy the changes from an already existing file on my computer. – Will Wost May 26 '21 at 22:16
  • We're looking for the redacted portions. No file-path is confidential even in OPSEC environments. You should be able to release the path so we can further diagnose the issue. – DekuDesu May 26 '21 at 22:28
  • 2
    Please provide the values of `targetname`, `_lfile`, `fn`, and `c` for an instance when your method fails. – Jonathan May 26 '21 at 22:30
  • I just added another picture from the result. – Will Wost May 26 '21 at 22:32
  • @WillWost Have you checked to see if the path that is causing the method to fail contains [`Path.GetInvalidPathChars`](https://learn.microsoft.com/en-us/dotnet/api/system.io.path.getinvalidpathchars?view=net-5.0) (this isn't inclusive but should rule out some common problem chars) – DekuDesu May 26 '21 at 22:32
  • The second directory is the target name and the third one is the new file created. – Will Wost May 26 '21 at 22:32
  • @WillWost Have you tried checking [`Char.IsWhiteSpace`](https://learn.microsoft.com/en-us/dotnet/api/system.char.iswhitespace?view=net-5.0) on that problem character to see if you can identify and remove it that way? – DekuDesu May 26 '21 at 22:34
  • @DekuDesu No I haven't tried this, but normally the only illegal character there is the whitespace isnt't it? – Will Wost May 26 '21 at 22:34
  • No illegal characters also include things like `", <, >, |` – DekuDesu May 26 '21 at 22:34
  • I just added another piece of code so maybe you can understand better what my code is doing. – Will Wost May 26 '21 at 22:38
  • Could you update your post to include a **direct** copy/paste of that full error and paths? – DekuDesu May 26 '21 at 22:49
  • I just added a copy/paste of it – Will Wost May 26 '21 at 22:50
  • 1
    You understand that when you call `Replace` on `targetName` that this returns a *copy* of `targetName` with the replacement made, and does NOT change `targetName` itself, right? – Eric Lippert May 26 '21 at 23:36
  • 2
    Also I would strongly encourage you to break the program down into smaller functions *each of which can be tested independently*. Can you write a method `static string FixPath(string path)` which you can then implement and write test cases for? That way you can narrow down the problem by creating subsystems that you have confidence in. – Eric Lippert May 26 '21 at 23:38
  • File paths can have as many spaces and dots as they want. The problem is the string interpolation and `.Replace('\\', '_').Replace(":", "_c_")`. These two produce an invalid path but since you don't show that result anywhere, it's impossible to guess. It's also very hard to guess what this code is trying to do. I strongly suggest cleaning up the code and using the `Path` methods like `Path.GetFileName`or `Path.Combine` to create new paths. – Panagiotis Kanavos May 27 '21 at 14:01

1 Answers1

0

I don't see you actually trying to strip whitespace, nor do I see where you're getting rid of those brackets. I also didn't like typing out your paths -- I hope I didn't get them wrong. Maybe you can see now why everyone was asking you to provide actual text for your values, not screenshots...

Personally, I would make use of regex and limit characters to alphanumeric and maybe a couple of other special characters (like underscore). You can modify the regex as needed and add additional special characters. There is a risk, however, that you will get 2 files that don't have the same original names, but do have the same target names (eg 'test one.jpg' and 'testone.jpg), so you should think about that.

Perhaps an approach like this may be better:

using System;
using System.Text.RegularExpressions;
                    
public class Program
{
    public static void Main()
    {
        var fn = "";
        var targetname = @"C:\Users\lashi\Desktop\Klera_Internship (2).lnk";
        var _lfile = @"c:\temp\expt1-log";
        var c = 12; // arbitrary int

        // what you're doing now... I don't see you trying to clean any spaces, nor brackets:
        fn = targetname.Replace('\\', '_').Replace(":", "_c_");

        Console.WriteLine($"Target Before: {targetname}");
        Console.WriteLine($"Destination Before: {_lfile}\\{fn}.{c}");
        
        // what you could do with a few more replacements:
        fn = targetname.ToSafeFilename();
        
        Console.WriteLine($"Target After: {targetname}");
        Console.WriteLine($"Destination After: {_lfile}\\{fn}.{c}");
        
    }
}
public static class StringExtensions{
    public static string ToSafeFilename(this string inString)
    {
        inString = Regex.Replace(inString, @"\\", "_");
        inString = Regex.Replace(inString, @":", "_c_");
        return Regex.Replace(inString, @"[^0-9a-zA-Z_]+", "");
    }
}

Output:

//Target Before: C:\Users\lashi\Desktop\Klera_Internship (2).lnk
//Destination Before: c:\temp\expt1-log\C_c__Users_lashi_Desktop_Klera_Internship (2).lnk.12
//Target After: C:\Users\lashi\Desktop\Klera_Internship (2).lnk
//Destination After: c:\temp\expt1-log\C_c__Users_lashi_Desktop_Klera_Internship2lnk.12

See:

https://dotnetfiddle.net/MfTyYo

Jonathan
  • 4,916
  • 2
  • 20
  • 37
  • Thank you so much Jonathan for taking your time to answer to my question. I have tried to get rid of the white spaces, but as it didn't work I just removed that piece of code. That is why you don't see it. – Will Wost May 26 '21 at 23:27
  • @WillWost does what I posted above resolve your issue? – Jonathan May 27 '21 at 14:47