단일 인스턴스 WPF 애플리케이션을 만드는 올바른 방법은 무엇입니까?

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

  •  09-06-2019
  •  | 
  •  

문제

.NET에서 C# 및 WPF 사용(대신 윈도우 폼 또는 콘솔), 단일 인스턴스로만 실행할 수 있는 애플리케이션을 만드는 올바른 방법은 무엇입니까?

나는 그것이 뮤텍스라고 불리는 신화적인 것과 관련이 있다는 것을 알고 있습니다. 귀찮게 멈추고 이것들 중 하나가 무엇인지 설명하는 사람을 거의 찾을 수 없습니다.

또한 코드는 사용자가 두 번째 인스턴스를 시작하려고 시도했음을 이미 실행 중인 인스턴스에 알리고, 존재하는 경우 명령줄 인수를 전달할 수도 있습니다.

도움이 되었습니까?

해결책

여기 아주 좋은 것이 있습니다 기사 Mutex 솔루션에 관해.기사에서 설명하는 접근 방식은 두 가지 이유로 유리합니다.

첫째, Microsoft.VisualBasic 어셈블리에 대한 종속성이 필요하지 않습니다.내 프로젝트가 이미 해당 어셈블리에 종속되어 있는 경우 아마도 다음 접근 방식을 사용하는 것이 좋습니다. 다른 답변에 표시됨.그러나 현재로서는 Microsoft.VisualBasic 어셈블리를 사용하지 않으며 프로젝트에 불필요한 종속성을 추가하고 싶지 않습니다.

둘째, 이 문서에서는 사용자가 다른 인스턴스를 시작하려고 할 때 애플리케이션의 기존 인스턴스를 포그라운드로 가져오는 방법을 보여줍니다.여기에 설명된 다른 Mutex 솔루션에서는 다루지 않는 매우 유용한 기능입니다.


업데이트

2014년 8월 1일 현재, 위에 링크한 글은 여전히 ​​활성화되어 있지만, 블로그는 한동안 업데이트가 되지 않았습니다.결국에는 그것이 사라질 수도 있고, 그와 함께 옹호되는 해결책도 사라질까 봐 걱정됩니다.나는 후손을 위해 여기에 기사의 내용을 재현하고 있습니다.해당 단어는 다음 블로그 소유자에게만 속합니다. 온전한 코딩.

오늘 저는 애플리케이션을 금지하는 일부 코드를 리팩터링하고 싶었습니다 의 여러 인스턴스를 실행하지 못하게 합니다.

이전에 내가 사용했던 시스템.진단.프로세스 를 검색하여 인스턴스를 프로세스 목록에 추가합니다.이 방법은 효과가 있지만 는 많은 오버헤드가 발생하기 때문에 더 깔끔한 것을 원했습니다.

이 작업을 위해 뮤텍스를 사용할 수 있다는 것을 알면서도 (하지만 한 번도 해본 적은 없습니다 이전) 저는 코드를 줄이고 생활을 단순화하기 시작했습니다.

내 애플리케이션 메인 클래스에서 나는 이름이 붙은 정적을 만들었습니다. 뮤텍스:

static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    ...
}

명명된 뮤텍스가 있으면 다음과 같이 동기화 스택을 쌓을 수 있습니다 여러 스레드와 프로세스는 제가 찾고 있는 마법과도 같습니다 에 대해.

Mutex.WaitOne 에는 시간을 지정하는 과부하가 있습니다 를 클릭합니다.실제로 코드를 동기화하려는 것이 아니기 때문에 (현재 사용 중인지 확인하기만 하면 됩니다.) 과부하가 발생하면 두 개의 매개변수: Mutex.WaitOne(시간 범위 제한 시간, boolexitContext).입력할 수 있으면 true를 반환하고, 입력할 수 없으면 false를 반환하도록 기다립니다.이 경우 우리는 전혀 기다리고 싶지 않습니다.뮤텍스가 사용했으면 건너뛰고 계속 진행하므로 TimeSpan.0을 전달합니다(대기 0 밀리초)를 설정하고, exitContext를 true로 설정하여 동기화 컨텍스트에 대한 잠금을 획득하려고 시도하기 전에.사용 이를 위해 Application.Run 코드를 다음과 같이 래핑합니다:

static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    static void Main() {
        if(mutex.WaitOne(TimeSpan.Zero, true)) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            mutex.ReleaseMutex();
        } else {
            MessageBox.Show("only one instance at a time");
        }
    }
}

따라서 앱이 실행되면 Waitone이 False를 반환하고 메시지 상자를 얻을 수 있습니다.

메시지 상자를 표시하는 대신 작은 Win32를 사용하여 다음을 수행했습니다 실행 중인 인스턴스를 누군가 잊어버렸다는 것을 알립니다 를 실행하면 다른 모든 창 상단에 표시됩니다.To 이를 달성하기 위해 사용한 방법 게시 메시지 를 사용하여 모든 창 (사용자 지정 메시지가 등록창메시지 를 실행하는 애플리케이션에 의해, 즉 내 애플리케이션만 이면 두 번째 인스턴스가 종료됩니다.실행 중인 애플리케이션 인스턴스 가 해당 알림을 수신하고 처리합니다.이를 위해 저는 오버라이드 WndProc 를 기본 양식에 추가하고 내 사용자 정의에 대해 들어 보았습니다 알림.그 알림을 받았을 때 저는 양식의 TopMost 속성을 true로 설정하여 맨 위에 표시합니다.

내가 끝낸 내용은 다음과 같습니다.

  • Program.cs
static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    static void Main() {
        if(mutex.WaitOne(TimeSpan.Zero, true)) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            mutex.ReleaseMutex();
        } else {
            // send our Win32 message to make the currently running instance
            // jump on top of all the other windows
            NativeMethods.PostMessage(
                (IntPtr)NativeMethods.HWND_BROADCAST,
                NativeMethods.WM_SHOWME,
                IntPtr.Zero,
                IntPtr.Zero);
        }
    }
}
  • 네이티브메소드.cs
// this class just wraps some Win32 stuff that we're going to use
internal class NativeMethods
{
    public const int HWND_BROADCAST = 0xffff;
    public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
    [DllImport("user32")]
    public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
    [DllImport("user32")]
    public static extern int RegisterWindowMessage(string message);
}
  • Form1.cs(앞면부분)
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    protected override void WndProc(ref Message m)
    {
        if(m.Msg == NativeMethods.WM_SHOWME) {
            ShowMe();
        }
        base.WndProc(ref m);
    }
    private void ShowMe()
    {
        if(WindowState == FormWindowState.Minimized) {
            WindowState = FormWindowState.Normal;
        }
        // get our current "TopMost" value (ours will always be false though)
        bool top = TopMost;
        // make our form jump to the top of everything
        TopMost = true;
        // set it back to whatever it was
        TopMost = top;
    }
}

다른 팁

Mutex 클래스를 사용할 수도 있지만 인수 등을 전달하려면 코드를 직접 구현해야 한다는 사실을 곧 알게 될 것입니다.글쎄요, 저는 WinForms에서 프로그래밍할 때의 요령을 읽다가 배웠습니다. 크리스 셀의 책.이 트릭은 프레임워크에서 이미 사용할 수 있는 논리를 사용합니다.당신은 어떤지 모르겠지만 프레임워크에서 재사용할 수 있는 것에 대해 알게 되면 일반적으로 바퀴를 재발명하는 대신 이것이 제가 택하는 경로입니다.물론 내가 원하는 모든 것을 다 해주지는 않는 한 말이다.

