Разрабоатываю 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
Ответ
Натолкнулся на одно решение и оно у меня на 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).
Комментариев нет:
Отправить комментарий