동일한 프로그램의 여러 인스턴스가 안전을 실행하는 것을 방지하기 위해 뮤트를 사용하고 있습니까?

StackOverflow https://stackoverflow.com/questions/646480

문제

프로그램의 두 번째 인스턴스가 동시에 실행되는 것을 방지하기 위해이 코드를 사용하고 있습니다. 안전합니까?

Mutex appSingleton = new System.Threading.Mutex(false, "MyAppSingleInstnceMutx");
if (appSingleton.WaitOne(0, false)) {
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new MainForm());
    appSingleton.Close();
} else {
    MessageBox.Show("Sorry, only one instance of MyApp is allowed.");
}

무언가가 예외를 던지고 앱이 충돌하면 뮤 테스가 여전히 유지 될 것이라고 걱정합니다. 그게 사실인가요?

도움이 되었습니까?

해결책

일반적으로 예, 이것은 효과가 있습니다. 그러나 악마는 세부 사항에 있습니다.

먼저 뮤트를 닫으려고합니다 finally 차단하다. 그렇지 않으면 프로세스가 갑자기 종료되어 예외와 같이 신호 상태로 남겨 둘 수 있습니다. 그것은 미래의 프로세스 인스턴스가 시작될 수 없도록 만들 것입니다.

불행히도,조차도 finally 블록 당신은 뮤텍스를 풀지 않고 프로세스가 종료 될 가능성을 다루어야합니다. 예를 들어 사용자가 TaskManager를 통해 프로세스를 죽인 경우 발생할 수 있습니다. 코드에는 두 번째 프로세스가 AbandonedMutexException 에서 WaitOne 전화. 이를위한 복구 전략이 필요합니다.

나는 당신이 읽는 것을 권장합니다 MUTEX 클래스의 세부 사항. 그것을 사용하는 것이 항상 간단한 것은 아닙니다.


인종 조건 가능성 확장 :

다음과 같은 일련의 이벤트가 발생할 수있어 응용 프로그램의 두 번째 인스턴스가 발생합니다.

  1. 일반 프로세스 시작.
  2. 두 번째 프로세스가 시작되어 뮤 테스에 대한 손잡이를 아피 어입니다. WaitOne 전화.
  3. 프로세스 #1은 갑자기 종료됩니다. 프로세스 #2에 손잡이가 있기 때문에 뮤트는 파괴되지 않습니다. 대신 버려진 상태로 설정됩니다.
  4. 두 번째 프로세스는 다시 실행되기 시작하여 AbanonedMutexException.

다른 팁

이 목적으로 Windows 이벤트를 사용하는 것이 더 평범하고 편리합니다. 예를 들어

static EventWaitHandle s_event ;

bool created ;
s_event = new EventWaitHandle (false, 
    EventResetMode.ManualReset, "my program#startup", out created) ;
if (created) Launch () ;
else         Exit   () ;

프로세스가 종료되거나 종료되면 Windows는 이벤트를 닫고 열린 핸들이 남아 있지 않으면 파괴합니다.

추가: 세션을 관리하려면 사용하십시오 Local\ 그리고 Global\ 이벤트 (또는 뮤텍스) 이름의 접두사. 응용 프로그램이 사용자 당 인 경우 적절하게 엉망이 된 로그온 사용자 이름을 이벤트 이름에 추가하십시오.

뮤텍스를 사용할 수 있지만 먼저 이것이 실제로 원하는 것인지 확인하십시오.

"여러 인스턴스를 피하는 것"은 명확하게 정의되지 않기 때문입니다. 의미가 있습니다

  1. 사용자 세션이 가지고있는 데스크톱 수에 관계없이 동일한 사용자 세션에서 여러 인스턴스를 피했지만 여러 인스턴스가 다른 사용자 세션에 대해 동시에 실행할 수 있도록했습니다.
  2. 동일한 데스크탑에서 여러 인스턴스를 피하기 시작했지만 각 인스턴스가 별도의 데스크탑에있는 한 여러 인스턴스를 실행할 수 있습니다.
  3. 이 계정에서 실행되는 데스크톱이나 세션 수에 상관없이 동일한 사용자 계정에 대해 여러 인스턴스를 피하는 것이 시작되었지만 다른 사용자 계정에서 실행되는 세션에 대해 여러 인스턴스가 동시에 실행할 수 있습니다.
  4. 동일한 기계에서 여러 인스턴스를 피하는 것이 시작되었습니다. 즉, 임의의 사용자가 몇 개의 데스크탑을 사용하더라도 프로그램의 최대 한 인스턴스가 실행될 수 있습니다.

