Как реализовать средство запуска wpf с помощью SetForegroundWindow
Я пытаюсь реализовать команду кнопки, которая запускает новое приложение WPF при первом нажатии пользователем кнопки, а затем (когда пользователь нажимает кнопку снова) отправляет ее на передний план, если она уже запущена. Все это работает на .Net v4.0
То, что я пытался сделать, это работает нормально, как и ожидалось, когда запущенный процесс является обычным приложением WPF, но это не очень хорошо, если запущенное приложение WPF имеет заставку. Проблема в том, что SetForegroundWindow
не удается, потому что я не могу получить правильный дескриптор окна в этом конкретном случае. Можете ли вы предложить исправить или обойти? Предположим, вы можете изменить источник как запускающего, так и запущенного WPF.
Соответствующий код из View Model модуля запуска
private void ClaimRptLogic()
{
if (ClaimRptHandle != IntPtr.Zero)
{
ShowWindow(ClaimRptHandle, SW_RESTORE);
LaunchState = SetForegroundWindow(ClaimRptHandle)? "" : "can't set to foreground";
return;
}
Process rpt = new Process();
rpt.StartInfo = new ProcessStartInfo()
{
WorkingDirectory = ConfigurationManager.AppSettings["ClaimRptPath"],
FileName = ConfigurationManager.AppSettings["ClaimRptexe"]
};
rpt.Start();
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += new DoWorkEventHandler((o, e) => {
rpt.WaitForExit();
});
bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler((o, e) => {
ClaimRptHandle = IntPtr.Zero;
LaunchState = "ClaimRpt closed";
});
bg.RunWorkerAsync();
Thread.Sleep(3000);
ClaimRptHandle = rpt.MainWindowHandle;
}
2 ответа
Еще один вариант, если вы не можете изменить запущенное приложение WPF, но вы знаете заголовок заголовка его главного окна, кроме идентификатора процесса, конечно.
В этом случае фоновый поиск будет
LaunchedHandle = rpt.MainWindowHandle;
mainWin = rpt.MainWindowHandle;
BackgroundWorker bgTitle = new BackgroundWorker();
bgTitle.DoWork += new DoWorkEventHandler((o, e) => {
while (!rpt.HasExited)
{
LaunchedHandle = MainWindowHandle(rpt);
Thread.Sleep(500);
}
Debug.WriteLine("Process exited!");
});
bgTitle.RunWorkerAsync();
использование фильтра на основе идентификатора процесса
private IntPtr MainWindowHandle(Process rpt)
{
EnumWindowsProc ewp = new EnumWindowsProc(EvalWindow);
EnumWindows(ewp, new IntPtr(rpt.Id));
return mainWin;
}
и обратный вызов, проверяющий заголовок заголовка (в этом примере это Launched
)
private bool EvalWindow(IntPtr hWnd, IntPtr lParam)
{
int procId;
GetWindowThreadProcessId(hWnd, out procId);
if (new IntPtr(procId) != lParam)
{
return true;
}
StringBuilder b = new StringBuilder(50);
GetWindowText(hWnd, b, 50);
string test = b.ToString();
if (test.Equals("Launched"))
{
mainWin = hWnd;
}
return true;
}
Предположим, вы можете изменить источник как запускающего, так и запущенного WPF.
Исходя из этого предположения, я мог определить правильную ручку в Loaded
событие запущенного приложения WPF и отправьте его обратно на панель запуска с помощью именованного канала.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var callback = new WindowInteropHelper(this).Handle;
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += (s, a) =>
{
WritePipe("at loaded evt: " + callback);
};
bg.RunWorkerAsync();
}
private void WritePipe(string line)
{
using (NamedPipeServerStream server =
new NamedPipeServerStream(Environment.UserName, PipeDirection.InOut))
{
server.WaitForConnection();
using (StreamWriter sw = new StreamWriter(server))
{
sw.WriteLine(line);
}
}
}
и прочитайте правильный дескриптор окна из того же Именованного канала в другом фоновом работнике модуля запуска
bg.RunWorkerAsync();
Thread.Sleep(3000);
if (rpt.HasExited)
{
return;
}
LaunchedHandle = rpt.MainWindowHandle;
BackgroundWorker bgPipe = new BackgroundWorker();
bgPipe.DoWork += new DoWorkEventHandler((o, e) => {
while (!rpt.HasExited)
{
string testHandle = ReadPipe();
if (testHandle.StartsWith("at loaded evt: "))
{
Debug.WriteLine(testHandle);
Debug.WriteLine("CallBack from Launched Process!");
var handle = testHandle.Replace("at loaded evt: ","");
LaunchedHandle = new IntPtr(int.Parse(handle));
return;
}
LaunchedHandle = rpt.MainWindowHandle;
Thread.Sleep(500);
}
Debug.WriteLine("Process exited!");
});
bgPipe.RunWorkerAsync();
CanLaunchCmd = true;
с
private string ReadPipe()
{
string line = "";
using (NamedPipeClientStream client =
new NamedPipeClientStream(".", Environment.UserName, PipeDirection.InOut))
{
client.Connect();
using (StreamReader sr = new StreamReader(client))
{
line = sr.ReadLine();
}
return line;
}
}
Конечно, я открыт для разных идей.