Как реализовать средство запуска 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;
    }
}

Конечно, я открыт для разных идей.

Другие вопросы по тегам