从C#执行另一个程序,我是否需要自己解析注册表中的“命令行”?

从注册表中,对于给定的文件类型,我得到一个包含以下内容的字符串:

"C:\Program Files\AppName\Executable.exe" /arg1 /arg2 /arg3 

或有时:

 "C:\Program Files\AppName\Executable.exe" /arg1 /arg2 /arg3 "%1" 

为了让我执行这个程序,并传递一个文件名作为参数(我知道它接受),我是否必须自己解析这个字符串,或者是否有一个运行时类为我做这个? 请注意,我不是要求处理两者之间是否有“%1”的区别,而是我需要拆分可执行文件的名称,分别获取命令行参数。

我尝试只是附加/注入文件的完整路径和文件的名称传递到上面的字符串并将整个shebang传递给Process.Start,但当然它只需要文件名作为单个参数,所以这不是工作。

基本上,上面必须像这样手动完成:

 Process proc = new Process(); proc.StartInfo.FileName = @"C:\Program Files\AppName\Executable.exe"; proc.StartInfo.Arguments = "/arg1 /arg2 /arg3 \"" + fileName + "\""; proc.Start(); 

我尝试使用UseShellExecute ,但这没有帮助。 还有其他指针吗?

要清楚,我想要这个:

 String commandPath = ReadFromRegistry(); String fullCommand = commandPath + " " + fileName; // assuming not %1 Process.Start(fullCommand); // <-- magic happens here 

您遇到的问题是可执行文件名和一些参数已经在您的变量commandPath (它不仅是路径,而且还有一些参数)。 如果第一部分只是由字符组成(没有空格),那么将可执行文件与params分开并不是很难,但这是Windows,所以你可能有空格,所以你被卡住了。 所以它看起来。

解决方案是使用Process.Start而不使用ShellExecuteProcess.Start ,无论你是要求它使用ShellExecute还是CreateProcess ,在这两种情况下,它都需要设置FileName参数/成员,它按原样传递给CreateProcess和ShellExecute。

那么呢? 而是简单地说:自己使用CreateProcess 。 该API函数的一个鲜为人知的特性是您可以将完整的命令行传递给它,就像在WinKey + R(Windows运行)下一样。 您要求的“魔法”可以通过将其第一个参数设置为null并将其第二个参数设置为完整路径(包括所有参数)来实现。 如下所示,它将为您启动Windows照片库 ,同时使用与Process.Start的params相同的字符串,任何方式都会产生“找不到文件”错误:

 STARTUPINFO si = new STARTUPINFO(); PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); CreateProcess( /* app name */ null, /* cmd line */ @"C:\Program Files\Windows Photo Gallery\WindowsPhotoGallery.exe testBogusParam", /* proc atts */ IntPtr.Zero, /* thread atts */ IntPtr.Zero, /* inh handles */ false, /* create flags */ 0, /* env ptr */ IntPtr.Zero, /* current dir */ null, /* startupinfo */ ref si, /* processinfo */ out pi); 

请注意,我故意不在可执行文件路径中包含引号。 但是如果可执行路径有引号,就像上面的代码一样,它仍然有用,所有的魔力就在那里。 将其与您的代码段相结合,以下将按您希望的方式启动该过程:

 /* with your code */ String commandPath = ReadFromRegistry(); String fullCommand = commandPath + " " + fileName; // assuming not %1 STARTUPINFO si = new STARTUPINFO(); PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); CreateProcess( null, fullCommand, IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, null, ref si, out pi); 

声明是你可以从http://www.pinvoke.net获得的,但为了方便起见,这里应该粘贴在类部分中以使上述工作正常。 可以在Microsoft的MSDN中找到这些函数的参考,如何检查结果(成功/失败)以及STARTUPINFOPROCESS_INFORMATION结构。 为方便起见,我建议在实用程序函数中调用CreateProcess

 /* place the following at the class level */ [DllImport("kernel32.dll")] static extern bool CreateProcess( string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); public struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public uint dwProcessId; public uint dwThreadId; } public struct STARTUPINFO { public uint cb; public string lpReserved; public string lpDesktop; public string lpTitle; public uint dwX; public uint dwY; public uint dwXSize; public uint dwYSize; public uint dwXCountChars; public uint dwYCountChars; public uint dwFillAttribute; public uint dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } 

希望我能正确理解你的问题。 如果您在实施上述代码时遇到问题,请与我们联系。

我相信(我做了这个已经有一段时间了)你可以使用:

 System.Diagnostics.Process.Start(/*File to open*/); 

它将使用默认应用程序打开文件(如果有)。 您不需要知道它将要使用的应用程序。

我明白你在看什么吗? 还是我错过了什么?

如何产生cmd.exe / C“你的字符串”

即 – 像

 Process proc = new Process(); proc.StartInfo.FileName = "cmd.exe"; proc.StartInfo.Arguments = @"/C ""C:\Program Files\AppName\Executable.exe"" /arg1 /arg2 /arg3 """ + fileName + """"; proc.Start(); 

我遇到了类似的问题(从注册表解析ClickOnce UninstallString以使用System.Diagnostics.Process执行)。 我通过从卸载字符串末尾删除令牌来解决它, 直到我能够检测到有效的文件路径

  public static string GetExecutable(string command) { string executable = string.Empty; string[] tokens = command.Split(' '); for (int i = tokens.Length; i >= 0; i--) { executable = string.Join(" ", tokens, 0, i); if (File.Exists(executable)) break; } return executable; }