뮤텍스를 사용하면 기본적으로 정의 번호 4를 사용하고 있습니다.

나는이 방법을 사용합니다. 응용 프로그램에 의해 오래 보유되지 않으면 뮤트가 파괴되기 때문에 안전하다고 생각합니다 (처음에 MUTEXT를 만들 수없는 경우 응용 프로그램이 종료됩니다). 이는 'AppDomain-Processes'에서 동일하게 작동하거나 작동하지 않을 수 있습니다 (하단의 링크 참조).

// Make sure that appMutex has the lifetime of the code to guard --
// you must keep it from being collected (and the finalizer called, which
// will release the mutex, which is not good here!).
// You can also poke the mutex later.
Mutex appMutex;

// In some startup/initialization code
bool createdNew;
appMutex = new Mutex(true, "mutexname", out createdNew);
if (!createdNew) {
  // The mutex already existed - exit application.
  // Windows will release the resources for the process and the
  // mutex will go away when no process has it open.
  // Processes are much more cleaned-up after than threads :)
} else {
  // win \o/
}

위의 것은 악의적 인 프로그램이 뮤트에 앉을 수있는 다른 답변/의견의 메모로 고통 받고 있습니다. 여기서 걱정하지 않습니다. 또한 "로컬"공간에서 생성되지 않은 뮤 테스. 아마도 여기서 오른쪽에있을 것입니다.

보다: http://ayende.com/blog/archive/2008/02/28/the-mysterious-of-mutexes.aspx -Jon Skeet과 함께 제공 ;-)

Windows에서 프로세스를 종료하면 다음과 같은 결과가 있습니다.

  • 프로세스의 나머지 스레드는 종료로 표시됩니다.
  • 프로세스에 의해 할당 된 모든 자원이 해제됩니다.
  • 모든 커널 객체가 닫힙니다.
  • 프로세스 코드는 메모리에서 제거됩니다.
  • 프로세스 종료 코드가 설정됩니다.
  • 프로세스 객체가 신호됩니다.

Mutex 객체는 커널 객체이므로 프로세스가 종료 될 때 프로세스에 의해 보관 된 모든 프로세스가 닫힙니다 (Windows에서).

그러나 Createmutex () 문서에서 다음과 같은 점을 확인하십시오.

이름이 지정된 Mutex를 사용하여 응용 프로그램을 단일 인스턴스로 제한하는 경우 악의적 인 사용자 가이 MUTEX를 만들고 응용 프로그램을 시작하지 못하게 할 수 있습니다.

예, 안전합니다. 다음 패턴을 제안합니다. Mutex 항상 출시됩니다.

using( Mutex mutex = new Mutex( false, "mutex name" ) )
{
    if( !mutex.WaitOne( 0, true ) )
    {
        MessageBox.Show("Unable to run multiple instances of this program.",
                        "Error",  
                        MessageBoxButtons.OK, 
                        MessageBoxIcon.Error);
    }
    else
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm());                  
    }
}

코드 스 니펫은 다음과 같습니다

public enum ApplicationSingleInstanceMode
{
    CurrentUserSession,
    AllSessionsOfCurrentUser,
    Pc
}

public class ApplicationSingleInstancePerUser: IDisposable
{
    private readonly EventWaitHandle _event;

    /// <summary>
    /// Shows if the current instance of ghost is the first
    /// </summary>
    public bool FirstInstance { get; private set; }

    /// <summary>
    /// Initializes 
    /// </summary>
    /// <param name="applicationName">The application name</param>
    /// <param name="mode">The single mode</param>
    public ApplicationSingleInstancePerUser(string applicationName, ApplicationSingleInstanceMode mode = ApplicationSingleInstanceMode.CurrentUserSession)
    {
        string name;
        if (mode == ApplicationSingleInstanceMode.CurrentUserSession)
            name = $"Local\\{applicationName}";
        else if (mode == ApplicationSingleInstanceMode.AllSessionsOfCurrentUser)
            name = $"Global\\{applicationName}{Environment.UserDomainName}";
        else
            name = $"Global\\{applicationName}";

        try
        {
            bool created;
            _event = new EventWaitHandle(false, EventResetMode.ManualReset, name, out created);
            FirstInstance = created;
        }
        catch
        {
        }
    }

    public void Dispose()
    {
        _event.Dispose();
    }
}

