Обнаружение, если другой экземпляр приложения уже запущен
Мое приложение должно вести себя несколько иначе, когда оно загружается, если уже запущен экземпляр.
Я понимаю, как использовать мьютексы для предотвращения загрузки дополнительных экземпляров, но это не совсем решает мою проблему.
Например:
- Загружается экземпляр 1, получает мьютекс.
- Загружает экземпляр 2, не может получить мьютекс, знает другой экземпляр. Пока что так хорошо.
- Экземпляр 1 закрывается, высвобождает мьютекс.
- Экземпляр 3 загружает, получает мьютекс, не знает, что экземпляр 2 все еще запущен.
Любые идеи? К счастью, ему не нужно иметь дело с несколькими учетными записями пользователей или что-то в этом роде.
(С#, настольное приложение)
Изменить: Чтобы уточнить, приложение не обязательно должно быть ограничено одним экземпляром, просто выполните несколько другое стартовое действие, если другой экземпляр уже запущен. Несколько экземпляров являются точными (и ожидаемыми).
Ответы
Ответ 1
Это, вероятно, сделает именно то, что вы хотите. Он имеет приятную дополнительную функцию для переноса уже запущенного экземпляра вперед.
EDIT: обновил код, чтобы автоматически определить название приложения.
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
static void Main()
{
if (!EnsureSingleInstance())
{
return;
}
//...
}
static bool EnsureSingleInstance()
{
Process currentProcess = Process.GetCurrentProcess();
var runningProcess = (from process in Process.GetProcesses()
where
process.Id != currentProcess.Id &&
process.ProcessName.Equals(
currentProcess.ProcessName,
StringComparison.Ordinal)
select process).FirstOrDefault();
if (runningProcess != null)
{
ShowWindow(runningProcess.MainWindowHandle, SW_SHOWMAXIMIZED);
SetForegroundWindow(runningProcess.MainWindowHandle);
return false;
}
return true;
}
[DllImport("user32.dll", EntryPoint = "SetForegroundWindow")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow);
private const int SW_SHOWMAXIMIZED = 3;
Ответ 2
Другой подход - обнаружить исполняемый экземпляр, как описано в блог Scott Hanselman
Его пример активирует первый экземпляр, когда второй пытается.
Тем не менее, нетрудно заставить второй экземпляр просто остановиться, если вы этого хотели.
Ответ 3
Попробуйте использовать Семафор вместо Mutex
Ответ 4
Не удалось ли просто проверить GetLastError()
после создания мьютекса с помощью CreateMutex()
? Если он возвращает ERROR_ALREADY_EXISTS
, то есть еще один исполняемый экземпляр вашего приложения.
Согласно http://msdn.microsoft.com/en-us/library/ms682411%28VS.85%29.aspx,
Если мьютекс является именованным мьютеком и объект существовал до этой функции call, возвращаемое значение является дескриптором существующий объект, GetLastError возвращает ERROR_ALREADY_EXISTS, bInitialOwner игнорируется, и вызывающий поток не предоставляется владение. Однако, если вызывающий абонент ограниченные права доступа, функция сбой ERROR_ACCESS_DENIED и вызывающий должен использовать OpenMutex функция.
EDIT: Просто понял, что это вопрос С#/. Net, извините.
В .Net используйте конструктор Mutex, который возвращает созданный флаг New, http://msdn.microsoft.com/en-us/library/bwe34f1k%28VS.80%29.aspx:
public Mutex (
bool initiallyOwned,
string name,
out bool createdNew
)
Ответ 5
хороший подход - использовать решение Sandor, но использовать WMI для получения списка процессов, описанного здесь: С#: как получить полный путь к запуску процесса? (Решение Джеффа). Таким образом, вы также можете проверить, соответствуют ли другие запущенные экземпляры по пути и идентификатору сеанса удаленного терминала:
static bool EnsureSingleInstance()
{
Process currentProcess = Process.GetCurrentProcess();
var wmiQueryString = "SELECT ProcessId, ExecutablePath, CommandLine FROM Win32_Process";
using (var searcher = new ManagementObjectSearcher(wmiQueryString))
using (var results = searcher.Get())
{
var query = from p in Process.GetProcesses()
join mo in results.Cast<ManagementObject>()
on p.Id equals (int)(uint)mo["ProcessId"]
select new
{
Process = p,
Path = (string)mo["ExecutablePath"],
CommandLine = (string)mo["CommandLine"],
};
var runningProcess = (from process in query
where
process.Process.Id != currentProcess.Id &&
process.Process.ProcessName.Equals(
currentProcess.ProcessName,
StringComparison.Ordinal) &&
process.Path == currentProcess.MainModule.FileName &&
process.Process.SessionId == currentProcess.SessionId
select process).FirstOrDefault();
return runningProcess == null;
}
}