从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
, 而不使用ShellExecute
。 Process.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中找到这些函数的参考,如何检查结果(成功/失败)以及STARTUPINFO
和PROCESS_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; }