Mutex 기반 접근법을 사용하려면 실제로 로컬 뮤트를 사용해야합니다. Curent 사용자의 로그인 세션에 대한 접근 방식을 제한합니다.. 또한 MUTEX 접근법과의 강력한 자원 처리에 대한 해당 링크의 다른 중요한 경고에 주목하십시오.

한 가지주의 사항 중 하나는 MUTEX 기반 접근 방식으로 사용자가 두 번째 인스턴스를 시작하려고 할 때 앱의 첫 번째 인스턴스를 활성화 할 수 없다는 것입니다.

대안은 FindWindow에 Pinvoke에 이어 ForeWindow와 함께 첫 번째 인스턴스에 대한 setforegroundwindow가 이어집니다. 또 다른 대안은 이름으로 프로세스를 확인하는 것입니다.

Process[] processes = Process.GetProcessesByName("MyApp");
if (processes.Length != 1)
{
    return;
} 

이 후자의 대안 모두 앱의 두 인스턴스가 동시에 시작된 다음 서로를 감지 할 수있는 가상의 인종 조건을 가지고 있습니다. 실제로는 발생하지 않을 것입니다. 실제로 테스트 중에는 그렇게 할 수 없었습니다.

이 두 후자의 대안의 또 다른 문제는 터미널 서비스를 사용할 때 작동하지 않는다는 것입니다.

PubandonedMutexexception을 피하면서 타임 아웃 및 보안 설정이있는 앱을 사용하십시오. 내 커스텀 클래스를 사용했습니다.

private class SingleAppMutexControl : IDisposable
    {
        private readonly Mutex _mutex;
        private readonly bool _hasHandle;

        public SingleAppMutexControl(string appGuid, int waitmillisecondsTimeout = 5000)
        {
            bool createdNew;
            var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null),
                MutexRights.FullControl, AccessControlType.Allow);
            var securitySettings = new MutexSecurity();
            securitySettings.AddAccessRule(allowEveryoneRule);
            _mutex = new Mutex(false, "Global\\" + appGuid, out createdNew, securitySettings);
            _hasHandle = false;
            try
            {
                _hasHandle = _mutex.WaitOne(waitmillisecondsTimeout, false);
                if (_hasHandle == false)
                    throw new System.TimeoutException();
            }
            catch (AbandonedMutexException)
            {
                _hasHandle = true;
            }
        }

        public void Dispose()
        {
            if (_mutex != null)
            {
                if (_hasHandle)
                    _mutex.ReleaseMutex();
                _mutex.Dispose();
            }
        }
    }

그리고 그것을 사용하십시오 :

    private static void Main(string[] args)
    {
        try
        {
            const string appguid = "{xxxxxxxx-xxxxxxxx}";
            using (new SingleAppMutexControl(appguid))
            {
                //run main app
                Console.ReadLine();
            }
        }
        catch (System.TimeoutException)
        {
            Log.Warn("Application already runned");
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, "Fatal Error on running");
        }
    }

내가 이것에 접근 한 방법은 다음과 같습니다

프로그램 클래스에서 : 1. Process.GetCurrentProcess ()를 사용하여 System.Diagnostics.Process를 가져옵니다. GetCurrentProcess () 2. Process.GetProcessesByName (thisprocess.processName)을 사용하여 응용 프로그램의 현재 이름으로 열린 프로세스 모음을 단계별로 진행하십시오. thisprocess.id에 대한 각 process.id와 인스턴스가 이미 열린 경우 최소 1 개는 이름이 아니지만 ID가 아닌 일치하지 않으면 인스턴스를 계속 여는 것입니다.

using System.Diagnostics;

.....    

static void Main()
{
   Process thisProcess = Process.GetCurrentProcess();
   foreach(Process p in Process.GetProcessesByName(thisProcess.ProcessName))
   {
      if(p.Id != thisProcess.Id)
      {
         // Do whatever u want here to alert user to multiple instance
         return;
      }
   }
   // Continue on with opening application

이것을 끝내는 좋은 터치는 이미 열린 인스턴스를 사용자에게 제시하는 것입니다. 그들이 열려 있다는 것을 알지 못했을 가능성이 높습니다. 이를 위해 user32.dll을 사용하여 메시지를 Windows 메시징 루프, 사용자 정의 메시지로 브로드 캐스트하고 앱을 WNDProc 메소드에서 듣고이 메시지를 받으면 사용자에게 자신을 제시합니다. .Show () 또는 무엇이

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top