В статье «Запускать только один экземпляр приложения» всё нормально работало, но минус всё же был — если приложение уже было запущено, то мы просто получали об этом уведомление в виде MessageBox. Дело в том, что мы не умели работать с другим процессом. В этот раз попробуем реализовать обращение к другому процессу и восстановление окна, если оно было свёрнуто. Давным давно мы тоже это делали, но там подключалась отдельная библиотека, исходник которой сейчас уже удалили и остались только её копии. Я не знаю причину, по которой она была удалена с оригинального сайта. Может что-то было в коде и эти ошибки (уязвимости?) не стали исправлять… На этот раз я буду использовать уже готовый класс из набора .NET, он называется Mutex. Я нашёл не так много примеров с его реализацией для моего случая, поэтому не уверен на счёт максимально эффективного кода. Но это решение мне понравилось, хотя кто-то в коментах писал, что у этого метода есть проблемы с производительностью. Тем не менее, распишу его здесь поподробнее.
Изменения для файла App.xaml.cs
Хотя позже я узнал, что в Visual Studio встроена утилита: ▶ ▶ ▶ . После копирования и вставки, нужно будет удалить фигурные скобки.
Полный код файла App.xaml.cs будет такой:
using System;
using System.Threading;
using System.Windows;
namespace SingleInstance
{
public partial class App
{
// The event mutex name.
private const string UniqueEventName = "4380587d-6dde-47b6-bb23-68eb68a27ba5";
// The unique mutex name.
private const string UniqueMutexName = "5e81acba-2dd9-4a71-b8bd-625ab30309e4";
// The event wait handle.
private EventWaitHandle eventWaitHandle;
// The mutex.
private Mutex mutex;
// The app on startup event
private void AppOnStartup(object sender, StartupEventArgs e)
{
bool isOwned;
mutex = new Mutex(true, UniqueMutexName, out isOwned);
eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, UniqueEventName);
// So, R# would not give a warning that this variable is not used.
GC.KeepAlive(mutex);
if (isOwned)
{
// Spawn a thread which will be waiting for our event
var thread = new Thread(
() =>
{
while (eventWaitHandle.WaitOne())
{
Current.Dispatcher.BeginInvoke(
(Action)(() => ((MainWindow)Current.MainWindow).BringToForeground()));
}
});
// It is important mark it as background otherwise it will prevent app from exiting.
thread.IsBackground = true;
thread.Start();
return;
}
// Notify other instance so it could bring itself to foreground.
eventWaitHandle.Set();
// Terminate this instance.
Shutdown();
}
}
}
Изменения для файла App.xaml
Чтобы запускалось не две копии приложения, а только одна, нам нужно обратить внимание на файл App.xaml, здесь свойство StartupUri дополняется подпиской на событие Startup, т.о. полный код этого файла теперь будет такой:
<Application x:Class="SingleInstance.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml"
Startup="AppOnStartup">
<Application.Resources>
</Application.Resources>
</Application>
Изменения для файла MainWindow.xaml.cs
В файле App.xaml.cs мы обращались к методу BringToForeground, его необходимо описать в файле логики для главного окна MainWindow.xaml.cs:
using System.Windows;
namespace SingleInstance
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
// Brings main window to foreground.
public void BringToForeground()
{
if (WindowState == WindowState.Minimized || Visibility == Visibility.Hidden)
{
Show();
WindowState = WindowState.Normal;
}
// According to some sources these steps guarantee that an app will be brought to foreground.
Activate();
Topmost = true;
Topmost = false;
Focus();
}
}
}
- C#: Сравнить два массива - 12.12.2024
- EVE-Online: Фильтры каналов - 23.11.2024
- Не приходит СМС для авторизации на сайте Госуслуги - 01.11.2024