Запуск процесса без кражи фокуса (С#)

Мне нужно иметь возможность запускать процессы (как консольные, так и оконные) без кражи фокуса. Единственный способ в .NET Framework, который я нашел для этого, - это Microsoft.VisualBasic.Interaction.Shell с Microsoft.VisualBasic.AppWinStyle. [Минимизированный | Нормальный] NoFocus (который отображается в SW_SHOWMINNOACTIVE/SW_SHOWMA, который передается в ShellExecute).

В текущей версии моего кода (который делает кражу фокуса) я использую System.Diagnostics.Process и полагаюсь на некоторые функции, которые дают мне, чего не имеет метод Interaction.Shell.

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

1.) Правильно ли, что у меня нет выбора, кроме как самостоятельно обернуть CreateProcess или ShellExecuteEx, или я пропустил какое-то другое решение? Я действительно надеялся избежать этого, так как Process - это такая полная и полезная оболочка, кроме этого надзора, и было бы так много возможностей для реализации, вызовы P/Invoke для отладки и всевозможные разнообразные боли.

2.) Почему одна команда в Microsoft создала такую ​​(в противном случае) полную оболочку, а затем исключила бы половину возможных значений из ProcessWindowStyle, в то время как другая команда создала аналогичную оболочку, которая была намного менее полной, но при условии, что все полезные стили окна?

Ответы

Ответ 1

Команда VB.Net сделала гораздо больше, чтобы облегчить работу разработчика в отношении аппаратуры окон, и я не вижу проблемы с добавлением ссылки на dll VB и использую ее в вашей программе на С#.

Это две команды с разным фокусом, все. И вы не должны плохо относиться к использованию Microsoft.VisualBasic.Interaction.Shell, если он решает вашу проблему.

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

[Edit - добавленный пример кода после комментария, чтобы показать, что вы можете комбинировать Interaction.Shell и Process]

int pid = Interaction.Shell("notepad.exe", AppWinStyle.NormalFocus);
Process p = Process.GetProcessById(pid);
p.Exited += ((o, e) => Console.WriteLine("Exit"));
p.EnableRaisingEvents = true;
Console.ReadLine();

Здесь я использую метод Shell, чтобы начать процесс, получить дескриптор процесса из pid и зацепить события. Вы можете даже сделать p.Kill(), чтобы прервать процесс.

[Изменить - обходной путь для cmd.exe]

Он начинает становиться немного хакерским на мой вкус, но он работает. Замените "NEWWINDOW" на случайный указатель или что-то, чтобы сделать его уникальным.

Microsoft.VisualBasic.Interaction.Shell(@"cmd.exe /c ""start cmd.exe /k title NEWWINDOW""", AppWinStyle.NormalFocus);
foreach (var process in Process.GetProcessesByName("cmd"))
{
    if (process.MainWindowTitle.EndsWith("NEWWINDOW"))
    {
        process.Exited += ((o, e) => Console.WriteLine("Exit"));
        process.EnableRaisingEvents = true;
    }
}

Ответ 2

Посмотрите здесь:

System.Diagnostics.ProcessStartInfo procInfo = new System.Diagnostics.ProcessStartInfo();
procInfo.CreateNoWindow = true;
procInfo.UseShellExecute = true;
procInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procInfo;
proc.EnableRaisingEvents = true;
proc.Exited += new EventHandler(proc_Exited);
proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);
proc.Start(...)
// Do something with proc.Handle...
void  proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
   /* Do something here... */
}

void  proc_Exited(object sender, EventArgs e)
{
/* Do something here... */
}

Изменить: Я изменил код, чтобы показать средства для создания событий и обработки их, также я показал использование свойства Handle, которое является дескриптором процесса, который является работает.