Обнаружение, если другой экземпляр приложения уже запущен - PullRequest
13 голосов
/ 07 июля 2010

Мое приложение должно вести себя немного иначе, когда загружается, если уже запущен экземпляр.

Я понимаю, как использовать мьютекс для предотвращения загрузки дополнительных экземпляров, но это не решает мою проблему.

Например:

  • Экземпляр 1 загружается, получает мьютекс.
  • Экземпляр 2 загружается, не может получить мьютекс, знает, что есть другой экземпляр. Пока все хорошо.
  • Экземпляр 1 закрывается, освобождает мьютекс.
  • Экземпляр 3 загружается, получает мьютекс, не знает, что Экземпляр 2 все еще работает.

Есть идеи? К счастью, ему не нужно иметь дело с несколькими учетными записями пользователей или чем-то в этом роде.

(C #, настольное приложение)

Редактировать: Чтобы уточнить, приложение не должно быть ограничено одним экземпляром, просто выполните немного другое действие при запуске, если уже запущен другой экземпляр. Несколько экземпляров хорошо (и ожидается).

Ответы [ 5 ]

12 голосов
/ 07 июля 2010

Это, вероятно, будет делать то, что вы хотите. Он имеет приятную дополнительную функцию переноса уже запущенного экземпляра.

РЕДАКТИРОВАТЬ: обновил код для автоматического определения заголовка приложения.

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 голосов
/ 07 июля 2010

Другой подход заключается в обнаружении запущенного экземпляра, как описано в блоге Скотта Хансельмана

Его пример активирует первый экземпляр, когда второй пытается.

Тем не менее, было бы нетрудно заставить второй экземпляр просто остановиться, если вы этого хотели.

1 голос
/ 07 июля 2010

Попробуйте использовать семафор вместо мьютекса

0 голосов
/ 05 декабря 2014

хороший подход - использовать решение 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;
        }
    }
0 голосов
/ 07 июля 2010

Не могли бы вы просто проверить 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 используйте конструктор Mutex, который возвращает флаг созданногоНового, http://msdn.microsoft.com/en-us/library/bwe34f1k%28VS.80%29.aspx:

public Mutex (
    bool initiallyOwned,
    string name,
    out bool createdNew
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...