Запуск процесса без кражи фокуса (С#)
Мне нужно иметь возможность запускать процессы (как консольные, так и оконные) без кражи фокуса. Единственный способ в .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
, которое является дескриптором процесса, который является работает.