Process.StartInfo.FileName是否接受长文件名?

时间:2009-03-05 22:16:05

标签: c#

看起来不是。

如果我将文件名转换为其短值,则Process.Start()可以正常工作。

Process runScripts = new Process();
runScripts.StartInfo.FileName = @"C:\long file path\run.cmd";
runScripts.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
runScripts.StartInfo.UseShellExecute = true;
runScripts.StartInfo.RedirectStandardOutput = false;
runScripts.Start();

上述代码失败。但...

Process runScripts = new Process();
runScripts.StartInfo.FileName = @"C:\short\file\path\run.cmd";
runScripts.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
runScripts.StartInfo.UseShellExecute = true;
runScripts.StartInfo.RedirectStandardOutput = false;
runScripts.Start();

成功。

我设法通过将长路径名转换为短路径名来解决这个问题。 但我发现这一点有点惊讶。 关于此的任何原因或背景信息?

感谢。

更新1
Microsoft .NET Framework版本2.0.50727

9 个答案:

答案 0 :(得分:5)

很奇怪,我重现了这个行为,实际上,它没有像你说的那样执行,但修改它,它起作用了:

    System.Diagnostics.Process runScripts = new System.Diagnostics.Process();
    runScripts.StartInfo.FileName = @"run.cmd";
    // new
    runScripts.StartInfo.WorkingDirectory = @"C:\long file path";
    runScripts.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
    runScripts.StartInfo.UseShellExecute = true;
    runScripts.StartInfo.RedirectStandardOutput = false;
    runScripts.Start();

答案 1 :(得分:4)

我无法重现您所描述的行为。以下代码适用于我。

string path = @"X:\Temp\long file path\run.cmd";
Process p = new Process();
p.StartInfo.FileName = path;

// Works with both true (default) and false.
p.StartInfo.UseShellExecute = true;
p.Start();

修改

我可以使用以下代码重现

  string path = @"X:\Temp\long file path";
  string file = "run.cmd";

  Process p = new Process();
  p.StartInfo.FileName = file;
  p.StartInfo.WorkingDirectory = path;
  p.StartInfo.UseShellExecute = false; // only fails with false.
  p.Start();
  return;

此节目(使用进程监视器),ConsoleApplication.vshost.exe尝试在project \ bin \ Release,System32,System,Windows,System32 \ Wbem中找到run.cmd,然后进一步进入某些(我猜)路径变量。但是,如果我设置UseShellExecute = true。

答案 2 :(得分:3)

为了重现您的问题,我使用了以下程序:

// file test.cs
using System;
using System.ComponentModel;
using System.Diagnostics;

public class Test
{
    public static int Main()
    {
        string error;
        try {
            ProcessStartInfo i = new ProcessStartInfo();
            i.FileName = @"C:\long file path\run.cmd";
            i.WindowStyle = ProcessWindowStyle.Hidden;
            i.UseShellExecute = true;
            i.RedirectStandardOutput = false;
            using (Process p = Process.Start(i)) {
                error = "No process object was returned from Process.Start";
                if (p != null) {
                    p.WaitForExit();
                    if (p.ExitCode == 0) {
                        Console.ForegroundColor = ConsoleColor.Green;
                        Console.WriteLine("OK");
                        Console.ResetColor();
                        return 0;
                    }
                    error = "Process exit code was " + p.ExitCode;
                }
            }
        }
        catch (Win32Exception ex) {
            error = "(Win32Exception) " + ex.Message;
        }
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine("Whooops: " + error);
        Console.ResetColor();
        return 1;
    }
}

代码启动一个新进程(根据您的代码示例)并报告它可能无法执行的不同方式。您可以从命令行编译程序:

c:\windows\Microsoft.NET\Framework\v2.0.50727\csc test.cs

(假设test.cs在当前目录中;它将在与test.cs相同的目录中创建test.exe)

正如预期的那样,当“C:\ long file path \ run.cmd”不存在时,程序将失败并显示:

Whooops: (Win32Exception) The system cannot find the file specified

现在让我们创建一个目录“C:\ long file path”并在其中放一个非常简单的run.cmd:

rem file run.cmd
echo I ran at %Time% > "%~dp0\run.out.txt"

