Обнаружение, если другой экземпляр приложения уже запущен
мое приложение должно вести себя немного иначе при загрузке, если уже запущен экземпляр.
Я понимаю, как использовать мьютекс для предотвращения дополнительной нагрузки экземпляры, но это не совсем решает мою проблему.
например:
- экземпляр 1 загружается, получает мьютекс.
- экземпляр 2 загружается, не может получить мьютекс, знает, что есть другой экземпляр. Пока все идет хорошо.
- экземпляр 1 закрывается, выпускает взаимное исключение.
- экземпляр 3 загружается, получает мьютекс, не знает, что экземпляр 2 все еще работает.
какие идеи? К счастью, ему не нужно иметь дело с несколькими учетными записями пользователей или чем-то подобным.
(на C# настольных приложений)
Edit: чтобы уточнить, приложение не должно быть ограничено одним экземпляром, просто выполните немного другое действие запуска, если уже запущен другой экземпляр. Несколько экземпляров в порядке (и ожидалось).
5 ответов
Это, вероятно, будет делать то, что вы хотите. Он имеет приятную дополнительную функцию переноса уже запущенного экземпляра вперед.
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;
другой подход заключается в обнаружении запущенного экземпляра, как описано в блог Скотта Хансельмана
его пример активирует первый экземпляр, когда второй пытается.
однако было бы нетрудно заставить второй экземпляр просто остановиться, если это то, что вы хотели.
не могли бы вы просто проверить GetLastError()
после создания мьютекса с CreateMutex()
? Если он вернется ERROR_ALREADY_EXISTS
, то есть еще один запущенный экземпляр приложения.
по данным http://msdn.microsoft.com/en-us/library/ms682411%28VS.85%29.aspx,
если мьютекс является именованным мьютексом и объект существовал до этой функции вызов, возвращаемое значение является дескриптором существующий объект, GetLastError возвращает ERROR_ALREADY_EXISTS, bInitialOwner игнорируется, и вызывающий поток не предоставляется собственность. Однако, если абонент ограниченные права доступа, функция завершится результат error_access_denied и вызывающий должен использовать OpenMutex функция.
EDIT: просто понял, что это вопрос C#/.Net, извините.
в .Net используйте конструктор мьютекса, возвращающий флаг createdNew, http://msdn.microsoft.com/en-us/library/bwe34f1k%28VS.80%29.aspx:
public Mutex (
bool initiallyOwned,
string name,
out bool createdNew
)
хороший подход-использовать решение Sandor, но использовать WMI для получения списка процессов, описанного здесь:в C#: Как получить полный путь запущенного процесса? (решение Джеффа). таким образом, вы также можете проверить, совпадают ли другие запущенные экземпляры по пути и идентификатору сеанса удаленного терминала:
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;
}
}