从C#执行另一个程序,我是否需要解析“命令行"?从我自己的注册表? [英] Executing another program from C#, do I need to parse the "command line" from registry myself?

查看:60
本文介绍了从C#执行另一个程序,我是否需要解析“命令行"?从我自己的注册表?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

From the registry, for a given file type, I get a string containing something like this:

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

或有时:

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

为了使我能够执行该程序,并传递文件名作为参数(我知道它接受),我是否必须自己解析此字符串,或者是否有运行时类为我执行此操作?请注意,我并不是要处理两者之间是否具有%1"的区别,而是要分割可执行文件的名称,并单独获取命令行参数.

In order for me to execute this program, and pass along a filename as a parameter (which I know it accepts), do I have to parse this string myself, or is there a runtime class that will do this for me? Note that I'm not asking about handling the difference between the two in regards to whether it has a "%1" or not, but rather I need to split off the name of the executable, get the command line arguments to it separately.

我尝试仅将文件的完整路径和名称附加/注入到上面的字符串中,然后将整个shebang传递给Process.Start,但是当然它只希望文件名作为单个参数,因此不起作用.

I tried just appending/injecting the full path to and name of the file to pass along into the string above and pass the whole shebang to Process.Start, but of course it expects just the filename as the single argument, so that doesn't work.

基本上,上述操作必须手动完成:

Basically, the above would have to be done like this manually:

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

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

I tried using UseShellExecute, but that didn't help. Any other pointers?

要清楚,我想要这个:

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

推荐答案

您面临的问题是,变量 commandPath 中的可执行文件名称和一些参数已经在一起(不仅是路径,但也有一些参数).如果第一部分仅由字符组成(没有空格),那么将可执行文件与参数分开并不是很困难,但这是Windows,因此您可能会有空格,因此会卡住.看来.

The problem you are facing is that the executable name and some arguments are already together in your variable commandPath (which is not only the path, but also some params). If the first part were only made up of characters (no spaces), it wouldn't have been too hard to separate the executable from the params, but this is Windows, so you may have spaces, so you are stuck. So it seems.

该解决方案使用 Process.Start 中,并且使用 ShellExecute 中. Process.Start ,无论您要求它使用 ShellExecute 还是 CreateProcess ,在两种情况下都需要 FileName 要设置的参数/成员,将按原样传递给CreateProcess和ShellExecute.

The solution is in not using Process.Start, and not using ShellExecute. Process.Start, whether you ask it to use ShellExecute or CreateProcess, in both cases, it requires the FileName parameter/member to be set, which is passed as-is to CreateProcess and ShellExecute.

那又如何呢?简而言之,就是:自己使用 CreateProcess .该API函数的一个鲜为人知的功能是,您可以将完整的命令行传递给它,就像在WinKey + R(Windows运行)下一样.您可以通过将其第一个参数设置为 null 并将其第二个参数设置为完整路径(包括所有参数)来实现所需的魔术" .类似于以下内容,它将为您启动 Windows Photo Gallery ,同时将相同的字符串与带有 Process.Start 的参数一起使用,以任何方式产生"找不到文件" 错误:

So what then? Rather simply put: use CreateProcess yourself. A lesser known feature of that API function is that you can pass a full commandline to it, just as you can under WinKey+R (Windows Run). The "magic" that you ask for can be achieved by setting its first param to null and its second param to the full path, including all parameters. Like the following, which will start the Windows Photo Gallery for you, while using the same string with the params with Process.Start any which way would yield a "File Not Found" error:

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);

请注意,我故意不在可执行文件路径两边加上引号.但是,如果可执行文件路径周围带有引号,就像上面的代码一样,它仍然可以使用,所有 magic 都在那里.将其与您的代码段结合起来,以下内容将以您想要的方式启动该过程:

Note that I deliberately did not include quotes around the executable path. But if the executable path has quotes around it, as with your code above, it will still work, all the magic is there. Combine that with your code snippet, the following will start the process the way you want:

/* 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 获取声明,但是为了方便起见,这是应该粘贴到类部分中的部分,以使上述内容起作用.有关这些功能的参考,如何检查结果(成功/失败)以及 STARTUPINFO PROCESS_INFORMATION 结构,请参见

The declarations are something you can get from http://www.pinvoke.net, but for convenience, here's the part that should be pasted inside the class section to get the above to work. Reference of these functions, how to check the result (success / fail) and the STARTUPINFO and PROCESS_INFORMATION structures can be found at Microsoft's MSDN here. for convenience, I recommend to place the call to CreateProcess in a utility function.

/* 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;
}

希望我已正确理解您的问题.如果您在执行上述代码时遇到问题,请告诉我.

Hope I understood your problem correctly. Let me know if you have trouble implementing the above code.

这篇关于从C#执行另一个程序,我是否需要解析“命令行"?从我自己的注册表?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