4

I've a C# application that displays a login form when launched and displays the main form after users are authenticated. I used Mutex to restrict that only one instance of my application runs. And, this works fine for only the Login form. Once the main form is displayed, it doesn't restrict users from reopening the Login form. I was looking for a solution by which the Login screen couldn't be displayed once the main form is already opened.

Here is my Program.cs

 [STAThread]
    static void Main()
    {
        bool mutexCreated=true;

        using (Mutex mutex = new Mutex(true, "eCS", out mutexCreated))
        {
            if (mutexCreated)
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Login());
            }
            else
            {
                Process current = Process.GetCurrentProcess();
                foreach (Process process in Process.GetProcessesByName(current.ProcessName))
                {
                    if (process.Id != current.Id)
                    {
                        XtraMessageBox.Show("Another instance of eCS is already running.", "eCS already running", MessageBoxButtons.OK, MessageBoxIcon.Information);
                        SetForegroundWindow(process.MainWindowHandle);
                        break;
                    }
                }
            }
        }
    }
aby
  • 810
  • 6
  • 21
  • 36
  • So you have no control over what you can allow users to do and what not? I assume there must be some button or other means by which a user can reopen the login screen. Why don't you just remove those? – Andreas Sep 09 '12 at 14:50
  • Move your code from the Login form to the Main() method in Program.cs – Hans Passant Sep 09 '12 at 17:46
  • Hans Passant, my Program.cs is supposed to launch the Login window. And my code is already in the Program.cs but not working. Here the code: – aby Sep 09 '12 at 17:55

4 Answers4

7

I've made some small changes:


namespace CSMutex
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            bool mutexCreated=true;
            using(Mutex mutex = new Mutex(true, "eCS", out mutexCreated))
            {
                if (mutexCreated)
                {
                    Application.EnableVisualStyles();
                    Application.SetCompatibleTextRenderingDefault(false);
                    Login loging = new Login();
                    Application.Run(loging);
                    Application.Run(new Main() { UserName = loging.UserName });
                }
                else
                {
                    Process current = Process.GetCurrentProcess();
                    foreach (Process process in Process.GetProcessesByName(current.ProcessName))
                    {
                        if (process.Id != current.Id)
                        {
                            MessageBox.Show("Another instance of eCS is already running.", "eCS already running", MessageBoxButtons.OK, MessageBoxIcon.Information);
                            //SetForegroundWindow(process.MainWindowHandle);
                            break;
                        }
                    }
                }
            }
        }
    }
}

That works as expected - i.e. even when Login form is closed (and the main application form is started) it doesn't let user run the application once again. I've decided not to create Main from within Login (this is I believe how you application works) and instead I am passing parameter to Main. I have also made some small change to Login so it has UserName propert (same as Main).

Maciek Talaska
  • 1,628
  • 1
  • 14
  • 22
  • 1
    I'm curious why you are using a for loop to check the processes in the else statement, why not just show the info message ? – Harag Apr 02 '19 at 10:01
0

If you are ok with a reference to Microsoft.VisualBasic, you can use its SingleInstance processing.

    [STAThread]
    static void Main(string[] args)
    {
        using (System.Threading.Mutex mutex = new System.Threading.Mutex(true, "MyApp.SingleInstance.Mutex", out createdNew))
        {
            MainForm = new MainDlg();
            SingleInstanceApplication.Run(MainForm, StartupNextInstanceEventHandler);
        }
    }

    public static void StartupNextInstanceEventHandler(object sender, StartupNextInstanceEventArgs e)
    {
        MainForm.Activate();
    }

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

    public static void Run(Form f, StartupNextInstanceEventHandler startupHandler)
    {
        SingleInstanceApplication app = new SingleInstanceApplication();
        app.MainForm = f;
        app.StartupNextInstance += startupHandler;
        app.Run(Environment.GetCommandLineArgs());
    }
}
U1199880
  • 907
  • 1
  • 10
  • 21
-1

if you want limit to only one instance for a Form, you can do something like this:

public static class LoginForm 
{
    private static Form _loginForm = new Form();


    public static bool ShowLoginForm(){

        if(_loginForm.Visible)
             return false;

        _loginForm.Show();
        return true;
    }
}

So if more then one clients will call this method, which is only possible method to show login form, in case when its already visible, it will not be executed.

Tigran
  • 61,654
  • 8
  • 86
  • 123
  • Thanks Tigran for the quick response. The login form (for the first time) works fine. The problem is restricting the login not to open after the main form is already running. How can i do that? – aby Sep 09 '12 at 15:03
  • @user758961: if you use kind of wrapper presented in answer you can achieve that actually. Call `ShowLoginForm(..)` when it's appropriate. – Tigran Sep 09 '12 at 15:11
  • @user758961: you can extend this to `CloseLoginForm(..)` or use this trick *inside* login form itself. – Tigran Sep 09 '12 at 15:11
-3
private bool IsSingleInstance()
    {
        string szCurrentProcessName = this.ProductName;
        Process[] processlist = Process.GetProcesses();
        foreach(Process theprocess in processlist)
        {
            string szProcessName = theprocess.MainModule.ModuleName.ToString();
            if (szProcessName.Contains(szCurrentProcessName))
                return false;
        }
        return true;
    }
Rahul Gupta
  • 89
  • 2
  • 9