WPF에 입문했을 때 동일한 코드를 WPF 애플리케이션에서 사용하는 방법을 생각해 냈습니다.이 솔루션은 귀하의 질문에 따라 귀하의 요구 사항을 충족해야 합니다.

먼저, 애플리케이션 클래스를 생성해야 합니다.이 클래스에서는 OnStartup 이벤트를 재정의하고 나중에 사용할 Activate라는 메서드를 만듭니다.

public class SingleInstanceApplication : System.Windows.Application
{
    protected override void OnStartup(System.Windows.StartupEventArgs e)
    {
        // Call the OnStartup event on our base class
        base.OnStartup(e);

        // Create our MainWindow and show it
        MainWindow window = new MainWindow();
        window.Show();
    }

    public void Activate()
    {
        // Reactivate the main window
        MainWindow.Activate();
    }
}

둘째, 인스턴스를 관리할 수 있는 클래스를 만들어야 합니다.이 작업을 진행하기 전에 Microsoft.VisualBasic 어셈블리에 있는 일부 코드를 실제로 재사용하겠습니다.이 예제에서는 C#을 사용하고 있으므로 어셈블리에 대한 참조를 만들어야 했습니다.VB.NET을 사용하는 경우에는 아무 것도 할 필요가 없습니다.우리가 사용할 클래스는 WindowsFormsApplicationBase이며 인스턴스 관리자를 상속한 다음 속성과 이벤트를 활용하여 단일 인스턴스를 처리합니다.

public class SingleInstanceManager : Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
{
    private SingleInstanceApplication _application;
    private System.Collections.ObjectModel.ReadOnlyCollection<string> _commandLine;

    public SingleInstanceManager()
    {
        IsSingleInstance = true;
    }

    protected override bool OnStartup(Microsoft.VisualBasic.ApplicationServices.StartupEventArgs eventArgs)
    {
        // First time _application is launched
        _commandLine = eventArgs.CommandLine;
        _application = new SingleInstanceApplication();
        _application.Run();
        return false;
    }

    protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
    {
        // Subsequent launches
        base.OnStartupNextInstance(eventArgs);
        _commandLine = eventArgs.CommandLine;
        _application.Activate();
    }
}

기본적으로 우리는 VB 비트를 사용하여 단일 인스턴스를 감지하고 그에 따라 처리합니다.OnStartup은 첫 번째 인스턴스가 로드될 때 시작됩니다.OnStartupNextInstance는 애플리케이션이 다시 실행될 때 시작됩니다.보시다시피 이벤트 인수를 통해 명령줄에 전달된 내용을 확인할 수 있습니다.인스턴스 필드에 값을 설정했습니다.여기에서 명령줄을 구문 분석하거나 생성자와 Activate 메서드 호출을 통해 응용 프로그램에 전달할 수 있습니다.

셋째, EntryPoint를 생성할 차례입니다.평소처럼 애플리케이션을 새로 만드는 대신 SingleInstanceManager를 활용하겠습니다.

public class EntryPoint
{
    [STAThread]
    public static void Main(string[] args)
    {
        SingleInstanceManager manager = new SingleInstanceManager();
        manager.Run(args);
    }
}

글쎄, 나는 당신이 모든 것을 따라갈 수 있고 이 구현을 사용하여 자신의 것으로 만들 수 있기를 바랍니다.

에서 여기.

크로스 프로세스 뮤텍스의 일반적인 용도는 한 번에 프로그램 인스턴스만 실행할 수 있도록 하는 것입니다.수행 방법은 다음과 같습니다.

class OneAtATimePlease {

  // Use a name unique to the application (eg include your company URL)
  static Mutex mutex = new Mutex (false, "oreilly.com OneAtATimeDemo");

  static void Main()
  {
    // Wait 5 seconds if contended – in case another instance
    // of the program is in the process of shutting down.
    if (!mutex.WaitOne(TimeSpan.FromSeconds (5), false))
    {
        Console.WriteLine("Another instance of the app is running. Bye!");
        return;
    }

    try
    {    
        Console.WriteLine("Running - press Enter to exit");
        Console.ReadLine();
    }
    finally
    {
        mutex.ReleaseMutex();
    }    
  }    
}

Mutex의 좋은 기능은 ReleaseMutex가 먼저 호출되지 않고 애플리케이션이 종료되면 CLR이 자동으로 Mutex를 해제한다는 것입니다.

MSDN에는 실제로 이 작업을 정확하게 수행하기 위한 C# 및 VB용 샘플 응용 프로그램이 있습니다. http://msdn.microsoft.com/en-us/library/ms771662(v=VS.90).aspx

가장 일반적이고 신뢰할 수 있는 기술 단일 인스턴스 개발용 탐지는 Microsoft .NET 프레임워크 원격 인프라 (System.Remoting).Microsoft .NET 프레임워크(버전 2.0)에는 다음과 같은 기능이 포함됩니다 유형, WindowsFormsApplicationBase, 를 캡슐화하여 필요한 원격 기능.통합하려면 이 유형을 WPF 애플리케이션에 넣으면 유형이 파생되어야 하며, 이 유형이 애플리케이션 사이의 심으로 사용 정적 진입점 메서드, 메인, 그리고 wPF 애플리케이션의 애플리케이션 유형입니다.심은 다음을 감지합니다 애플리케이션이 처음 실행되고 후속 실행이 시도 및 수익률로 WPF를 제어합니다 애플리케이션 유형에 따른 방법 결정 실행을 처리합니다.

  • C# 사용자의 경우 심호흡을 하고 'VisualBasic DLL을 포함하고 싶지 않습니다'라는 내용은 모두 잊어버리세요.때문에 이것 그리고 뭐 스콧 한셀만 라고 그리고 이것이 문제에 대한 가장 깔끔한 해결책이고 당신보다 프레임워크에 대해 더 많이 아는 사람들에 의해 설계되었다는 사실입니다.
  • 유용성 관점에서 볼 때 사실은 사용자가 애플리케이션을 로드하고 있고 해당 애플리케이션이 이미 열려 있고 다음과 같은 오류 메시지를 표시하는 것입니다. 'Another instance of the app is running. Bye' 그러면 그들은 매우 행복한 사용자가 되지 못할 것입니다.(GUI 애플리케이션에서) 해당 애플리케이션으로 전환하고 제공된 인수를 전달해야 합니다. 또는 명령줄 매개변수가 의미가 없는 경우 최소화되었을 수 있는 애플리케이션을 팝업해야 합니다.

프레임워크는 이미 이를 지원합니다. 단지 DLL이라는 이름의 바보가 있을 뿐입니다. Microsoft.VisualBasic 그리고 그건 들어가지 않았어 Microsoft.ApplicationUtils 또는 그런 것.그것을 극복하거나 반사경을여십시오.

팁:이 접근 방식을 있는 그대로 사용하고 리소스 등이 포함된 App.xaml이 이미 있는 경우당신은 원할 것입니다 이것도 좀 보세요.

이 코드는 기본 메소드로 이동해야 합니다.보다 여기 WPF의 기본 메서드에 대한 자세한 내용은

