При выполнении другой программы из C # мне нужно самому анализировать «командную строку» из реестра? - PullRequest
4 голосов
/ 13 октября 2009

Из реестра для данного типа файла я получаю строку, содержащую что-то вроде этого:

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

8 голосов
/ 13 октября 2009

Проблема, с которой вы сталкиваетесь, заключается в том, что имя исполняемого файла и некоторые аргументы уже содержатся в вашей переменной commandPath (которая представляет собой не только путь, но и некоторые параметры). Если бы первая часть состояла только из символов (без пробелов), было бы не сложно отделить исполняемый файл от параметров, но это Windows, поэтому у вас могут быть пробелы, поэтому вы застряли. Так кажется.

Решение в не с использованием Process.Start и не с использованием ShellExecute. Process.Start, независимо от того, просите ли вы использовать ShellExecute или CreateProcess, в обоих случаях требуется задать параметр / элемент FileName, который передается как есть в CreateProcess и ShellExecute.

Так что тогда? Скорее просто: используйте CreateProcess самостоятельно. Менее известная особенность этой функции API заключается в том, что вы можете передать ей полную командную строку, так же, как и в WinKey + R (Windows Run). "магия" , которую вы запрашиваете, может быть достигнута путем установки ее первого параметра на null и его второго параметра на полный путь, включая все параметры. Подобно следующему, которое запустит Windows Photo Gallery для вас, при использовании той же строки с параметрами с 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);

Обратите внимание, что я намеренно не включал кавычки вокруг пути к исполняемому файлу. Но если путь к исполняемому файлу заключен в кавычки, как в приведенном выше коде, он все равно будет работать, все magic есть. Добавьте к этому фрагмент кода, следующий процесс запустится так, как вы хотите:

/* 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 можно найти по адресу 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;
}

Надеюсь, я правильно понял вашу проблему. Дайте мне знать, если у вас возникли проблемы с реализацией вышеуказанного кода.

1 голос
/ 13 октября 2009

Как насчет spawning 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();
1 голос
/ 13 октября 2009

Я считаю (прошло много времени с тех пор, как я это сделал), что вы можете просто использовать:

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

и он откроет файл с приложением по умолчанию, если оно есть. Вам не нужно было знать приложение, которое оно собирается использовать.

Я понимаю, что вы ищете? Или я что-то пропустил?

0 голосов
/ 08 марта 2016

У меня была похожая проблема (синтаксический анализ 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;
    }
...