相关文章推荐
低调的枇杷  ·  Solved: Select Unique ...·  7 月前    · 
腼腆的针织衫  ·  这壶黄芩茶 ...·  2 年前    · 
威武的蜡烛  ·  database is locked · ...·  2 年前    · 

System.Diagnostics.Process long paths issues #58492

@krwq

Description

Couple of issues related to using long paths with System.Diagnostics.Process

In all cases below I've used following ProcessStartInfo options:

ProcessStartInfo si = new();
si.WorkingDirectory = dir; // depending on case this line might have been skipped
si.FileName = file;
si.ArgumentList.Add("foo");
si.RedirectStandardOutput = true;
si.RedirectStandardError = true;
si.CreateNoWindow = true;
si.UseShellExecute = false;
// and then process started with:
Process process = Process.Start(si);
  • Starting a process by passing FileName into ProcessStartInfo where file name is a full path longer than MAX_PATH regardless of prefixing path with \\?\ always ends up with exception:
  • Click to see exception details When using `\\?\` prefix:
    Unhandled exception. System.ComponentModel.Win32Exception (206): The filename or extension is too long.
       at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo) in System.Diagnostics.Process.dll:token 0x600016a+0x37c
       at System.Diagnostics.Process.Start() in System.Diagnostics.Process.dll:token 0x600013f+0xab
       at System.Diagnostics.Process.Start(ProcessStartInfo startInfo) in System.Diagnostics.Process.dll:token 0x6000143+0x1b
       at ConsoleApp40.Program.Main(String[] args) in C:\Users\kwicher\source\repos\ConsoleApp40\x\ConsoleApp40\Program.cs:line 77
    

    When using path directly:

    Unhandled exception. System.ComponentModel.Win32Exception (2): The system cannot find the file specified.
       at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo) in System.Diagnostics.Process.dll:token 0x600016a+0x37c
       at System.Diagnostics.Process.Start() in System.Diagnostics.Process.dll:token 0x600013f+0xab
       at System.Diagnostics.Process.Start(ProcessStartInfo startInfo) in System.Diagnostics.Process.dll:token 0x6000143+0x1b
       at ConsoleApp40.Program.Main(String[] args) in C:\Users\kwicher\source\repos\ConsoleApp40\x\ConsoleApp40\Program.cs:line 77
    It's possible to workaround this issue by using `GetShortPathName`, i.e. (in this example shorten the path by using `GetFileName` method):
    Click to expand code
    
            [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
            private static unsafe extern int GetShortPathName(char* longPath, char* shortPath, int numChars);
            public static unsafe string GetFileName(string longPath)
                longPath = Path.GetFullPath(longPath);
                const int MAX_PATH = 260;
                if (longPath.Length < MAX_PATH)
                    return longPath;
                if (!longPath.StartsWith(@"\\?\"))
                    longPath = @"\\?\" + longPath;
                fixed (char* longPathBuff = longPath)
                    int size = GetShortPathName(longPathBuff, null, 0);
                    if (size == 0)
                        // error, best attempt is to at least try with that path:
                        return longPath;
                    char[] buff = new char[size];
                    fixed (char* shortPathBuff = buff)
                        if (GetShortPathName(longPathBuff, shortPathBuff, size) == 0)
                            // error, best attempt:
                            return longPath;
                    return new string(buff);
    
  • When starting a process by passing just a file name (without directory) to FileName and directory to WorkingDirectory in the ProcessStructInfo this always throws an exception as well:
  • Click to see exception details

    Regardless of using \\?\ prefix you get the same exception:

    Unhandled exception. System.ComponentModel.Win32Exception (267): The directory name is invalid.
       at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo) in System.Diagnostics.Process.dll:token 0x600016a+0x37c
       at System.Diagnostics.Process.Start() in System.Diagnostics.Process.dll:token 0x600013f+0xab
       at System.Diagnostics.Process.Start(ProcessStartInfo startInfo) in System.Diagnostics.Process.dll:token 0x6000143+0x1b
       at ConsoleApp40.Program.Main(String[] args) in C:\Users\kwicher\source\repos\ConsoleApp40\x\ConsoleApp40\Program.cs:line 78
    

    Possibly same workaround as above could work to fix this although have not tried.

  • When using workaround from the first bullet point the arguments passed to ProcessStartInfo disappear, meaning that args in main has zero length even though arguments were passed - I have not digged into why this happens or where do they get lost
  • I've tried on netcoreapp3.1, net5.0 and net6.0 (preview) and seeing the same results.