[DllImport("user32.dll")]
private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow);

private const int SW_SHOWMAXIMIZED = 3;

static void Main() 
{
    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);
       return; 
    }
}

방법 2

static void Main()
{
    string procName = Process.GetCurrentProcess().ProcessName;
    // get the list of all processes by that name

    Process[] processes=Process.GetProcessesByName(procName);

    if (processes.Length > 1)
    {
        MessageBox.Show(procName + " already running");  
        return;
    } 
    else
    {
        // Application.Run(...);
    }
}

메모 : 위의 방법에서는 프로세스/애플리케이션에 고유한 이름이 있다고 가정합니다.기존 프로세서가 있는지 찾기 위해 프로세스 이름을 사용하기 때문입니다.따라서 애플리케이션에 매우 일반적인 이름이 있는 경우(예:메모장) 위의 접근 방식은 작동하지 않습니다.

글쎄요, 대부분의 사용 사례에서 쉽게 작동하는 일회용 클래스가 있습니다.

다음과 같이 사용하세요:

static void Main()
{
    using (SingleInstanceMutex sim = new SingleInstanceMutex())
    {
        if (sim.IsOtherInstanceRunning)
        {
            Application.Exit();
        }

        // Initialize program here.
    }
}

여기있어:

/// <summary>
/// Represents a <see cref="SingleInstanceMutex"/> class.
/// </summary>
public partial class SingleInstanceMutex : IDisposable
{
    #region Fields

    /// <summary>
    /// Indicator whether another instance of this application is running or not.
    /// </summary>
    private bool isNoOtherInstanceRunning;

    /// <summary>
    /// The <see cref="Mutex"/> used to ask for other instances of this application.
    /// </summary>
    private Mutex singleInstanceMutex = null;

    /// <summary>
    /// An indicator whether this object is beeing actively disposed or not.
    /// </summary>
    private bool disposed;

    #endregion

    #region Constructor

    /// <summary>
    /// Initializes a new instance of the <see cref="SingleInstanceMutex"/> class.
    /// </summary>
    public SingleInstanceMutex()
    {
        this.singleInstanceMutex = new Mutex(true, Assembly.GetCallingAssembly().FullName, out this.isNoOtherInstanceRunning);
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets an indicator whether another instance of the application is running or not.
    /// </summary>
    public bool IsOtherInstanceRunning
    {
        get
        {
            return !this.isNoOtherInstanceRunning;
        }
    }

    #endregion

    #region Methods

    /// <summary>
    /// Closes the <see cref="SingleInstanceMutex"/>.
    /// </summary>
    public void Close()
    {
        this.ThrowIfDisposed();
        this.singleInstanceMutex.Close();
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            /* Release unmanaged ressources */

            if (disposing)
            {
                /* Release managed ressources */
                this.Close();
            }

            this.disposed = true;
        }
    }

    /// <summary>
    /// Throws an exception if something is tried to be done with an already disposed object.
    /// </summary>
    /// <remarks>
    /// All public methods of the class must first call this.
    /// </remarks>
    public void ThrowIfDisposed()
    {
        if (this.disposed)
        {
            throw new ObjectDisposedException(this.GetType().Name);
        }
    }

    #endregion
}

Mutex 및 IPC를 사용하고 모든 명령줄 인수를 실행 중인 인스턴스에 전달하는 새로운 것은 다음과 같습니다. WPF 단일 인스턴스 애플리케이션.

코드 C# .NET 단일 인스턴스 애플리케이션 표시된 답변에 대한 참조는 좋은 시작입니다.

그러나 이미 존재하는 인스턴스에 모달 대화 상자가 열려 있는 경우, 해당 대화 상자가 관리되는 대화 상자인지(예: 정보 상자와 같은 다른 양식) 관리되지 않는 대화 상자인지(예: OpenFileDialog는 표준 .NET 클래스를 사용하는 경우에도 마찬가지입니다.원래 코드를 사용하면 기본 양식이 활성화되지만 모달 양식은 비활성 상태로 유지되어 이상해 보이며 사용자가 앱을 계속 사용하려면 이를 클릭해야 합니다.

그래서 Winforms 및 WPF 응용 프로그램에 대해 이 모든 것을 자동으로 처리하기 위해 SingleInstance 유틸리티 클래스를 만들었습니다.

윈폼:

1) 다음과 같이 Program 클래스를 수정합니다.

static class Program
{
    public static readonly SingleInstance Singleton = new SingleInstance(typeof(Program).FullName);

    [STAThread]
    static void Main(string[] args)
    {
        // NOTE: if this always return false, close & restart Visual Studio
        // this is probably due to the vshost.exe thing
        Singleton.RunFirstInstance(() =>
        {
            SingleInstanceMain(args);
        });
    }

    public static void SingleInstanceMain(string[] args)
    {
        // standard code that was in Main now goes here
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}

2) 기본 창 클래스를 다음과 같이 수정합니다.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    protected override void WndProc(ref Message m)
    {
        // if needed, the singleton will restore this window
        Program.Singleton.OnWndProc(this, m, true);

        // TODO: handle specific messages here if needed
        base.WndProc(ref m);
    }
}

WPF:

1) 앱 페이지를 다음과 같이 수정합니다(그리고 Main 메서드를 재정의할 수 있도록 빌드 작업을 페이지로 설정했는지 확인하세요).

public partial class App : Application
{
    public static readonly SingleInstance Singleton = new SingleInstance(typeof(App).FullName);

    [STAThread]
    public static void Main(string[] args)
    {
        // NOTE: if this always return false, close & restart Visual Studio
        // this is probably due to the vshost.exe thing
        Singleton.RunFirstInstance(() =>
        {
            SingleInstanceMain(args);
        });
    }

    public static void SingleInstanceMain(string[] args)
    {
        // standard code that was in Main now goes here
        App app = new App();
        app.InitializeComponent();
        app.Run();
    }
}

2) 기본 창 클래스를 다음과 같이 수정합니다.

public partial class MainWindow : Window
{
    private HwndSource _source;

    public MainWindow()
    {
        InitializeComponent();
    }

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        _source = (HwndSource)PresentationSource.FromVisual(this);
        _source.AddHook(HwndSourceHook);
    }

    protected virtual IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        // if needed, the singleton will restore this window
        App.Singleton.OnWndProc(hwnd, msg, wParam, lParam, true, true);

        // TODO: handle other specific message
        return IntPtr.Zero;
    }

유틸리티 클래스는 다음과 같습니다.

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;

namespace SingleInstanceUtilities
{
    public sealed class SingleInstance
    {
        private const int HWND_BROADCAST = 0xFFFF;

        [DllImport("user32.dll")]
        private static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);

        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        private static extern int RegisterWindowMessage(string message);

        [DllImport("user32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);

        public SingleInstance(string uniqueName)
        {
            if (uniqueName == null)
                throw new ArgumentNullException("uniqueName");

            Mutex = new Mutex(true, uniqueName);
            Message = RegisterWindowMessage("WM_" + uniqueName);
        }

        public Mutex Mutex { get; private set; }
        public int Message { get; private set; }

        public void RunFirstInstance(Action action)
        {
            RunFirstInstance(action, IntPtr.Zero, IntPtr.Zero);
        }

        // NOTE: if this always return false, close & restart Visual Studio
        // this is probably due to the vshost.exe thing
        public void RunFirstInstance(Action action, IntPtr wParam, IntPtr lParam)
        {
            if (action == null)
                throw new ArgumentNullException("action");

            if (WaitForMutext(wParam, lParam))
            {
                try
                {
                    action();
                }
                finally
                {
                    ReleaseMutex();
                }
            }
        }

        public static void ActivateWindow(IntPtr hwnd)
        {
            if (hwnd == IntPtr.Zero)
                return;

            FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd));
        }

