Еще один способ реализации Single Instance Application в .NET
Итак, возникла такая задача. На WPF пишется небольшое приложение (под .NET Framework 4 с рассчетом на пользователя Windows 7, а именно меня), а, заодно, и изучается сама технология WPF. Требуется не допускать запуска второго экземпляра приложения из-под того же пользователя.
Поиском в гугле найдено два способа реализации Single Instance Application:
Недостатки очевидны — если имя экзешника не слишком мудреное, есть вероятность совпадений, — это во-первых, а во-вторых — что делать, если такой процесс уже запущен из-под другого пользователя (все-таки Windows NT допускает несколько одновременных сеансов разных юзеров). Думаю, вторая проблема в принципе разрешима, но все равно получается как-то некрасиво.
Поэтому пришлось искать свое решение. Итак — от одного пользователя может быть запущен только один экземпляр приложения:
Этот код делает следующее. В HKCU\Software создается параметр реестра с нетривиальным именем.
При запуске программы у этого параметра создается значение, имя которого представляет собой строковую запись PID текущего процесса. Затем все значения проверяются на соответствие запущенным процессам, если процесс отсутствует, соответствующее значение удаляется (на случай, если в прошлый раз программа была завершена аварийно). Если же процесс существует, и его PID не равен PID текущего процесса, приложение завершается. При нормальном же завершении программы значение в реестре, соответствующее текущему процессу, также удаляется. Следует отметить, что при вызове Application.Shutdown(), использованном в этом коде, событие ApplicationExit также происходит, в результате чего при попытках повторного запуска программы у нашего параметра реестра не образуются лишние мусорные значения.
Поиском в гугле найдено два способа реализации Single Instance Application:
1. Простейший
using System.Diagnostics;
...
//при запуске приложения
int pid = Process.GetCurrentProcess().Id;
string pname = Process.GetCurrentProcess().ProcessName;
foreach(var p in Process.GetProcesses())
if(p.ProcessName==pname && p.Id != pid)
Application.Current.Exit(0);
Недостатки очевидны — если имя экзешника не слишком мудреное, есть вероятность совпадений, — это во-первых, а во-вторых — что делать, если такой процесс уже запущен из-под другого пользователя (все-таки Windows NT допускает несколько одновременных сеансов разных юзеров). Думаю, вторая проблема в принципе разрешима, но все равно получается как-то некрасиво.
2. С мьютексом
Опытным путем установлено, что в Windows 7 под 4-м .NET Framework не работает. Плюс, если бы и работал — были бы проблемы с повторным запуском приложения после его аварийного завершения. Так что код приводить не буду.Поэтому пришлось искать свое решение. Итак — от одного пользователя может быть запущен только один экземпляр приложения:
public partial class App : Application
{
private const string MYAPPNAME = "WLaunchPad-734685-3487563456-345734564";
private void Application_Startup(object sender, StartupEventArgs e)
{
var cp = Process.GetCurrentProcess();
var cpid = cp.Id;
var cpname = cp.ProcessName;
var regpath = Registry.CurrentUser.CreateSubKey("Software", RegistryKeyPermissionCheck.ReadWriteSubTree).
CreateSubKey(MYAPPNAME, RegistryKeyPermissionCheck.ReadWriteSubTree);
regpath.SetValue(cpid.ToString(), "running");
bool stopflag = false;
foreach (string v in regpath.GetValueNames())
{
int id = Convert.ToInt32(v);
Process proc = null;
try
{
proc = Process.GetProcessById(id);
}
catch (ArgumentException)
{
regpath.DeleteValue(v);
}
if (proc == null)
continue;
if (proc.Id != cpid && proc.ProcessName == cpname)
Application.Current.Shutdown();
}
}
private void Application_Exit(object sender, ExitEventArgs e)
{
var regpath = Registry.CurrentUser.CreateSubKey("Software", RegistryKeyPermissionCheck.ReadWriteSubTree).
CreateSubKey(MYAPPNAME, RegistryKeyPermissionCheck.ReadWriteSubTree);
try
{
var cpid = Process.GetCurrentProcess().Id;
string valname = cpid.ToString();
if (regpath.GetValueNames().Contains(valname))
regpath.DeleteValue(valname);
}
catch { }
}
Этот код делает следующее. В HKCU\Software создается параметр реестра с нетривиальным именем.
При запуске программы у этого параметра создается значение, имя которого представляет собой строковую запись PID текущего процесса. Затем все значения проверяются на соответствие запущенным процессам, если процесс отсутствует, соответствующее значение удаляется (на случай, если в прошлый раз программа была завершена аварийно). Если же процесс существует, и его PID не равен PID текущего процесса, приложение завершается. При нормальном же завершении программы значение в реестре, соответствующее текущему процессу, также удаляется. Следует отметить, что при вызове Application.Shutdown(), использованном в этом коде, событие ApplicationExit также происходит, в результате чего при попытках повторного запуска программы у нашего параметра реестра не образуются лишние мусорные значения.
0 комментариев