4

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

"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
4

4 回答 4

10

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

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

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

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获得的内容,但为了方便起见,这里是应该粘贴在类部分中以使上述内容生效的部分。这些函数的参考,如何检查结果(成功/失败)STARTUPINFOPROCESS_INFORMATION结构可以在微软的 MSDN中找到。为方便起见,我建议将调用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;
}

希望我正确理解了您的问题。如果您在实现上述代码时遇到问题,请告诉我。

于 2009-10-13T16:58:47.727 回答
1

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

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

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

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

于 2009-10-13T13:54:18.687 回答
1

产生 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();
于 2009-10-13T14:01:06.550 回答
0

我有一个类似的问题(从注册表解析 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;
    }
于 2016-03-07T21:45:28.660 回答