        public void OnWndProc(IntPtr hwnd, int m, IntPtr wParam, IntPtr lParam, bool restorePlacement, bool activate)
        {
            if (m == Message)
            {
                if (restorePlacement)
                {
                    WindowPlacement placement = WindowPlacement.GetPlacement(hwnd, false);
                    if (placement.IsValid && placement.IsMinimized)
                    {
                        const int SW_SHOWNORMAL = 1;
                        placement.ShowCmd = SW_SHOWNORMAL;
                        placement.SetPlacement(hwnd);
                    }
                }

                if (activate)
                {
                    SetForegroundWindow(hwnd);
                    FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd));
                }
            }
        }

#if WINFORMS // define this for Winforms apps
        public void OnWndProc(System.Windows.Forms.Form form, int m, IntPtr wParam, IntPtr lParam, bool activate)
        {
            if (form == null)
                throw new ArgumentNullException("form");

            if (m == Message)
            {
                if (activate)
                {
                    if (form.WindowState == System.Windows.Forms.FormWindowState.Minimized)
                    {
                        form.WindowState = System.Windows.Forms.FormWindowState.Normal;
                    }

                    form.Activate();
                    FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(form.Handle));
                }
            }
        }

        public void OnWndProc(System.Windows.Forms.Form form, System.Windows.Forms.Message m, bool activate)
        {
            if (form == null)
                throw new ArgumentNullException("form");

            OnWndProc(form, m.Msg, m.WParam, m.LParam, activate);
        }
#endif

        public void ReleaseMutex()
        {
            Mutex.ReleaseMutex();
        }

        public bool WaitForMutext(bool force, IntPtr wParam, IntPtr lParam)
        {
            bool b = PrivateWaitForMutext(force);
            if (!b)
            {
                PostMessage((IntPtr)HWND_BROADCAST, Message, wParam, lParam);
            }
            return b;
        }

        public bool WaitForMutext(IntPtr wParam, IntPtr lParam)
        {
            return WaitForMutext(false, wParam, lParam);
        }

        private bool PrivateWaitForMutext(bool force)
        {
            if (force)
                return true;

            try
            {
                return Mutex.WaitOne(TimeSpan.Zero, true);
            }
            catch (AbandonedMutexException)
            {
                return true;
            }
        }
    }

    // NOTE: don't add any field or public get/set property, as this must exactly map to Windows' WINDOWPLACEMENT structure
    [StructLayout(LayoutKind.Sequential)]
    public struct WindowPlacement
    {
        public int Length { get; set; }
        public int Flags { get; set; }
        public int ShowCmd { get; set; }
        public int MinPositionX { get; set; }
        public int MinPositionY { get; set; }
        public int MaxPositionX { get; set; }
        public int MaxPositionY { get; set; }
        public int NormalPositionLeft { get; set; }
        public int NormalPositionTop { get; set; }
        public int NormalPositionRight { get; set; }
        public int NormalPositionBottom { get; set; }

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool SetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool GetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl);

        private const int SW_SHOWMINIMIZED = 2;

        public bool IsMinimized
        {
            get
            {
                return ShowCmd == SW_SHOWMINIMIZED;
            }
        }

        public bool IsValid
        {
            get
            {
                return Length == Marshal.SizeOf(typeof(WindowPlacement));
            }
        }

        public void SetPlacement(IntPtr windowHandle)
        {
            SetWindowPlacement(windowHandle, ref this);
        }

        public static WindowPlacement GetPlacement(IntPtr windowHandle, bool throwOnError)
        {
            WindowPlacement placement = new WindowPlacement();
            if (windowHandle == IntPtr.Zero)
                return placement;

            placement.Length = Marshal.SizeOf(typeof(WindowPlacement));
            if (!GetWindowPlacement(windowHandle, ref placement))
            {
                if (throwOnError)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                return new WindowPlacement();
            }
            return placement;
        }
    }

    public static class FormUtilities
    {
        [DllImport("user32.dll")]
        private static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr SetActiveWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern bool IsWindowVisible(IntPtr hWnd);

        [DllImport("kernel32.dll")]
        public static extern int GetCurrentThreadId();

        private delegate bool EnumChildrenCallback(IntPtr hwnd, IntPtr lParam);

        [DllImport("user32.dll")]
        private static extern bool EnumThreadWindows(int dwThreadId, EnumChildrenCallback lpEnumFunc, IntPtr lParam);

        private class ModalWindowUtil
        {
            private const int GW_OWNER = 4;
            private int _maxOwnershipLevel;
            private IntPtr _maxOwnershipHandle;

            private bool EnumChildren(IntPtr hwnd, IntPtr lParam)
            {
                int level = 1;
                if (IsWindowVisible(hwnd) && IsOwned(lParam, hwnd, ref level))
                {
                    if (level > _maxOwnershipLevel)
                    {
                        _maxOwnershipHandle = hwnd;
                        _maxOwnershipLevel = level;
                    }
                }
                return true;
            }

            private static bool IsOwned(IntPtr owner, IntPtr hwnd, ref int level)
            {
                IntPtr o = GetWindow(hwnd, GW_OWNER);
                if (o == IntPtr.Zero)
                    return false;

                if (o == owner)
                    return true;

                level++;
                return IsOwned(owner, o, ref level);
            }

            public static void ActivateWindow(IntPtr hwnd)
            {
                if (hwnd != IntPtr.Zero)
                {
                    SetActiveWindow(hwnd);
                }
            }

            public static IntPtr GetModalWindow(IntPtr owner)
            {
                ModalWindowUtil util = new ModalWindowUtil();
                EnumThreadWindows(GetCurrentThreadId(), util.EnumChildren, owner);
                return util._maxOwnershipHandle; // may be IntPtr.Zero
            }
        }

        public static void ActivateWindow(IntPtr hwnd)
        {
            ModalWindowUtil.ActivateWindow(hwnd);
        }

        public static IntPtr GetModalWindow(IntPtr owner)
        {
            return ModalWindowUtil.GetModalWindow(owner);
        }
    }
}

다음은 애플리케이션의 단일 인스턴스를 가질 수 있는 예입니다.새 인스턴스가 로드되면 실행 중인 기본 인스턴스에 인수를 전달합니다.

public partial class App : Application
{
    private static Mutex SingleMutex;
    public static uint MessageId;

