Single Instance app, Activate

Using this in Main to enforce a single instance of the app:

Dim firstInstance As Boolean
Dim safeName As String = Application.UserAppDataPath.Replace("\", "_")
Dim mtx As Mutex = New Mutex(True, safeName, firstInstance)
If Not firstInstance Then
Return
End If

Application.Run(New Form1)
...


But instead of just returning if its not the first instance, I would like to Activate the existing instance's window. I guess the question is how can I get a reference to the form in the instance already running


Answer this question

Single Instance app, Activate

  • Eddie RIveron

    I'm using (got it from http://www.smithvoice.com/vbfun.htm)

     m_Mutex = New System.Threading.Mutex(False, m_UniqueIdentifier)
            Dim bFound As Boolean = False
            If m_Mutex.WaitOne(1, True) Then
                'first instance
              
                Application.Run(New MainForm)

                       Else
                'not first instance
                Try
                    Dim Procs() As Process = Process.GetProcesses()
                    Dim proc As Process
                    For Each proc In Procs

                        If proc.MainModule.FileName.Replace("\", "_") = m_UniqueIdentifier _
                           And proc.Id <> Process.GetCurrentProcess.Id Then
                            'MsgBox("another")
                            bFound = True
                            Write_File(aList)
                            Exit For

                        End If

                    Next proc

                    If bFound Then
                        'start this one
                        Dim temphwnd, x As Integer
                        temphwnd = proc.MainWindowHandle.ToInt32

                        x = ShowWindowAsync(temphwnd, SW_RESTORE)
                        'bring it to the top of the z-order
                        SetWindowPos(temphwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE)
                        'and release it
                        SetWindowPos(temphwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE)

                    End If

                    'exceptions from Process.GetProcesses are possbile 
                    'on certain hardware running Win2kPro and using Hyperthreading
                    'MS is aware of this and while they are not fixing .Net1x
                    'they say that the fix will be in .Net2

                Catch ex As ArgumentOutOfRangeException
                    MessageBox.Show("This program is already running." & ControlChars.CrLf & _
                    "To maximize performance of your computer only one instance of" & ControlChars.CrLf & _
                    "this program can run at a time.")
                Catch ex As InvalidOperationException
                    MessageBox.Show("This program is already running." & ControlChars.CrLf & _
                    "To maximize performance of your computer only one instance of" & ControlChars.CrLf & _
                    "this program can run at a time.")
                End Try


    It works fine to check and display the first instance. I have a writeFile in there to work with a filewatcher-and will test the results- however-I would like to try the remoting route- can the same application be use for both a host and a client at the same time, using remoting   It seems that there would be a problem with the port numbers.

    ps- Bear with me I'm just hacking my way thru on this.

  • Janus007

    maybe you want to look here http://www.codeproject.com/dotnet/VB6andVBNETWindowMessages.asp

    i think if you send a message to your already open window and then recv the message with the data you want to send, then it should work

  • RCroft

    The SingleApplication class is real good, but there some problems, when you are using a NotifyIcon in your application.  You will have more than one Icon in your System Tray, because of the forms's constructor. You need a function, that ensures, that only one instance of the NotifiIcon will be created. You place the function call into the InitializeComponent(). 

    Ex:

    private void InitTrayIcon()
    {
    Process[] RunningProcesses = Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName);

    if(RunningProcesses.Length == 1)
    {
    this.trayIcon = new System.Windows.Forms.NotifyIcon(this.components);

    this.trayIcon.ContextMenu = this.contextMenu;
    this.trayIcon.Icon = ((System.Drawing.Icon)(resources.GetObject("trayIcon.Icon")));
    this.trayIcon.Text = "Project Builder";
    this.trayIcon.Visible = true;
    this.trayIcon.DoubleClick += new System.EventHandler(this.trayIcon_DoubleClick);
    }
    }

  • Werwolf13

    I am not sure if I have got your question exactly right but maybe you could declare a  variable form which you initialize:

    form = new Form1();
    Application.Run(form);

    where form is a static instance of type Form1 declared in the class where your main method is defined. If its not the first instance you could check if form is null and if not just activate it. If it is the first instance you could just intialize it as above.


  • Lydon

    Here is the proper code to look for other instances of your app: 

    If UBound(Diagnostics.Process.GetProcessesByName(Diagnostics.Process.GetCurrentProcess.ProcessName)) > 0 Then
    'Close this instance
                Me.Close()
    Else
    'Code needed to load/prepare application
    End If

    I'm looking for the code that will let you switch to the other instance...  The object Diagnostics.Process.GetProcessesByName(Diagnostics.Process.GetCurrentProcess.ProcessName) contains all the handles necessary to grab another running app, I just can't remember the proper object method to do it...

  • markep12

    I use this class for single instance applications, that also switches to the already running instance. In your program's Main method, call SingleInstance.SingleApplication.Run(new Form1()) instead of Application.Run(new Form1()).


    using System;
    using System.IO;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading;
    using System.Windows.Forms;

    namespace SingleInstance
    {
    public sealed class SingleApplication
    {
    private SingleApplication()
    {
    }

    [DllImport("user32.Dll")]
    private static extern int EnumWindows(EnumWinCallBack callBackFunc, int lParam); 

    [DllImport("User32.Dll")]
    private static extern void GetWindowText(int hWnd, StringBuilder str, int nMaxCount);

    [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 static bool EnumWindowCallBack(int hwnd, int lParam) 

    windowHandle = (IntPtr)hwnd;

    StringBuilder sbuilder = new StringBuilder(256);
    GetWindowText((int)windowHandle, sbuilder, sbuilder.Capacity);
    string strTitle = sbuilder.ToString();

    if(strTitle == sTitle)
    {
    ShowWindow(windowHandle, SW_RESTORE); 
    SetForegroundWindow(windowHandle);
    return false;
    }
    return true;
    }

    /// <summary>
    /// Execute a form base application if another instance already running on
    /// the system activate previous one
    /// </summary>
    /// <param name="frmMain">main form</param>
    /// <returns>true if no previous instance is running</returns>
    public static bool Run(System.Windows.Forms.Form frmMain)
    {
    if(IsAlreadyRunning())
    {
    sTitle = frmMain.Text;
    //set focus on previously running app
    EnumWindows (new EnumWinCallBack(EnumWindowCallBack), 0);
    return false;
    }
    Application.Run(frmMain);
    return true;
    }

    public static bool Run(System.Windows.Forms.ApplicationContext context)
    {
    if(IsAlreadyRunning())
    {
    sTitle = context.MainForm.Text;
    //set focus on previously running app
    EnumWindows (new EnumWinCallBack(EnumWindowCallBack), 0);
    return false;
    }
    Application.Run(context);
    return true;
    }

    /// <summary>
    /// for console base application
    /// </summary>
    /// <returns></returns>
    public static bool Run()
    {
    if(IsAlreadyRunning()) 
    {
    return false;
    }
    return true;
    }

    /// <summary>
    /// check if given exe alread running or not
    /// </summary>
    /// <returns>returns true if already running</returns>
    private static bool IsAlreadyRunning()
    {
    string strLoc = Assembly.GetEntryAssembly().Location;

    FileSystemInfo fileInfo = new FileInfo(strLoc); 
    string sExeName = fileInfo.Name;
    mutex = new Mutex(true, sExeName);

    if (mutex.WaitOne(0, false))
    {
    return false;
    }
    return true;
    }

    private static Mutex mutex;
    private const int SW_RESTORE = 9;
    private static string sTitle;
    private static IntPtr windowHandle;
    private delegate bool EnumWinCallBack(int hwnd, int lParam);
    }
    }


    I think I found this code on codeproject.com, but I added the method Run(ApplicationContext) and fixed a bug in IsAlreadyRunning() where it has to check the entry assembly and not the executing assembly.

  • manba

    Ok, that was easy...  AppActivate is the funciton I couldn't think of...  Here is the full code to only allow one instance and switch to the current one.  Place this as the first code in the Form.Load event:

    If UBound(Diagnostics.Process.GetProcessesByName(Diagnostics.Process.GetCurrentProcess.ProcessName)) > 0 Then 

    'Display an optional message to the user
    'MsgBox("More than one instance of the application is not allowed.")

    'Make the current instance active
    AppActivate(Diagnostics.Process.GetProcessesByName(Diagnostics.Process.GetCurrentProcess.ProcessName)(0).Id)

    'Close this instance 
    Me.Close() 

    Else 

    'Code needed to load/prepare application 

    End If 

    So there you go.  One If-Then statement with two required lines of code.  :)

  • Mauro Araujo

    I am not sure if I have got your question exactly right but maybe you could declare a  variable form which you initialize:

    form = new Form1();

    where form is a static instance of type Form1 declared in the class where your main method is defined. If its not the first instance you could check if form is null and if not just activate it.


  • Daniel Leom

    Ooh.. thats a little tougher...  I'm not sure how you'd get a reference to a form in the other instance without remoting...

    But do you need the reference

    What I mean is:  You say that you need to pass the command line arguements from instance 2 to instance 1 before instance 2 is closed.  You could do this with a file or the registry.  You might have the app "watch" for a file or registry key to appear; this would be created only when instance 2 closes.  Whenever the app sees this file (or key) get created (or changed) it reads the value (which is your command line args) and then deletes or empties the file/key.

    This would be a simple work-around to avoid the messy remoting; especially since you don't really need to "control" the other instance - just give it some info.

  • Chrismar

    I've found that creating a "form-less" app is the easiest way to have you app's control centered around the notify icon.  I've posted code in this <a href="http://www.windowsforms.com/Forums/ShowPost.aspx tabIndex=1&tabId=41&PostID=26043">thread</a> that gives a sample of starting your app without a form and then creating a notifyicon to generate forms.  It would be very easy to modify this code to limit the forms to a single instance and pass data between them.
  • Inquisitve2

    Great!   Can I get a reference to the Form in the already-running instance, though   I need to ba able to send the command-line arguments to the Form1 of what ever instance is running.  For this reason I use the ugly remoting technique...
  • General

    that would work for child windows (basically the singleton pattern).   But not for the whole application, since static or not,  members of one app instance are not available to another.  However, I did find they way to do what I wanted.  It involves using remoting


    Dim firstInstance As Boolean
            Dim safeName As String = Application.UserAppDataPath.Replace("\", "_")
            Dim mtx As Mutex = New Mutex(True, safeName, firstInstance)
            If Not firstInstance Then

                Dim formUrl As String = "tcp://localhost:1313/form1"
                Dim connection As Object = RemotingServices.Connect(GetType(Form1), formUrl)
                Dim otherForm1 As Form1 = DirectCast(connection, Form1)

                otherForm1.TopMost = True
                otherForm1.Activate()
                otherForm1.WindowState = FormWindowState.Normal
                otherForm1.TopMost = False

                Return

            End If

                _form1Instance = New Form1

                ChannelServices.RegisterChannel(New TcpChannel(1313))
                RemotingServices.Marshal(_form1Instance, "form1")

                Application.Run(_form1Instance)


  • Single Instance app, Activate