즐겁게 개발을...

개발보다 게임이 더 많이 올라오는 것 같은...

개발/C#

싱글 인스턴스로 프로그램 실행하기

다물칸 2023. 7. 4. 13:07
728x90
반응형

지원 Framework: 프레임워크, .Net 6에서 확인함.

 

"싱글 인스턴스가 뭐냐?"라는 질의 보다 이것을 왜 사용했는지부터 알아보겠습니다. 

 

프로그램을 실행할 때 아규먼트를 받아서 처리하는 프로그램을 개발할 때, 이미 떠 있는 프로세스에 아규먼트를 전달 할 방법이 뭐가 있을까?

 

이 방법 이전에는 프로그램이 떠있는지 체크해서 Send Message Win32 API를 통해 아규먼트를 전달하는 방법을 사용했는데 이 경우 별도의 프로그램을 중간매개로 만들어야 했습니다. 

 

C# 프로젝트를 생성하면 program.cs가 자동생성되고 

 

Application.Run(new Form1());

위 코드를 사용해 폼을 띄웁니다. 프로그램이 시작된 상태에서 다시 실행파일을 실행하면 위 코드를 통해 똑같은 프로그램이 뜨겠죠. 이를 다중 인스턴스라 명명하겠습니다. 같은 프로그램을 두개 이상 띄울 수 있다고 하여 .... 

 

다시 돌아와서 싱글로 기존에 떠있는 프로세스를 Static으로 다시 사용할 방법을 설명드립니다. 

 

program.cs를 다음과 같이 교체해주세요. logger는 nLog를 사용했습니다. 콘솔로 변경해주셔도 무방합니다. 

internal static class Program 
{
    private static Frmmain1 _Frmmain;
    private static readonly NLog.Logger logger = NLog.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType.ToString());

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main() 
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        _Frmmain = new Frmmain1();
        SingleInstanceApplication.Run(_Frmmain, NewInstanceHandler);        
    }
    
    public static void NewInstanceHandler(object sender, StartupNextInstanceEventArgs eventArgs)
    {
        try
        {
            logger.Debug("## NewInstanceHandler ##");
            _Frmmain.AfterLoginProc(eventArgs);
            eventArgs.BringToForeground = true;
        }
        catch (Exception ex)
        {
            logger.Error(ex);
        }
    }    

    public class SingleInstanceApplication : WindowsFormsApplicationBase
    {
        private SingleInstanceApplication()
        {
            base.IsSingleInstance = true;
        }

        public static void Run(Form f, StartupNextInstanceEventHandler startupHandler)
        {
            try
            {
                SingleInstanceApplication app = new SingleInstanceApplication();
                app.MainForm = f;
                app.StartupNextInstance += startupHandler;
                app.Run(Environment.GetCommandLineArgs());
            }
            catch (Exception ex)
            {
                logger.Error(ex);
            }
        }

        protected override bool OnStartup(StartupEventArgs eventArgs)
        {
            try
            {
                //MessageBox.Show(eventArgs.CommandLine.Count.ToString());
                logger.Debug("## SingleInstanceApplication.OnStartup ##");
                _Frmmain.AfterLoginProc(eventArgs);
            }
            catch (Exception ex)
            {
                logger.Error(ex);
            }
            return true;
        }

        protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
        {
            try
            {
                // Subsequent launches
                base.OnStartupNextInstance(eventArgs);
            }
            catch (Exception ex)
            {
                logger.Error(ex);
            }
        }
    }
}

 

아규먼트를 실행되어 있는 프로세스에 어떻게 전달하는가를 테스트하는 것이기 때문에 폼에 아규먼트를 확인할 수 있는 장치 (리스트 박스에 아규먼트 뿌리기)를 해둡니다. 

// 아규먼트 구조체를 1개만 쓸 것이지 왜 두개를 만들어서... 오버라이드로 함수 1개 더 추가합니다.
public void AfterLoginProc(StartupEventArgs EventArgs)
{
    if (EventArgs != null && EventArgs.CommandLine != null && EventArgs.CommandLine.Count > 0)
    {
        lstArgs.Items.Clear();
        foreach (string line in EventArgs.CommandLine)
        {
            lstArgs.Items.Add(line);
        }
    }
}

public void AfterLoginProc(StartupNextInstanceEventArgs EventArgs)
{
    if (EventArgs != null && EventArgs.CommandLine != null && EventArgs.CommandLine.Count > 0)
    {
        lstArgs.Items.Clear();
        foreach (string line in EventArgs.CommandLine)
        {
            lstArgs.Items.Add(line);
        }
    }
}

 

디버그 상태로 테스트하기가 힘들기에 빌드 후, 실행파일을 통해 바로가기 아이콘을 두개 만든 후, 대충 아규먼트를 다르게 해서 변경합니다. 

 

그리고 차례대로 바로가기를 실행하면 실행되는 프로세스는 한개에 리스트 박스에 변경되는 아규먼트를 보실 수 있습니다. 이것을 이용해서 궂이 Mutex를 이용해 이중실행 방지를 하지 않아도 되지 않을까 합니다. 

 

 

반응형