    private void Application_Startup(object sender, StartupEventArgs e)
    {
        IntPtr Result;
        IntPtr SendOk;
        Win32.COPYDATASTRUCT CopyData;
        string[] Args;
        IntPtr CopyDataMem;
        bool AllowMultipleInstances = false;

        Args = Environment.GetCommandLineArgs();

        // TODO: Replace {00000000-0000-0000-0000-000000000000} with your application's GUID
        MessageId   = Win32.RegisterWindowMessage("{00000000-0000-0000-0000-000000000000}");
        SingleMutex = new Mutex(false, "AppName");

        if ((AllowMultipleInstances) || (!AllowMultipleInstances && SingleMutex.WaitOne(1, true)))
        {
            new Main();
        }
        else if (Args.Length > 1)
        {
            foreach (Process Proc in Process.GetProcesses())
            {
                SendOk = Win32.SendMessageTimeout(Proc.MainWindowHandle, MessageId, IntPtr.Zero, IntPtr.Zero,
                    Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
                    2000, out Result);

                if (SendOk == IntPtr.Zero)
                    continue;
                if ((uint)Result != MessageId)
                    continue;

                CopyDataMem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.COPYDATASTRUCT)));

                CopyData.dwData = IntPtr.Zero;
                CopyData.cbData = Args[1].Length*2;
                CopyData.lpData = Marshal.StringToHGlobalUni(Args[1]);

                Marshal.StructureToPtr(CopyData, CopyDataMem, false);

                Win32.SendMessageTimeout(Proc.MainWindowHandle, Win32.WM_COPYDATA, IntPtr.Zero, CopyDataMem,
                    Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
                    5000, out Result);

                Marshal.FreeHGlobal(CopyData.lpData);
                Marshal.FreeHGlobal(CopyDataMem);
            }

            Shutdown(0);
        }
    }
}

public partial class Main : Window
{
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        HwndSource Source;

        Source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
        Source.AddHook(new HwndSourceHook(Window_Proc));
    }

    private IntPtr Window_Proc(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam, ref bool Handled)
    {
        Win32.COPYDATASTRUCT CopyData;
        string Path;

        if (Msg == Win32.WM_COPYDATA)
        {
            CopyData = (Win32.COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.COPYDATASTRUCT));
            Path = Marshal.PtrToStringUni(CopyData.lpData, CopyData.cbData / 2);

            if (WindowState == WindowState.Minimized)
            {
                // Restore window from tray
            }

            // Do whatever we want with information

            Activate();
            Focus();
        }

        if (Msg == App.MessageId)
        {
            Handled = true;
            return new IntPtr(App.MessageId);
        }

        return IntPtr.Zero;
    }
}

public class Win32
{
    public const uint WM_COPYDATA = 0x004A;

    public struct COPYDATASTRUCT
    {
        public IntPtr dwData;
        public int    cbData;
        public IntPtr lpData;
    }

    [Flags]
    public enum SendMessageTimeoutFlags : uint
    {
        SMTO_NORMAL             = 0x0000,
        SMTO_BLOCK              = 0x0001,
        SMTO_ABORTIFHUNG        = 0x0002,
        SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
    }

    [DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
    public static extern uint RegisterWindowMessage(string lpString);
    [DllImport("user32.dll")]
    public static extern IntPtr SendMessageTimeout(
        IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam,
        SendMessageTimeoutFlags fuFlags, uint uTimeout, out IntPtr lpdwResult);
}

몇 가지 생각:일부 사람들이 생각하는 것처럼 응용 프로그램의 단 하나의 인스턴스만 "절름발이"가 아니도록 요구하는 경우가 있습니다.데이터베이스 앱 등단일 사용자가 데이터베이스에 액세스할 수 있도록 앱의 여러 인스턴스를 허용하는 경우 훨씬 더 어렵습니다(알다시피, 사용자 컴퓨터에 있는 앱의 여러 인스턴스에서 열려 있는 모든 레코드를 업데이트하는 것 등). .첫째, "이름 충돌 문제의 경우 사람이 읽을 수 있는 이름을 사용하지 마세요. 대신 GUID를 사용하거나 GUID + 사람이 읽을 수 있는 이름을 사용하는 것이 더 좋습니다.이름 충돌 가능성이 레이더에서 사라졌고 Mutex는 신경 쓰지 않습니다.누군가 지적했듯이 DOS 공격은 형편없겠지만, 악의적인 사람이 뮤텍스 이름을 알아내 앱에 통합하는 데 어려움을 겪었다면 어쨌든 당신은 거의 표적이 되며 자신을 보호하기 위해 훨씬 더 많은 조치를 취해야 할 것입니다. 단지 뮤텍스 이름을 조작하는 것보다 말이죠.또한 다음 변형을 사용하는 경우:new Mutex(true, "some GUID plus Name", out AIsFirstInstance), Mutex가 첫 번째 인스턴스인지 여부에 대한 표시기가 이미 있습니다.

이렇게 간단해 보이는 질문에 대한 답변이 너무 많습니다.여기서 상황을 조금 흔들어 보면 이 문제에 대한 나의 해결책이 있습니다.

Mutex를 생성하는 것은 JIT 작업자가 코드의 작은 부분에 대해서만 Mutex를 사용하는 것을 보고 이를 가비지 수집 준비가 된 것으로 표시하기를 원하기 때문에 번거로울 수 있습니다.그것은 당신이 오랫동안 Mutex를 사용하지 않을 것이라고 생각하는 것을 현명하게 만들고 싶어합니다.실제로는 애플리케이션이 실행되는 동안 이 Mutex에 매달려 있기를 원합니다.가비지 수집기에게 Mutex를 그대로 두도록 지시하는 가장 좋은 방법은 여러 세대의 차고 수집에도 불구하고 이를 계속 유지하도록 지시하는 것입니다.예:

var m = new Mutex(...);
...
GC.KeepAlive(m);

나는 이 페이지에서 아이디어를 얻었습니다. http://www.ai.uga.edu/~mc/SingleInstance.html

이 문제를 처리하는 정말 좋은 방법이 있는 것 같습니다.

WPF 단일 인스턴스 애플리케이션

이는 구현을 사소한 수준까지 단순화하기 위해 모든 뮤텍스 및 메시징 문제를 관리하는 추가할 수 있는 클래스를 제공합니다.

다음 코드는 단일 인스턴스 애플리케이션을 등록하기 위한 WCF 명명된 파이프 솔루션입니다.다른 인스턴스가 시작을 시도할 때도 이벤트를 발생시키고 다른 인스턴스의 명령줄을 수신하기 때문에 좋습니다.

WPF를 사용하기 때문에 WPF에 맞춰져 있습니다. System.Windows.StartupEventHandler 클래스이지만 쉽게 수정할 수 있습니다.

이 코드에는 다음에 대한 참조가 필요합니다. PresentationFramework, 그리고 System.ServiceModel.

용법:

class Program
{
    static void Main()
    {
        var applicationId = new Guid("b54f7b0d-87f9-4df9-9686-4d8fd76066dc");

        if (SingleInstanceManager.VerifySingleInstance(applicationId))
        {
            SingleInstanceManager.OtherInstanceStarted += OnOtherInstanceStarted;

            // Start the application
        }
    }

    static void OnOtherInstanceStarted(object sender, StartupEventArgs e)
    {
        // Do something in response to another instance starting up.
    }
}

소스 코드:

/// <summary>
/// A class to use for single-instance applications.
/// </summary>
public static class SingleInstanceManager
{
  /// <summary>
  /// Raised when another instance attempts to start up.
  /// </summary>
  public static event StartupEventHandler OtherInstanceStarted;

