Страницы

Поиск по вопросам

вторник, 5 февраля 2019 г.

c#. Winforms. Single-Instance и активация окна при повторном запуске

Разрабоатываю WinForms приложение как Single-Instance. При первом вызове запускается как бы «Ядро» приложения, которое затем создает и отображает определенное окно. При последующем запуске приложение проверяет, запущено ли уже приложение и в положительном случае посылает сообщение ядру, передает ему опредленные данные и закрывается. Ядро получает сообщение с параметром, обрабатывает его пределенным образом и создает новую форму и ее оторбражает. Реализаций подобного есть несколько. Я ипользовал мютексы для проверки существования запущенного приложения и данные передаю при помощи именованных pipes (также пробовал с Remoting). Но есть одна проблема, которая доставляет большое неудобство, а именно, вторая инстанция приложения не получает фокус ввода. Чего я уже только не пробовал, приложение при актвации лишь мигает в таскбаре, а фокус ввода находится там, откуда оно было вызвано.
Я уже и с виндовc API игрался, никак не выходит. Например вызов SetForegroundWindow(HandleRef hWnd) возвращает False. И ничего нельзя сделать.
Как можно решить задачу?

Добавление:
Если кто-то захочет помочь, как базис можно использовать например вот эту статью и код. Хотя я использую несколько другой подход, в этом приложении тоже проявляется описанная мною проблема:
http://blogs.microsoft.co.il/maxim/2010/02/13/single-instance-application-manager/
Просто запускайте приложение из файлового проводника при помощи "Enter", и увидете что фокус ввода при повторном запуске остается в проводнике. Экспериментировать, как я понимаю, нужно здесь:
private static void SingleInstanceCallback(object sender, InstanceCallbackEventArgs args) { if (args == null || _mainFrm == null) return; Action d = (bool x) => { _mainFrm.ApendArgs(args.CommandLineArgs); _mainFrm.Activate(x); }; _mainFrm.Invoke(d, true); }


Ответ

Натолкнулся на одно решение и оно у меня на Windows 10 с небольшими изменениями работает: https://stackoverflow.com/questions/6319568/how-to-bring-a-form-already-shown-up-to-the-very-foreground-and-focus-it/22737820#22737820
[DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("user32.dll")] private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
public static void xActivateAndBringToFront(this Form form) {
// activate window var currentForegroundWindow = GetForegroundWindow(); var thisWindowThreadId = GetWindowThreadProcessId(form.Handle, IntPtr.Zero); var currentForegroundWindowThreadId = GetWindowThreadProcessId(currentForegroundWindow, IntPtr.Zero); AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, true); form.Activate(); // or: SetForegroundWindow(form.Handle); AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, false);
// set window to front form.TopMost = true; form.TopMost = false; }

Дополнение после подсказок от MSDN.WhiteKnight.
MSDN.WhiteKnight указал на то, что применение метода AttachThreadInput может вызывать в некоторых случаях проблемы. Краткий поиск в интернете подтверждает это.
Поэтому, с его же подсказки, я попробовал применить метод AllowSetForegroundWindow. С успехом! А именно, приложение при первом запуске сохраняет ID своего процесса в реестре:
Application.UserAppDataRegistry.SetValue(CORE_PROCESS_ID, Process.GetCurrentProcess().Id);
Затем, при повторном запуске приложение перед посылкой сообщения ядру вызывает метод AllowSetForegroundWindow:
int processID = (int)Application.UserAppDataRegistry.GetValue(CORE_PROCESS_ID); bool b = AllowSetForegroundWindow(processID); // .. сообщение ядру .. // .. выход ..
Ядро создает и отображает окно, окно получает фокус ввода как положено. Не нужно даже как-то принудительно подымать окно. По сути form.Activate() достаточно, но даже этот вызов не нужен, если окно создается и отображается в регламентированном порядке через Show(), ShowDialog() или Application.Run(form).

Комментариев нет:

Отправить комментарий