但是,此时我无法重现您的失败。一旦上面的run.cmd到位,test.exe就会成功运行(即run.cmd正确执行 - 您可以通过查找新创建的文件“C:\ long file path \ run.out.txt”来验证这一点。

我在Vista x64(我的主开发机器),Windows XP SP3 x86(虚拟机),Windows Server 2008 x64(虚拟机)上运行了test.exe,它可以在任何地方运行。

您是否可以尝试在您的环境中运行上述代码并报告它是否失败?这样我们至少会建立相同的测试上下文(相同的.NET程序将尝试在同一位置为您运行相同的批处理文件)。

答案 3 :(得分:2)

我设法通过使用以下行为来实际运行它。

    private string StartProcessAndGetResult(string executableFile, string arguments)
    //NOTE executable file should be passed as string with a full path
    //For example: C:\Windows\Microsoft.NET\Framework\v3.0\Windows Communication Foundation\ServiceModelReg.exe
    //Without """ and @ signs
    {
        var result = String.Empty;
        var workingDirectory = Path.GetDirectoryName(executableFile);


        var processStartInfo = new ProcessStartInfo(executableFile, arguments)
                                   {
                                       WorkingDirectory = workingDirectory,
                                       UseShellExecute = false,
                                       ErrorDialog = false,
                                       CreateNoWindow = true,
                                       RedirectStandardOutput = true
                                   };
        var process = Process.Start(processStartInfo);
        if (process != null)
        {
            using (var streamReader = process.StandardOutput)
            {
                result = streamReader.ReadToEnd();
            }
        }
        return result;
    }

使用@或将字符串引用到引号(“”“)的解决方案在我的案例中并没有被忽略。

答案 4 :(得分:1)

尝试

runScripts.StartInfo.FileName = @"""C:\long file path\run.cmd""";

虽然我确信它是由Process类自动完成的。您确定提供了正确的路径吗?

答案 5 :(得分:1)

我有一个假设。 Win32有一个限制,用于描述文件名的字符串不能超过MAX_PATH(260字节),包括终止'\ 0'。

也许这个问题已泄露到C#中?

(我所做的测试表明,但我无法确认哪些错误。)

因此,尝试在路径前添加“\\?\”。 (反斜杠,反斜杠,问号,反斜杠) 即。

runScripts.StartInfo.FileName = @"\\?\C:\long file path\run.cmd";

有关MAX_PATH的更多详细信息:http://msdn.microsoft.com/en-us/library/aa365247.aspx

/雷夫

答案 6 :(得分:1)

(更新)

我认为我从Process.Start方法的逆向工程中发现了一个问题。我是在正确的路线上,在没有设置WorkingDirectory的情况下失败。

所以换句话说,CreateProcess需要一个有效的工作目录,而不是.NET(它应该真的从文件名中推断出它,但显然不是)

以下是证据:

namespace SoTest
{
    class Program
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool CreateProcess([MarshalAs(UnmanagedType.LPTStr)] string lpApplicationName, StringBuilder lpCommandLine, SECURITY_ATTRIBUTES lpProcessAttributes, SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, int dwCreationFlags, IntPtr lpEnvironment, [MarshalAs(UnmanagedType.LPTStr)] string lpCurrentDirectory, STARTUPINFO lpStartupInfo, PROCESS_INFORMATION lpProcessInformation);

        static void Main(string[] args)
        {
            Process process = new Process();
            process.StartInfo.FileName = @"C:\my test folder\my test.bat";

            StringBuilder cmdLine = new StringBuilder();
            cmdLine.Append(process.StartInfo.FileName);
            STARTUPINFO lpStartupInfo = new STARTUPINFO();
            PROCESS_INFORMATION lpProcessInformation = new PROCESS_INFORMATION();

            // This fails
            //string workingDirectory = process.StartInfo.WorkingDirectory;
            string workingDirectory = @"C:\my test folder\";

            CreateProcess(null, cmdLine, null, null, true, 0, IntPtr.Zero, workingDirectory, lpStartupInfo, lpProcessInformation);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    internal class PROCESS_INFORMATION
    {
        public IntPtr hProcess;
        public IntPtr hThread;
        public int dwProcessId;
        public int dwThreadId;

        public PROCESS_INFORMATION()
        {
            this.hProcess = IntPtr.Zero;
            this.hThread = IntPtr.Zero;
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    internal class SECURITY_ATTRIBUTES
    {
        public int nLength;
        public long lpSecurityDescriptor;
        public bool bInheritHandle;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal class STARTUPINFO
    {
        public int cb;
        public IntPtr lpReserved;
        public IntPtr lpDesktop;
        public IntPtr lpTitle;
        public int dwX;
        public int dwY;
        public int dwXSize;
        public int dwYSize;
        public int dwXCountChars;
        public int dwYCountChars;
        public int dwFillAttribute;
        public int dwFlags;
        public short wShowWindow;
        public short cbReserved2;
        public IntPtr lpReserved2;
        public IntPtr hStdInput;
        public IntPtr hStdOutput;
        public IntPtr hStdError;

        public STARTUPINFO()
        {
            this.lpReserved = IntPtr.Zero;
            this.lpDesktop = IntPtr.Zero;
            this.lpTitle = IntPtr.Zero;
            this.lpReserved2 = IntPtr.Zero;
            this.hStdInput = IntPtr.Zero;
            this.hStdOutput = IntPtr.Zero;
            this.hStdError = IntPtr.Zero;
            this.cb = Marshal.SizeOf(this);
        }
    }       
}

我已从原始代码中删除了SafeFileHandle,因为我们正在执行的操作不需要它。此外,没有设置启动标志,但无窗口版本需要这些标志。

答案 7 :(得分:0)

试试这个:

Process runScripts = new Process();
runScripts.StartInfo.FileName = @"""C:\long file path\run.cmd""";
runScripts.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
runScripts.StartInfo.UseShellExecute = true;
runScripts.StartInfo.RedirectStandardOutput = false;
runScripts.Start();

即。当FileName有空格时,使用FileName的带引号的字符串。

答案 8 :(得分:0)

您的代码可能因各种原因而失败,这些原因与长/短路径问题无关。您应该在问题中添加确切的异常描述(包括调用堆栈)。