  /// <summary>
  /// Checks to see if this instance is the first instance running on this machine.  If it is not, this method will
  /// send the main instance this instance's startup information.
  /// </summary>
  /// <param name="guid">The application's unique identifier.</param>
  /// <returns>True if this instance is the main instance.</returns>
  public static bool VerifySingleInstace(Guid guid)
  {
    if (!AttemptPublishService(guid))
    {
      NotifyMainInstance(guid);

      return false;
    }

    return true;
  }

  /// <summary>
  /// Attempts to publish the service.
  /// </summary>
  /// <param name="guid">The application's unique identifier.</param>
  /// <returns>True if the service was published successfully.</returns>
  private static bool AttemptPublishService(Guid guid)
  {
    try
    {
      ServiceHost serviceHost = new ServiceHost(typeof(SingleInstance));
      NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
      serviceHost.AddServiceEndpoint(typeof(ISingleInstance), binding, CreateAddress(guid));
      serviceHost.Open();

      return true;
    }
    catch
    {
      return false;
    }
  }

  /// <summary>
  /// Notifies the main instance that this instance is attempting to start up.
  /// </summary>
  /// <param name="guid">The application's unique identifier.</param>
  private static void NotifyMainInstance(Guid guid)
  {
    NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
    EndpointAddress remoteAddress = new EndpointAddress(CreateAddress(guid));
    using (ChannelFactory<ISingleInstance> factory = new ChannelFactory<ISingleInstance>(binding, remoteAddress))
    {
      ISingleInstance singleInstance = factory.CreateChannel();
      singleInstance.NotifyMainInstance(Environment.GetCommandLineArgs());
    }
  }

  /// <summary>
  /// Creates an address to publish/contact the service at based on a globally unique identifier.
  /// </summary>
  /// <param name="guid">The identifier for the application.</param>
  /// <returns>The address to publish/contact the service.</returns>
  private static string CreateAddress(Guid guid)
  {
    return string.Format(CultureInfo.CurrentCulture, "net.pipe://localhost/{0}", guid);
  }

  /// <summary>
  /// The interface that describes the single instance service.
  /// </summary>
  [ServiceContract]
  private interface ISingleInstance
  {
    /// <summary>
    /// Notifies the main instance that another instance of the application attempted to start.
    /// </summary>
    /// <param name="args">The other instance's command-line arguments.</param>
    [OperationContract]
    void NotifyMainInstance(string[] args);
  }

  /// <summary>
  /// The implementation of the single instance service interface.
  /// </summary>
  private class SingleInstance : ISingleInstance
  {
    /// <summary>
    /// Notifies the main instance that another instance of the application attempted to start.
    /// </summary>
    /// <param name="args">The other instance's command-line arguments.</param>
    public void NotifyMainInstance(string[] args)
    {
      if (OtherInstanceStarted != null)
      {
        Type type = typeof(StartupEventArgs);
        ConstructorInfo constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
        StartupEventArgs e = (StartupEventArgs)constructor.Invoke(null);
        FieldInfo argsField = type.GetField("_args", BindingFlags.Instance | BindingFlags.NonPublic);
        Debug.Assert(argsField != null);
        argsField.SetValue(e, args);

        OtherInstanceStarted(null, e);
      }
    }
  }
}

단일 인스턴스 애플리케이션을 구현하기 위해(또는 적어도 프로덕션 코드에 대해서는) 명명된 뮤텍스를 사용해서는 안 됩니다.악성 코드는 쉽게 DoS(서비스 거부) 엉덩이가...

여기 내가 사용하는 것이 있습니다."활성 클릭커"로부터 보호하기 위해 전환 및 뮤텍스를 수행하기 위해 프로세스 열거를 결합했습니다.

public partial class App
{
    [DllImport("user32")]
    private static extern int OpenIcon(IntPtr hWnd);

    [DllImport("user32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hWnd);

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var p = Process
           .GetProcessesByName(Process.GetCurrentProcess().ProcessName);
            foreach (var t in p.Where(t => t.MainWindowHandle != IntPtr.Zero))
            {
                OpenIcon(t.MainWindowHandle);
                SetForegroundWindow(t.MainWindowHandle);
                Current.Shutdown();
                return;
            }

            // there is a chance the user tries to click on the icon repeatedly
            // and the process cannot be discovered yet
            bool createdNew;
            var mutex = new Mutex(true, "MyAwesomeApp", 
               out createdNew);  // must be a variable, though it is unused - 
            // we just need a bit of time until the process shows up
            if (!createdNew)
            {
                Current.Shutdown();
                return;
            }

            new Bootstrapper().Run();
        }
    }

Dale Ragan과 유사하지만 약간 수정된 더 간단한 솔루션을 찾았습니다.표준 Microsoft WindowsFormsApplicationBase 클래스를 기반으로 필요한 거의 모든 작업을 수행합니다.

먼저 Windows Forms를 사용하는 다른 모든 단일 인스턴스 애플리케이션에서 사용할 수 있는 SingleInstanceController 클래스를 만듭니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.VisualBasic.ApplicationServices;


namespace SingleInstanceController_NET
{
    public class SingleInstanceController
    : WindowsFormsApplicationBase
    {
        public delegate Form CreateMainForm();
        public delegate void StartNextInstanceDelegate(Form mainWindow);
        CreateMainForm formCreation;
        StartNextInstanceDelegate onStartNextInstance;
        public SingleInstanceController(CreateMainForm formCreation, StartNextInstanceDelegate onStartNextInstance)
        {
            // Set whether the application is single instance
            this.formCreation = formCreation;
            this.onStartNextInstance = onStartNextInstance;
            this.IsSingleInstance = true;

            this.StartupNextInstance += new StartupNextInstanceEventHandler(this_StartupNextInstance);                      
        }

        void this_StartupNextInstance(object sender, StartupNextInstanceEventArgs e)
        {
            if (onStartNextInstance != null)
            {
                onStartNextInstance(this.MainForm); // This code will be executed when the user tries to start the running program again,
                                                    // for example, by clicking on the exe file.
            }                                       // This code can determine how to re-activate the existing main window of the running application.
        }

        protected override void OnCreateMainForm()
        {
            // Instantiate your main application form
            this.MainForm = formCreation();
        }

        public void Run()
        {
            string[] commandLine = new string[0];
            base.Run(commandLine);
        }
    }
}

그런 다음 프로그램에서 다음과 같이 사용할 수 있습니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using SingleInstanceController_NET;

namespace SingleInstance
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static Form CreateForm()
        {
            return new Form1(); // Form1 is used for the main window.
        }

        static void OnStartNextInstance(Form mainWindow) // When the user tries to restart the application again,
                                                         // the main window is activated again.
        {
            mainWindow.WindowState = FormWindowState.Maximized;
        }
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);            
            SingleInstanceController controller = new SingleInstanceController(CreateForm, OnStartNextInstance);
            controller.Run();         
        }
    }
}

프로그램과 SingleInstanceController_NET ​​솔루션은 모두 Microsoft.VisualBasic 을 참조해야 합니다.사용자가 실행 중인 프로그램을 다시 시작하려고 할 때 실행 중인 응용 프로그램을 일반 창으로 다시 활성화하려는 경우 SingleInstanceController의 두 번째 매개 변수는 null일 수 있습니다.주어진 예에서는 창이 최대화되었습니다.

다음 코드를 살펴보세요.WPF 애플리케이션의 여러 인스턴스를 방지하는 훌륭하고 간단한 솔루션입니다.

