2010-07-06 3 views
13

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

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

Например:

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

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

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

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

+0

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

+0

В этом случае экземпляр 1 ведет себя в одну сторону, экземпляры 2 и 3 должны использовать альтернативное поведение. Если есть уже запущенный экземпляр, независимо от того, когда он был запущен или что произошло, новый экземпляр будет вести себя незначительно - хотя и не подчёркнуто - иначе. Разница заключается только в одноразовом действии при загрузке. – Andy

ответ

12

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

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; 
+0

Я бы добавил, что APPLICATION-TITLE является исполняемым именем файла без расширения (если я правильно помню). –

+0

@sztomi: спасибо, что зажег память о том, что код лежит вокруг, чтобы определить, что автоматически. –

+0

Ха-ха, хорошо. Я думаю, что я жестко запрограммировал его, когда у меня возникла эта проблема. Это намного лучше :) –

1

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

2

Другой подход заключается в обнаружении запущенного экземпляра, как описано в Scott Hanselman's blog

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

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

+1

Почему никто не использует существующий механизм в [WindowsFormsApplicationBase] (http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.applicationservices.windowsformsapplicationbase.aspx)? Просто потому, что его пространство имен содержит VisualBasic! Если авторы помещают его в пространство имен Foo, оно будет использоваться гораздо большим количеством людей. – Oliver

0

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

public Mutex (
    bool initiallyOwned, 
    string name, 
    out bool createdNew 
) 
0

хороший подход заключается в использовании решения Шандора, но использовать WMI, чтобы получить список процессов, описано здесь: C#: How to get the full path of running process? (Джеффа). таким образом, вы также можете проверить соответствие совпадений другим путям и идентификатору сеанса удаленного терминала:

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; 
     } 
    } 
Смежные вопросы