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

person Community    schedule 18.03.2017    source источник
comment
Я не пробовал ваш код, но уже есть что искать msdn.microsoft.com/en-us/library/cc656886(v=vs.110).aspx   -  person AnjumSKhan    schedule 18.03.2017
comment
@AnjumSKhan Я не пробовал ваш код На самом деле вы показали, что не читали и не поняли мой вопрос. Запущенный wpf уже имеет заставку.   -  person    schedule 18.03.2017
comment
Я разместил эту ссылку, так как она может рассказать вам что-то, что вы могли упустить.   -  person AnjumSKhan    schedule 18.03.2017
comment
Спасибо, но, конечно, я знаю ссылку, и это точно, как я реализовал заставку в своем процессе... Кстати, я отредактировал вопрос, чтобы дополнительно указать, что заставка находится в запущенном процесс, а не в лаунчере (но я думаю, это было очевидно)   -  person    schedule 18.03.2017
comment
Это должно помочь вам stackoverflow.com/questions/29645968/   -  person AnjumSKhan    schedule 18.03.2017
comment
Там нет ответа (так как это поможет?), вероятно, потому что он запрашивает общее решение/библиотеку, в то время как я говорю, что можно изменить источник обоих приложений (там это не библиотека, а только конкретная виртуальная машина) и найдите любой обходной путь, чтобы заставить его работать...   -  person    schedule 18.03.2017
comment
Вы читали эти комментарии, я подумал, что это может помочь вам.   -  person AnjumSKhan    schedule 18.03.2017


Ответы (2)


Предположим, вы можете изменить источник как средства запуска, так и запущенного 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;
    }
}

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

person Community    schedule 18.03.2017

Просто еще один вариант, если вы не можете изменить запущенное приложение 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;
}
person Community    schedule 18.03.2017