private void Application_Startup(object sender, StartupEventArgs e)
{
    Process thisProc = Process.GetCurrentProcess();
    if (Process.GetProcessesByName(thisProc.ProcessName).Length > 1)
    {
        MessageBox.Show("Application running");
        Application.Current.Shutdown();
        return;
    }

    var wLogin = new LoginWindow();

    if (wLogin.ShowDialog() == true)
    {
        var wMain = new Main();
        wMain.WindowState = WindowState.Maximized;
        wMain.Show();
    }
    else
    {
        Application.Current.Shutdown();
    }
}

Mutex를 사용하지 않지만 간단한 대답은 다음과 같습니다.

System.Diagnostics;    
...
string thisprocessname = Process.GetCurrentProcess().ProcessName;

if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1)
                return;

안에 넣어두세요 Program.Main().
:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;

namespace Sample
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            //simple add Diagnostics namespace, and these 3 lines below 
            string thisprocessname = Process.GetCurrentProcess().ProcessName;
            if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1)
                return;

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Sample());
        }
    }
}

당신은 추가할 수 있습니다 MessageBox.Show ~로 if-statement를 입력하고 "애플리케이션이 이미 실행 중"이라고 입력합니다.
이것은 누군가에게 도움이 될 수 있습니다.

2017-01-25 업데이트. 몇 가지를 시도한 후 VisualBasic.dll을 사용하기로 결정했습니다. 이 방법이 더 쉽고 더 잘 작동합니다(적어도 저에게는).이전 답변을 참고 자료로 삼았습니다 ...

참고로, 이것은 인수를 전달하지 않고 수행한 방법입니다(그렇게 할 이유를 찾을 수 없습니다...).한 인스턴스에서 다른 인스턴스로 전달되는 인수가 있는 단일 앱을 의미합니다.파일 연결이 필요한 경우 각 문서에 대해 앱이 (사용자 표준 기대에 따라) 인스턴스화되어야 합니다.기존 앱에 args를 전달해야 한다면 vb dll을 사용할 것 같습니다.

인수를 전달하지 않고(단지 단일 인스턴스 앱) 새 창 메시지를 등록하지 않고 Matt Davis 솔루션에 정의된 메시지 루프를 재정의하지 않는 것을 선호합니다.VisualBasic dll을 추가하는 것은 큰 문제가 아니지만 단일 인스턴스 앱을 수행하기 위해 새 참조를 추가하는 것을 선호하지 않습니다.또한 가능한 한 빨리 종료되도록 App.Startup 재정의에서 Shutdown을 호출하는 대신 Main을 사용하여 새 클래스를 인스턴스화하는 것을 선호합니다.

누구라도 마음에 들었으면 좋겠다...아니면 약간의 영감을 줄 것입니다 :-)

프로젝트 시작 클래스는 'SingleInstanceApp'으로 설정해야 합니다.

public class SingleInstanceApp
{
    [STAThread]
    public static void Main(string[] args)
    {
        Mutex _mutexSingleInstance = new Mutex(true, "MonitorMeSingleInstance");

        if (_mutexSingleInstance.WaitOne(TimeSpan.Zero, true))
        {
            try
            {
                var app = new App();
                app.InitializeComponent();
                app.Run();

            }
            finally
            {
                _mutexSingleInstance.ReleaseMutex();
                _mutexSingleInstance.Close();
            }
        }
        else
        {
            MessageBox.Show("One instance is already running.");

            var processes = Process.GetProcessesByName(Assembly.GetEntryAssembly().GetName().Name);
            {
                if (processes.Length > 1)
                {
                    foreach (var process in processes)
                    {
                        if (process.Id != Process.GetCurrentProcess().Id)
                        {
                            WindowHelper.SetForegroundWindow(process.MainWindowHandle);
                        }
                    }
                }
            }
        }
    }
}

WindowHelper:

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;

namespace HQ.Util.Unmanaged
{
    public class WindowHelper
    {
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

뮤텍스 솔루션을 사용하십시오.

using System;
using System.Windows.Forms;
using System.Threading;

namespace OneAndOnlyOne
{
static class Program
{
    static String _mutexID = " // generate guid"
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        Boolean _isNotRunning;
        using (Mutex _mutex = new Mutex(true, _mutexID, out _isNotRunning))
        {
            if (_isNotRunning)
            {
                Application.Run(new Form1());
            }
            else
            {
                MessageBox.Show("An instance is already running.");
                return;
            }
        }
    }
}
}

다음은 응용 프로그램이 사용자 정의 창 메시지에 의존하거나 맹목적으로 프로세스 이름을 검색하지 않고도 기존 창을 전경으로 가져올 수 있도록 하는 간단한 솔루션입니다.

[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);

static readonly string guid = "<Application Guid>";

static void Main()
{
    Mutex mutex = null;
    if (!CreateMutex(out mutex))
        return;

    // Application startup code.

    Environment.SetEnvironmentVariable(guid, null, EnvironmentVariableTarget.User);
}

static bool CreateMutex(out Mutex mutex)
{
    bool createdNew = false;
    mutex = new Mutex(false, guid, out createdNew);

    if (createdNew)
    {
        Process process = Process.GetCurrentProcess();
        string value = process.Id.ToString();

        Environment.SetEnvironmentVariable(guid, value, EnvironmentVariableTarget.User);
    }
    else
    {
        string value = Environment.GetEnvironmentVariable(guid, EnvironmentVariableTarget.User);
        Process process = null;
        int processId = -1;

        if (int.TryParse(value, out processId))
            process = Process.GetProcessById(processId);

        if (process == null || !SetForegroundWindow(process.MainWindowHandle))
            MessageBox.Show("Unable to start application. An instance of this application is already running.");
    }

    return createdNew;
}

편집하다:뮤텍스 및 CreateNew를 정적으로 저장하고 초기화할 수도 있지만 작업이 끝나면 뮤텍스를 명시적으로 삭제/해제해야 합니다.개인적으로 저는 뮤텍스를 로컬로 유지하는 것을 선호합니다. 애플리케이션이 Main의 끝에 도달하지 않고 닫히더라도 자동으로 삭제되기 때문입니다.

다음을 사용할 수도 있습니다. CodeFluent 런타임 무료 도구 세트입니다.그것은 제공합니다 단일 인스턴스 단일 인스턴스 애플리케이션을 구현하는 클래스입니다.

Event를 통해 구현된 동일한 내용은 다음과 같습니다.

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();
    }
}

명명된 뮤텍스 기반 접근 방식은 명명된 뮤텍스가 Mono에서 전역이 아니기 때문에 크로스 플랫폼이 아닙니다.프로세스 열거 기반 접근 방식에는 동기화가 없으며 잘못된 동작이 발생할 수 있습니다(예:동시에 시작된 여러 프로세스는 타이밍에 따라 모두 자체 종료될 수 있습니다.창 시스템 기반 접근 방식은 콘솔 응용 프로그램에서 바람직하지 않습니다.Divin의 답변을 기반으로 구축된 이 솔루션은 다음과 같은 모든 문제를 해결합니다.

using System;
using System.IO;

namespace TestCs
{
    public class Program
    {
        // The app id must be unique. Generate a new guid for your application. 
        public static string AppId = "01234567-89ab-cdef-0123-456789abcdef";

        // The stream is stored globally to ensure that it won't be disposed before the application terminates.
        public static FileStream UniqueInstanceStream;

        public static int Main(string[] args)
        {
            EnsureUniqueInstance();

            // Your code here.

            return 0;
        }

        private static void EnsureUniqueInstance()
        {
            // Note: If you want the check to be per-user, use Environment.SpecialFolder.ApplicationData instead.
            string lockDir = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
                "UniqueInstanceApps");
            string lockPath = Path.Combine(lockDir, $"{AppId}.unique");

            Directory.CreateDirectory(lockDir);

            try
            {
                // Create the file with exclusive write access. If this fails, then another process is executing.
                UniqueInstanceStream = File.Open(lockPath, FileMode.Create, FileAccess.Write, FileShare.None);

                // Although only the line above should be sufficient, when debugging with a vshost on Visual Studio
                // (that acts as a proxy), the IO exception isn't passed to the application before a Write is executed.
                UniqueInstanceStream.Write(new byte[] { 0 }, 0, 1);
                UniqueInstanceStream.Flush();
            }
            catch
            {
                throw new Exception("Another instance of the application is already running.");
            }
        }
    }
}

찾을 수 없습니다. 짧은 솔루션 여기 누군가가 이것을 좋아하기를 바랍니다.

업데이트 날짜: 2018-09-20

(그런데.당신에게 코드를 넣어 "프로그램.cs")

    using System.Diagnostics;

    static void Main()
    {
        Process ThisProcess = Process.GetCurrentProcess();
        Process[] AllProcesses = Process.GetProcessesByName(ThisProcess.ProcessName);
        if (AllProcesses.Length > 1)
        {
            //Don't put a MessageBox in here because the user could spam this MessageBox.
            return;
        }

// 선택적 코드입니다.다른 사람이 실행되는 것을 원하지 않으면 다른 이름의 ".exe"를 사용하세요.

        string exeName = AppDomain.CurrentDomain.FriendlyName;
        if (exeName != "the name of you're executable.exe") // If you try that in debug mode, don't forget that u don't use ur normal .exe. Debug uses the .vshost.exe.
        {// You can add here a MessageBox if you want. To point users that the name got changed and maybe what the name should be or something like that^^ 
            MessageBox.Show("The executable name should be \"the name of you're executable.exe\"", 
            "Wrong executable name", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return;
        }

        //Following Code is default code:
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm());
    }

NativeMethods 클래스에 sendMessage 메서드를 추가했습니다.

응용 프로그램이 작업 표시줄에 표시되지 않으면 분명히 postmessage 방법이 작동하지만 sendmessage 방법을 사용하면 이 문제가 해결됩니다.

class NativeMethods
{
    public const int HWND_BROADCAST = 0xffff;
    public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
    [DllImport("user32")]
    public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
    [DllImport("user32")]
    public static extern int RegisterWindowMessage(string message);
}

일반적으로 .exe를 실행할 때마다 자체 주소 공간, 리소스 등을 포함하는 별도의 Windows 프로세스가 생성됩니다.그러나 우리는 이 기준을 원하지 않습니다. 이렇게 하면 단일 프로세스를 생성할 수 없기 때문입니다.단일 인스턴스 애플리케이션은 C#의 Mutex를 사용하여 생성할 수 있습니다. 이 기사에서 논의됩니다

또한 애플리케이션을 맨 위에 가져오려면 다음을 사용하여 수행할 수 있습니다.

 [DllImport("user32")]
 static extern IntPtr SetForegroundWindow(IntPtr hWnd);

일반적으로 이것은 단일 인스턴스에 사용하는 코드입니다. 윈도우 폼 신청:

[STAThread]
public static void Main()
{
    String assemblyName = Assembly.GetExecutingAssembly().GetName().Name;

    using (Mutex mutex = new Mutex(false, assemblyName))
    {
        if (!mutex.WaitOne(0, false))
        {
            Boolean shownProcess = false;
            Process currentProcess = Process.GetCurrentProcess();

            foreach (Process process in Process.GetProcessesByName(currentProcess.ProcessName))
            {
                if (!process.Id.Equals(currentProcess.Id) && process.MainModule.FileName.Equals(currentProcess.MainModule.FileName) && !process.MainWindowHandle.Equals(IntPtr.Zero))
                {
                    IntPtr windowHandle = process.MainWindowHandle;

                    if (NativeMethods.IsIconic(windowHandle))
                        NativeMethods.ShowWindow(windowHandle, ShowWindowCommand.Restore);

                    NativeMethods.SetForegroundWindow(windowHandle);

                    shownProcess = true;
                }
            }

            if (!shownProcess)
                MessageBox.Show(String.Format(CultureInfo.CurrentCulture, "An instance of {0} is already running!", assemblyName), assemblyName, MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1, (MessageBoxOptions)0);
        }
        else
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form());
        }
    }
}

기본 구성 요소는 다음과 같습니다.

[DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean IsIconic([In] IntPtr windowHandle);

[DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean SetForegroundWindow([In] IntPtr windowHandle);

[DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean ShowWindow([In] IntPtr windowHandle, [In] ShowWindowCommand command);

public enum ShowWindowCommand : int
{
    Hide                   = 0x0,
    ShowNormal             = 0x1,
    ShowMinimized          = 0x2,
    ShowMaximized          = 0x3,
    ShowNormalNotActive    = 0x4,
    Minimize               = 0x6,
    ShowMinimizedNotActive = 0x7,
    ShowCurrentNotActive   = 0x8,
    Restore                = 0x9,
    ShowDefault            = 0xA,
    ForceMinimize          = 0xB
}

해결책은 다음과 같습니다.

Protected Overrides Sub OnStartup(e As StartupEventArgs)
    Const appName As String = "TestApp"
    Dim createdNew As Boolean
    _mutex = New Mutex(True, appName, createdNew)
    If Not createdNew Then
        'app is already running! Exiting the application
        MessageBox.Show("Application is already running.")
        Application.Current.Shutdown()
    End If
    MyBase.OnStartup(e)
End Sub

이것이 제가 이 문제를 처리하게 된 방법입니다.테스트를 위해 디버그 코드가 아직 남아 있습니다.이 코드는 App.xaml.cs 파일의 OnStartup 내에 있습니다.(WPF)

        // Process already running ? 
        if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1)
        {

            // Show your error message
            MessageBox.Show("xxx is already running.  \r\n\r\nIf the original process is hung up you may need to restart your computer, or kill the current xxx process using the task manager.", "xxx is already running!", MessageBoxButton.OK, MessageBoxImage.Exclamation);

            // This process 
            Process currentProcess = Process.GetCurrentProcess();

            // Get all processes running on the local computer.
            Process[] localAll = Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName);

            // ID of this process... 
            int temp = currentProcess.Id;
            MessageBox.Show("This Process ID:  " + temp.ToString());

            for (int i = 0; i < localAll.Length; i++)
            {
                // Find the other process 
                if (localAll[i].Id != currentProcess.Id)
                {
                    MessageBox.Show("Original Process ID (Switching to):  " + localAll[i].Id.ToString());

                    // Switch to it... 
                    SetForegroundWindow(localAll[i].MainWindowHandle);

                }
            }

            Application.Current.Shutdown();

        }

아직 파악하지 못한 문제가 있을 수 있습니다.문제가 발생하면 답변을 업데이트하겠습니다.

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